Skip to content

Commit 01b7fff

Browse files
authored
Merge pull request #87 from microsoft/user/seolive/jank-events
Adding Jank Frame support
2 parents 8600d70 + df2e020 commit 01b7fff

File tree

12 files changed

+761
-0
lines changed

12 files changed

+761
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Microsoft.Performance.SDK;
7+
using Microsoft.Performance.SDK.Extensibility;
8+
using Microsoft.Performance.SDK.Extensibility.DataCooking;
9+
using Microsoft.Performance.SDK.Processing;
10+
using PerfettoCds.Pipeline.DataOutput;
11+
using PerfettoCds.Pipeline.SourceDataCookers;
12+
using PerfettoProcessor;
13+
using Utilities;
14+
15+
namespace PerfettoCds.Pipeline.CompositeDataCookers
16+
{
17+
/// <summary>
18+
/// Pulls data from multiple individual SQL tables and joins them to create PerfettoFrameEvents.
19+
/// These frame events represent frames that were scheduled and then rendered by apps.
20+
/// </summary>
21+
public sealed class PerfettoFrameEventCooker : CookedDataReflector, ICompositeDataCookerDescriptor
22+
{
23+
public static readonly DataCookerPath DataCookerPath = PerfettoPluginConstants.FrameEventCookerPath;
24+
25+
public string Description => "Frame event composite cooker";
26+
27+
public DataCookerPath Path => DataCookerPath;
28+
29+
// Declare all of the cookers that are used by this CompositeCooker.
30+
public IReadOnlyCollection<DataCookerPath> RequiredDataCookers => new[]
31+
{
32+
PerfettoPluginConstants.ThreadCookerPath,
33+
PerfettoPluginConstants.ProcessCookerPath,
34+
PerfettoPluginConstants.ActualFrameCookerPath,
35+
PerfettoPluginConstants.ExpectedFrameCookerPath
36+
};
37+
38+
[DataOutput]
39+
public ProcessedEventData<PerfettoFrameEvent> FrameEvents { get; }
40+
41+
42+
public PerfettoFrameEventCooker() : base(PerfettoPluginConstants.FrameEventCookerPath)
43+
{
44+
this.FrameEvents = new ProcessedEventData<PerfettoFrameEvent>();
45+
}
46+
47+
public void OnDataAvailable(IDataExtensionRetrieval requiredData)
48+
{
49+
// Gather the data from all the SQL tables
50+
var threadData = requiredData.QueryOutput<ProcessedEventData<PerfettoThreadEvent>>(new DataOutputPath(PerfettoPluginConstants.ThreadCookerPath, nameof(PerfettoThreadCooker.ThreadEvents)));
51+
var processData = requiredData.QueryOutput<ProcessedEventData<PerfettoProcessEvent>>(new DataOutputPath(PerfettoPluginConstants.ProcessCookerPath, nameof(PerfettoProcessCooker.ProcessEvents)));
52+
PopulateFrameEvents(requiredData, threadData, processData);
53+
}
54+
55+
void PopulateFrameEvents(IDataExtensionRetrieval requiredData, ProcessedEventData<PerfettoThreadEvent> threadData, ProcessedEventData<PerfettoProcessEvent> processData)
56+
{
57+
var actualFrameData = requiredData.QueryOutput<ProcessedEventData<PerfettoActualFrameEvent>>(new DataOutputPath(PerfettoPluginConstants.ActualFrameCookerPath, nameof(PerfettoActualFrameCooker.ActualFrameEvents)));
58+
var expectedFrameData = requiredData.QueryOutput<ProcessedEventData<PerfettoExpectedFrameEvent>>(new DataOutputPath(PerfettoPluginConstants.ExpectedFrameCookerPath, nameof(PerfettoExpectedFrameCooker.ExpectedFrameEvents)));
59+
60+
// Gather from both actual/expected tables and join with process info
61+
var joinedActual = from frame in actualFrameData
62+
join process in processData on frame.Upid equals process.Upid into pd orderby frame.Id
63+
from process in pd.DefaultIfEmpty()
64+
select new { frame, process };
65+
var joinedExpected = from frame in expectedFrameData
66+
join process in processData on frame.Upid equals process.Upid into pd orderby frame.Id
67+
from process in pd.DefaultIfEmpty()
68+
select new { frame, process };
69+
70+
// Create FrameEvents out of each type of event
71+
foreach (var result in joinedExpected)
72+
{
73+
Timestamp startTimestamp = new Timestamp(result.frame.RelativeTimestamp);
74+
Timestamp endTimestamp = new Timestamp(result.frame.RelativeTimestamp + result.frame.Duration);
75+
76+
PerfettoFrameEvent ev = new PerfettoFrameEvent
77+
(
78+
"Expected",
79+
result.process.Name,
80+
result.frame.Upid,
81+
result.frame.DisplayFrameToken,
82+
result.frame.SurfaceFrameToken,
83+
new TimestampDelta(result.frame.Duration),
84+
startTimestamp,
85+
endTimestamp,
86+
String.Empty,
87+
String.Empty,
88+
String.Empty,
89+
String.Empty,
90+
String.Empty,
91+
String.Empty
92+
);
93+
94+
this.FrameEvents.AddEvent(ev);
95+
}
96+
97+
foreach (var result in joinedActual)
98+
{
99+
Timestamp startTimestamp = new Timestamp(result.frame.RelativeTimestamp);
100+
Timestamp endTimestamp = new Timestamp(result.frame.RelativeTimestamp + result.frame.Duration);
101+
102+
PerfettoFrameEvent ev = new PerfettoFrameEvent
103+
(
104+
"Actual",
105+
result.process.Name,
106+
result.frame.Upid,
107+
result.frame.DisplayFrameToken,
108+
result.frame.SurfaceFrameToken,
109+
new TimestampDelta(result.frame.Duration),
110+
startTimestamp,
111+
endTimestamp,
112+
result.frame.JankType,
113+
result.frame.JankTag,
114+
result.frame.PresentType,
115+
result.frame.PredictionType,
116+
result.frame.OnTimeFinish.ToString(),
117+
result.frame.GpuComposition.ToString()
118+
);
119+
120+
this.FrameEvents.AddEvent(ev);
121+
}
122+
123+
this.FrameEvents.FinalizeData();
124+
125+
}
126+
}
127+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
using Microsoft.Performance.SDK;
4+
using System;
5+
using Utilities;
6+
7+
namespace PerfettoCds.Pipeline.DataOutput
8+
{
9+
/// <summary>
10+
/// An event that represents a frame that was scheduled or actual displayed by a process.
11+
/// </summary>
12+
public class PerfettoFrameEvent
13+
{
14+
15+
public string FrameType { get; }
16+
public string ProcessName { get; }
17+
public uint Upid { get; }
18+
public long DisplayFrameToken { get; }
19+
public long SurfaceFrameToken { get; }
20+
public TimestampDelta Duration { get; }
21+
public Timestamp StartTimestamp { get; }
22+
public Timestamp EndTimestamp { get; }
23+
public string JankType { get; }
24+
public string JankTag { get; }
25+
public string PresentType { get; }
26+
public string PredictionType { get; }
27+
public string GpuComposition { get; }
28+
29+
public string OnTimeFinish { get; }
30+
31+
32+
public PerfettoFrameEvent(string FrameType,
33+
string processName,
34+
uint upid,
35+
long displayToken,
36+
long surfaceToken,
37+
TimestampDelta duration,
38+
Timestamp startTimestamp,
39+
Timestamp endTimestamp,
40+
string JankType,
41+
string JankTag,
42+
string PresentType,
43+
string PredictionType,
44+
string OnTimeFinish,
45+
string GpuComposition)
46+
{
47+
this.FrameType = Common.StringIntern(FrameType);
48+
this.ProcessName = Common.StringIntern(processName);
49+
this.Upid = upid;
50+
this.DisplayFrameToken = displayToken;
51+
this.SurfaceFrameToken = surfaceToken;
52+
this.Duration = duration;
53+
this.StartTimestamp = startTimestamp;
54+
this.EndTimestamp = endTimestamp;
55+
this.JankType = Common.StringIntern(JankType);
56+
this.JankTag = Common.StringIntern(JankTag);
57+
this.PresentType = Common.StringIntern(PresentType);
58+
this.PredictionType = Common.StringIntern(PredictionType);
59+
this.OnTimeFinish = OnTimeFinish;
60+
this.GpuComposition = GpuComposition;
61+
}
62+
}
63+
}

PerfettoCds/Pipeline/PerfettoPluginConstants.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public static class PerfettoPluginConstants
3535
public const string StackProfileFrameCookerId = nameof(PerfettoStackProfileFrameCooker);
3636
public const string StackProfileMappingCookerId = nameof(PerfettoStackProfileMappingCooker);
3737
public const string StackProfileSymbolCookerId = nameof(PerfettoStackProfileSymbolCooker);
38+
public const string ExpectedFrameCookerId = nameof(PerfettoExpectedFrameCooker);
39+
public const string ActualFrameCookerId = nameof(PerfettoActualFrameCooker);
3840

3941
// ID for composite data cookers
4042
public const string GenericEventCookerId = nameof(PerfettoGenericEventCooker);
@@ -47,6 +49,7 @@ public static class PerfettoPluginConstants
4749
public const string ProcessMemoryEventCookerId = nameof(PerfettoProcessMemoryEventCooker);
4850
public const string SystemMemoryEventCookerId = nameof(PerfettoSystemMemoryEventCooker);
4951
public const string CpuSamplingEventCookerId = nameof(PerfettoCpuSamplingEventCooker);
52+
public const string FrameEventCookerId = nameof(PerfettoFrameEventCooker);
5053

5154
// Events for source cookers
5255
public const string SliceEvent = PerfettoSliceEvent.Key;
@@ -68,6 +71,8 @@ public static class PerfettoPluginConstants
6871
public const string StackProfileFrameEvent = PerfettoStackProfileFrameEvent.Key;
6972
public const string StackProfileMappingEvent = PerfettoStackProfileMappingEvent.Key;
7073
public const string StackProfileSymbolEvent = PerfettoStackProfileSymbolEvent.Key;
74+
public const string ExpectedFrameEvent = PerfettoExpectedFrameEvent.Key;
75+
public const string ActualFrameEvent = PerfettoActualFrameEvent.Key;
7176

7277
// Output events for composite cookers
7378
public const string GenericEvent = nameof(PerfettoGenericEvent);
@@ -80,6 +85,7 @@ public static class PerfettoPluginConstants
8085
public const string ProcessMemoryEvent = nameof(PerfettoProcessMemoryEvent);
8186
public const string SystemMemoryEvent = nameof(PerfettoSystemMemoryEvent);
8287
public const string CpuSamplingEvent = nameof(PerfettoCpuSamplingEvent);
88+
public const string FrameEvent = nameof(PerfettoFrameEvent);
8389

8490
// Paths for source cookers
8591
public static readonly DataCookerPath SliceCookerPath =
@@ -120,6 +126,10 @@ public static class PerfettoPluginConstants
120126
DataCookerPath.ForSource(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.StackProfileMappingCookerId);
121127
public static readonly DataCookerPath StackProfileSymbolCookerPath =
122128
DataCookerPath.ForSource(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.StackProfileSymbolCookerId);
129+
public static readonly DataCookerPath ExpectedFrameCookerPath =
130+
DataCookerPath.ForSource(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.ExpectedFrameCookerId);
131+
public static readonly DataCookerPath ActualFrameCookerPath =
132+
DataCookerPath.ForSource(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.ActualFrameCookerId);
123133

124134
// Paths for composite cookers
125135
public static readonly DataCookerPath GenericEventCookerPath =
@@ -142,5 +152,7 @@ public static class PerfettoPluginConstants
142152
DataCookerPath.ForComposite(PerfettoPluginConstants.SystemMemoryEventCookerId);
143153
public static readonly DataCookerPath CpuSamplingEventCookerPath =
144154
DataCookerPath.ForComposite(PerfettoPluginConstants.CpuSamplingEventCookerId);
155+
public static readonly DataCookerPath FrameEventCookerPath =
156+
DataCookerPath.ForComposite(PerfettoPluginConstants.FrameEventCookerId);
145157
}
146158
}

PerfettoCds/Pipeline/PerfettoSourceParser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ void EventCallback(PerfettoSqlEvent ev)
178178
new PerfettoStackProfileFrameEvent(),
179179
new PerfettoStackProfileMappingEvent(),
180180
new PerfettoStackProfileSymbolEvent(),
181+
new PerfettoActualFrameEvent(),
182+
new PerfettoExpectedFrameEvent()
181183
};
182184

183185
// Increment progress for each table queried.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
using System.Collections.Generic;
4+
using System.Threading;
5+
using Microsoft.Performance.SDK;
6+
using Microsoft.Performance.SDK.Extensibility;
7+
using Microsoft.Performance.SDK.Extensibility.DataCooking;
8+
using Microsoft.Performance.SDK.Extensibility.DataCooking.SourceDataCooking;
9+
using Microsoft.Performance.SDK.Processing;
10+
using PerfettoCds.Pipeline.Events;
11+
using PerfettoProcessor;
12+
13+
namespace PerfettoCds.Pipeline.SourceDataCookers
14+
{
15+
/// <summary>
16+
/// Cooks the data from the ActualFrame table in Perfetto traces.
17+
/// </summary>
18+
public sealed class PerfettoActualFrameCooker : SourceDataCooker<PerfettoSqlEventKeyed, PerfettoSourceParser, string>
19+
{
20+
public override string Description => "Processes events from the actual_frame_timeline_slice Perfetto SQL table";
21+
22+
//
23+
// The data this cooker outputs. Tables or other cookers can query for this data
24+
// via the SDK runtime
25+
//
26+
[DataOutput]
27+
public ProcessedEventData<PerfettoActualFrameEvent> ActualFrameEvents { get; }
28+
29+
// Instructs runtime to only send events with the given keys this data cooker
30+
public override ReadOnlyHashSet<string> DataKeys =>
31+
new ReadOnlyHashSet<string>(new HashSet<string> { PerfettoPluginConstants.ActualFrameEvent });
32+
33+
34+
public PerfettoActualFrameCooker() : base(PerfettoPluginConstants.ActualFrameCookerPath)
35+
{
36+
this.ActualFrameEvents = new ProcessedEventData<PerfettoActualFrameEvent>();
37+
}
38+
39+
public override DataProcessingResult CookDataElement(PerfettoSqlEventKeyed perfettoEvent, PerfettoSourceParser context, CancellationToken cancellationToken)
40+
{
41+
var newEvent = (PerfettoActualFrameEvent)perfettoEvent.SqlEvent;
42+
newEvent.RelativeTimestamp = newEvent.Timestamp - context.FirstEventTimestamp.ToNanoseconds;
43+
this.ActualFrameEvents.AddEvent(newEvent);
44+
45+
return DataProcessingResult.Processed;
46+
}
47+
48+
public override void EndDataCooking(CancellationToken cancellationToken)
49+
{
50+
base.EndDataCooking(cancellationToken);
51+
this.ActualFrameEvents.FinalizeData();
52+
}
53+
}
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
using System.Collections.Generic;
4+
using System.Threading;
5+
using Microsoft.Performance.SDK;
6+
using Microsoft.Performance.SDK.Extensibility;
7+
using Microsoft.Performance.SDK.Extensibility.DataCooking;
8+
using Microsoft.Performance.SDK.Extensibility.DataCooking.SourceDataCooking;
9+
using Microsoft.Performance.SDK.Processing;
10+
using PerfettoCds.Pipeline.Events;
11+
using PerfettoProcessor;
12+
13+
namespace PerfettoCds.Pipeline.SourceDataCookers
14+
{
15+
/// <summary>
16+
/// Cooks the data from the ExptectedFrame table in Perfetto traces
17+
/// </summary>
18+
public sealed class PerfettoExpectedFrameCooker : SourceDataCooker<PerfettoSqlEventKeyed, PerfettoSourceParser, string>
19+
{
20+
public override string Description => "Processes events from the expected_frame_timeline_slice Perfetto SQL table";
21+
22+
//
23+
// The data this cooker outputs. Tables or other cookers can query for this data
24+
// via the SDK runtime
25+
//
26+
[DataOutput]
27+
public ProcessedEventData<PerfettoExpectedFrameEvent> ExpectedFrameEvents { get; }
28+
29+
// Instructs runtime to only send events with the given keys this data cooker
30+
public override ReadOnlyHashSet<string> DataKeys =>
31+
new ReadOnlyHashSet<string>(new HashSet<string> { PerfettoPluginConstants.ExpectedFrameEvent });
32+
33+
34+
public PerfettoExpectedFrameCooker() : base(PerfettoPluginConstants.ExpectedFrameCookerPath)
35+
{
36+
this.ExpectedFrameEvents = new ProcessedEventData<PerfettoExpectedFrameEvent>();
37+
}
38+
39+
public override DataProcessingResult CookDataElement(PerfettoSqlEventKeyed perfettoEvent, PerfettoSourceParser context, CancellationToken cancellationToken)
40+
{
41+
var newEvent = (PerfettoExpectedFrameEvent)perfettoEvent.SqlEvent;
42+
newEvent.RelativeTimestamp = newEvent.Timestamp - context.FirstEventTimestamp.ToNanoseconds;
43+
this.ExpectedFrameEvents.AddEvent(newEvent);
44+
45+
return DataProcessingResult.Processed;
46+
}
47+
48+
public override void EndDataCooking(CancellationToken cancellationToken)
49+
{
50+
base.EndDataCooking(cancellationToken);
51+
this.ExpectedFrameEvents.FinalizeData();
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)