Skip to content

Commit 4d35a59

Browse files
authored
Add tracking of parent depth level in order to show parent/child relationships (#45)
Add tracking of parent depth level in order to show parent/child relationships and more closely match Perfetto UI. Also fix various scalar data types that can come across as optional / nullable.
1 parent 06bb13b commit 4d35a59

14 files changed

Lines changed: 215 additions & 56 deletions

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoCpuSchedEventCooker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ public void OnDataAvailable(IDataExtensionRetrieval requiredData)
5252
// those respective tables
5353
var joined = from schedSlice in schedSliceData
5454
join thread in threadData on schedSlice.Utid equals thread.Utid
55-
join process in processData on thread.Upid equals process.Upid
55+
join process in processData on thread.Upid equals process.Upid into pd from process in pd.DefaultIfEmpty()
5656
select new { schedSlice, thread, process };
5757

5858
// Create events out of the joined results
5959
foreach (var result in joined)
6060
{
6161
PerfettoCpuSchedEvent ev = new PerfettoCpuSchedEvent
6262
(
63-
result.process.Name,
63+
result.process?.Name,
6464
result.thread.Name,
6565
new TimestampDelta(result.schedSlice.Duration),
6666
new Timestamp(result.schedSlice.RelativeTimestamp),

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoFtraceEventCooker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void OnDataAvailable(IDataExtensionRetrieval requiredData)
6363
// Thread and process info is contained in their respective tables
6464
var joined = from raw in rawData
6565
join thread in threadData on raw.Utid equals thread.Utid
66-
join process in processData on thread.Upid equals process.Upid
66+
join process in processData on thread.Upid equals process.Upid into pd from process in pd.DefaultIfEmpty()
6767
join arg in argsData on raw.ArgSetId equals arg.ArgSetId into args
6868
select new { raw, args, thread, process };
6969

@@ -104,7 +104,7 @@ join arg in argsData on raw.ArgSetId equals arg.ArgSetId into args
104104
PerfettoFtraceEvent ev = new PerfettoFtraceEvent
105105
(
106106
new Timestamp(result.raw.RelativeTimestamp),
107-
result.process.Name,
107+
result.process?.Name,
108108
result.thread.Name,
109109
result.raw.Cpu,
110110
result.raw.Name,

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoGenericEventCooker.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
// Licensed under the MIT License.
33
using System;
44
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.IO;
57
using System.Linq;
68
using System.Reflection;
9+
using System.Xml;
10+
using System.Xml.Serialization;
711
using Microsoft.Performance.SDK;
812
using Microsoft.Performance.SDK.Extensibility;
913
using Microsoft.Performance.SDK.Extensibility.DataCooking;
1014
using Microsoft.Performance.SDK.Processing;
1115
using PerfettoCds.Pipeline.DataOutput;
12-
using PerfettoProcessor;
13-
using System.Xml;
14-
using System.Xml.Serialization;
15-
using System.IO;
1616
using PerfettoCds.Pipeline.SourceDataCookers;
17+
using PerfettoProcessor;
1718

1819
namespace PerfettoCds.Pipeline.CompositeDataCookers
1920
{
@@ -169,6 +170,9 @@ join processTrack in processTrackData on slice.TrackId equals processTrack.Id in
169170
join process in processData on processTrack?.Upid equals process.Upid into pd2 from process in pd2.DefaultIfEmpty()
170171
select new { slice, args, threadTrack, thread, threadProcess, process };
171172

173+
var longestRelTS = joined.Max(f => f.slice?.RelativeTimestamp);
174+
var longestEndTs = longestRelTS.HasValue ? new Timestamp(longestRelTS.Value) : Timestamp.MaxValue;
175+
172176
// Create events out of the joined results
173177
foreach (var result in joined)
174178
{
@@ -230,20 +234,37 @@ join processTrack in processTrackData on slice.TrackId equals processTrack.Id in
230234
processName = $"{result.process.Name} {result.process.Pid}";
231235
}
232236

237+
int parentTreeDepthLevel = 0;
238+
long? currentParentId = result.slice.ParentId;
239+
240+
// Walk the parent tree
241+
while (currentParentId.HasValue)
242+
{
243+
var parentPerfettoSliceEvent = sliceData[(int) currentParentId.Value];
244+
// Debug.Assert(parentPerfettoSliceEvent == null || (parentPerfettoSliceEvent.Id == currentParentId.Value)); // Should be guaranteed by slice Id ordering. Since we are relying on index being the Id
245+
currentParentId = parentPerfettoSliceEvent != null ? parentPerfettoSliceEvent.ParentId : null;
246+
parentTreeDepthLevel++;
247+
}
248+
233249
PerfettoGenericEvent ev = new PerfettoGenericEvent
234250
(
235251
result.slice.Name,
236252
result.slice.Type,
237253
new TimestampDelta(result.slice.Duration),
238254
new Timestamp(result.slice.RelativeTimestamp),
239-
new Timestamp(result.slice.RelativeTimestamp + result.slice.Duration),
255+
result.slice.Duration >= 0 ? // Duration can be not complete / negative
256+
new Timestamp(result.slice.RelativeTimestamp + result.slice.Duration) :
257+
longestEndTs,
240258
result.slice.Category,
241259
result.slice.ArgSetId,
242260
values,
243261
argKeys,
244262
processName,
245263
threadName,
246-
provider
264+
provider,
265+
result.threadTrack,
266+
result.slice.ParentId,
267+
parentTreeDepthLevel
247268
);
248269
this.GenericEvents.AddEvent(ev);
249270
}

PerfettoCds/Pipeline/DataOutput/PerfettoGenericEvent.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33
using Microsoft.Performance.SDK;
4+
using PerfettoProcessor;
45
using System.Collections.Generic;
56

67
namespace PerfettoCds.Pipeline.DataOutput
@@ -34,6 +35,12 @@ public readonly struct PerfettoGenericEvent
3435

3536
public string Provider { get; }
3637

38+
public long? ParentId{ get; }
39+
40+
public int ParentTreeDepthLevel { get; }
41+
42+
public PerfettoThreadTrackEvent ThreadTrack { get; }
43+
3744
public PerfettoGenericEvent(string eventName,
3845
string type,
3946
TimestampDelta duration,
@@ -45,20 +52,26 @@ public PerfettoGenericEvent(string eventName,
4552
List<string> argKeys,
4653
string process,
4754
string thread,
48-
string provider)
55+
string provider,
56+
PerfettoThreadTrackEvent threadTrack,
57+
long? parentId,
58+
int parentTreeDepthLevel)
4959
{
50-
this.EventName = eventName;
51-
this.Type = type;
52-
this.Duration = duration;
53-
this.StartTimestamp = startTimestamp;
54-
this.EndTimestamp = endTimestamp;
55-
this.Category = category;
56-
this.ArgSetId = argSetId;
57-
this.Values = values;
58-
this.ArgKeys = argKeys;
59-
this.Process = process;
60-
this.Thread = thread;
61-
this.Provider = provider;
60+
EventName = eventName;
61+
Type = type;
62+
Duration = duration;
63+
StartTimestamp = startTimestamp;
64+
EndTimestamp = endTimestamp;
65+
Category = category;
66+
ArgSetId = argSetId;
67+
Values = values;
68+
ArgKeys = argKeys;
69+
Process = process;
70+
Thread = thread;
71+
Provider = provider;
72+
ThreadTrack = threadTrack;
73+
ParentId = parentId;
74+
ParentTreeDepthLevel = parentTreeDepthLevel;
6275
}
6376
}
6477
}

PerfettoCds/Pipeline/Tables/PerfettoGenericEventTable.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class PerfettoGenericEventTable
5252
new UIHints {
5353
Width = 70,
5454
AggregationMode = AggregationMode.Max,
55-
SortPriority = 0,
55+
SortPriority = 1,
5656
SortOrder = SortOrder.Descending,
5757
CellFormat = TimestampFormatter.FormatMillisecondsGrouped,
5858
});
@@ -78,6 +78,23 @@ public class PerfettoGenericEventTable
7878
new ColumnMetadata(new Guid("{e7d08f97-f52c-4686-bc49-737f7a6a8bbb}"), "Provider", "Provider name of the event"),
7979
new UIHints { Width = 240 });
8080

81+
private static readonly ColumnConfiguration TrackNameIdColumn = new ColumnConfiguration(
82+
new ColumnMetadata(new Guid("{111094F9-BEB4-486F-AD60-3F53CFF702EA}"), "TrackNameId", "Track Name (Id)"),
83+
new UIHints { Width = 240 });
84+
85+
private static readonly ColumnConfiguration ParentIdColumn = new ColumnConfiguration(
86+
new ColumnMetadata(new Guid("{A77736C3-AC5C-4100-B246-3821A2E73B15}"), "Parent Id", "Parent Id"),
87+
new UIHints { Width = 240 });
88+
89+
private static readonly ColumnConfiguration ParentDepthLevelColumn = new ColumnConfiguration(
90+
new ColumnMetadata(new Guid("{89572E6E-86D5-4CBA-B0D4-4F9D2147BF50}"), "Parent Depth Level", "Parent Depth Level (0 is at Top of Tree)"),
91+
new UIHints
92+
{
93+
Width = 70,
94+
SortPriority = 0,
95+
SortOrder = SortOrder.Ascending,
96+
});
97+
8198
// Need 2 of these with different sorting
8299
const string CountColumnGuid = "{99192cbf-5888-4873-a3b3-4faf5beaea15}";
83100
private static readonly ColumnConfiguration CountColumn = new ColumnConfiguration(
@@ -161,6 +178,23 @@ public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieva
161178
genericEventProjection.Compose((genericEvent) => genericEvent.Provider));
162179
tableGenerator.AddColumn(providerColumn);
163180

181+
var trackNameIdColumn = new BaseDataColumn<string>(
182+
TrackNameIdColumn,
183+
genericEventProjection.Compose((genericEvent) => genericEvent.ThreadTrack != null ?
184+
(!String.IsNullOrWhiteSpace(genericEvent.ThreadTrack.Name) ? $"{genericEvent.ThreadTrack.Name} ({genericEvent.ThreadTrack.Id})" : genericEvent.ThreadTrack.Id.ToString())
185+
: String.Empty));
186+
tableGenerator.AddColumn(trackNameIdColumn);
187+
188+
var parentIdColumn = new BaseDataColumn<long>(
189+
ParentIdColumn,
190+
genericEventProjection.Compose((genericEvent) => genericEvent.ParentId.HasValue? genericEvent.ParentId.Value : -1));
191+
tableGenerator.AddColumn(parentIdColumn);
192+
193+
var parentDepthLevelColumn = new BaseDataColumn<int>(
194+
ParentDepthLevelColumn,
195+
genericEventProjection.Compose((genericEvent) => genericEvent.ParentTreeDepthLevel));
196+
tableGenerator.AddColumn(parentDepthLevelColumn);
197+
164198
tableGenerator.AddColumn(CountColumn, Projection.Constant<int>(1));
165199

166200
// The provider column is optionally populated depending on whether or not the user specified a ProviderGUID mapping file
@@ -240,8 +274,9 @@ public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieva
240274
SetGraphTableConfig(processThreadActivityConfig);
241275

242276
var processThreadNameColumns = new List<ColumnConfiguration>(defaultColumns);
277+
processThreadNameColumns.Insert(3, ParentDepthLevelColumn);
243278
processThreadNameColumns.Remove(EventNameColumn);
244-
processThreadNameColumns.Insert(3, EventNameColumn);
279+
processThreadNameColumns.Insert(4, EventNameColumn);
245280
var processThreadNameConfig = new TableConfiguration("Perfetto Trace Events - Process-Thread-Name")
246281
{
247282
Columns = processThreadNameColumns,

PerfettoProcessor/Events/PerfettoArgEvent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ public class PerfettoArgEvent : PerfettoSqlEvent
1313
public long ArgSetId { get; set; }
1414
public string Flatkey { get; set; }
1515
public string ArgKey { get; set; }
16-
public long IntValue { get; set; }
16+
public long? IntValue { get; set; }
1717
public string StringValue { get; set; }
18-
public double RealValue { get; set; }
18+
public double? RealValue { get; set; }
1919
public string ValueType { get; set; }
2020

2121
public override string GetSqlQuery()

PerfettoProcessor/Events/PerfettoMetadataEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class PerfettoMetadataEvent : PerfettoSqlEvent
1717
public string Name { get; set; }
1818
public string KeyType { get; set; }
1919

20-
public long IntValue { get; set; }
20+
public long? IntValue { get; set; }
2121
public string StrValue { get; set; }
2222

2323

PerfettoProcessor/Events/PerfettoProcessEvent.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ public class PerfettoProcessEvent : PerfettoSqlEvent
1515
public string Type { get; set; }
1616
public long Pid { get; set; }
1717
public string Name { get; set; }
18-
public long StartTimestamp { get; set; }
19-
public long RelativeStartTimestamp { get; set; }
20-
public long EndTimestamp{ get; set; }
21-
public long RelativeEndTimestamp { get; set; }
22-
public long ParentUpid { get; set; }
23-
public long Uid { get; set; }
24-
public long AndroidAppId { get; set; }
18+
public long? StartTimestamp { get; set; }
19+
public long? RelativeStartTimestamp { get; set; }
20+
public long? EndTimestamp{ get; set; }
21+
public long? RelativeEndTimestamp { get; set; }
22+
public long? ParentUpid { get; set; }
23+
public long? Uid { get; set; }
24+
public long? AndroidAppId { get; set; }
2525
public string CmdLine { get; set; }
2626
public long ArgSetId { get; set; }
2727

PerfettoProcessor/Events/PerfettoProcessTrackEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class PerfettoProcessTrackEvent : PerfettoSqlEvent
1414
public long Id { get; set; }
1515
public string Type { get; set; }
1616
public string Name { get; set; }
17-
public long SourceArgSetId { get; set; }
17+
public long? SourceArgSetId { get; set; }
1818
public long Upid { get; set; }
1919

2020
public override string GetSqlQuery()

PerfettoProcessor/Events/PerfettoSliceEvent.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ public class PerfettoSliceEvent : PerfettoSqlEvent
99
{
1010
public const string Key = "PerfettoSliceEvent";
1111

12-
public static string SqlQuery = "select ts, dur, arg_set_id, track_id, name, type, category from slice order by ts";
12+
public static string SqlQuery = "select id, ts, dur, arg_set_id, track_id, name, type, category, parent_id from slice order by id";
13+
public long Id { get; set; }
1314
public string Name { get; set; }
1415
public string Type { get; set; }
1516
public long Duration { get; set; }
@@ -18,6 +19,7 @@ public class PerfettoSliceEvent : PerfettoSqlEvent
1819
public long RelativeTimestamp { get; set; }
1920
public string Category { get; set; }
2021
public long TrackId { get; set; }
22+
public long? ParentId { get; set; }
2123

2224
public override string GetSqlQuery()
2325
{
@@ -45,6 +47,9 @@ public override void ProcessCell(string colName,
4547
var longVal = batch.VarintCells[counters.IntCounter++];
4648
switch (col)
4749
{
50+
case "id":
51+
Id = longVal;
52+
break;
4853
case "ts":
4954
Timestamp = longVal;
5055
break;
@@ -57,6 +62,9 @@ public override void ProcessCell(string colName,
5762
case "track_id":
5863
TrackId = longVal;
5964
break;
65+
case "parent_id":
66+
ParentId = longVal;
67+
break;
6068
}
6169

6270
break;

0 commit comments

Comments
 (0)