Skip to content

Commit a01a7b1

Browse files
committed
Initial support for Jank frames
1 parent 8600d70 commit a01a7b1

File tree

10 files changed

+734
-0
lines changed

10 files changed

+734
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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 a frame event.
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+
// The sched slice data contains the timings, CPU, priority, and end state info. We get the process and thread from
61+
// those respective tables
62+
var joinedActual = from frame in actualFrameData
63+
join process in processData on frame.Upid equals process.Upid into pd
64+
from process in pd.DefaultIfEmpty()
65+
select new { frame, process };
66+
var joinedExpected = from frame in expectedFrameData
67+
join process in processData on frame.Upid equals process.Upid into pd
68+
from process in pd.DefaultIfEmpty()
69+
select new { frame, process };
70+
71+
// Create events out of the joined results
72+
foreach (var result in joinedExpected)
73+
{
74+
Timestamp startTimestamp = new Timestamp(result.frame.RelativeTimestamp);
75+
Timestamp endTimestamp = new Timestamp(result.frame.RelativeTimestamp + result.frame.Duration);
76+
77+
PerfettoFrameEvent ev = new PerfettoFrameEvent
78+
(
79+
"Expected",
80+
result.process.Name,
81+
result.frame.Upid,
82+
result.frame.DisplayToken,
83+
result.frame.SurfaceToken,
84+
new TimestampDelta(result.frame.Duration),
85+
startTimestamp,
86+
endTimestamp,
87+
String.Empty,
88+
String.Empty,
89+
String.Empty,
90+
String.Empty,
91+
String.Empty,
92+
String.Empty
93+
);
94+
95+
this.FrameEvents.AddEvent(ev);
96+
}
97+
98+
foreach (var result in joinedActual)
99+
{
100+
Timestamp startTimestamp = new Timestamp(result.frame.RelativeTimestamp);
101+
Timestamp endTimestamp = new Timestamp(result.frame.RelativeTimestamp + result.frame.Duration);
102+
103+
PerfettoFrameEvent ev = new PerfettoFrameEvent
104+
(
105+
"Actual",
106+
result.process.Name,
107+
result.frame.Upid,
108+
result.frame.DisplayToken,
109+
result.frame.SurfaceToken,
110+
new TimestampDelta(result.frame.Duration),
111+
startTimestamp,
112+
endTimestamp,
113+
result.frame.JankType,
114+
result.frame.JankTag,
115+
result.frame.OnTimeFinish.ToString(),
116+
result.frame.PresentType,
117+
result.frame.GpuComposition.ToString(),
118+
result.frame.PredictionType
119+
);
120+
121+
this.FrameEvents.AddEvent(ev);
122+
}
123+
124+
this.FrameEvents.FinalizeData();
125+
126+
}
127+
}
128+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 long Upid { get; }
18+
public long DisplayToken { get; }
19+
public long SurfaceToken { 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 AppOnTime { get; }
26+
public string PresentType { get; }
27+
public string GpuComposition { get; }
28+
public string PredictionType { get; }
29+
30+
31+
public PerfettoFrameEvent(string FrameType,
32+
string processName,
33+
long upid,
34+
long displayToken,
35+
long surfaceToken,
36+
TimestampDelta duration,
37+
Timestamp startTimestamp,
38+
Timestamp endTimestamp,
39+
string JankType,
40+
string JankTag,
41+
string AppOnTime,
42+
string PresentType,
43+
string GpuComposition,
44+
string PredictionType)
45+
{
46+
this.FrameType = Common.StringIntern(FrameType);
47+
this.ProcessName = Common.StringIntern(processName);
48+
this.Upid = upid;
49+
this.DisplayToken = displayToken;
50+
this.SurfaceToken = surfaceToken;
51+
this.Duration = duration;
52+
this.StartTimestamp = startTimestamp;
53+
this.EndTimestamp = endTimestamp;
54+
this.JankType = Common.StringIntern(JankType);
55+
this.JankTag = Common.StringIntern(JankTag);
56+
this.AppOnTime = Common.StringIntern(AppOnTime);
57+
this.PresentType = Common.StringIntern(PresentType);
58+
this.GpuComposition = Common.StringIntern(GpuComposition);
59+
this.PredictionType = Common.StringIntern(PredictionType);
60+
}
61+
}
62+
}

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)