Skip to content

Commit 855fe0b

Browse files
authored
Fix exception when parsing trace with null arg value. (#93)
* Fix exception when parsing trace with null arg value. Refactor other args processors to use common arg processor with this fix.
1 parent 08a91b6 commit 855fe0b

12 files changed

+57
-122
lines changed

PerfettoCds/PerfettoCds.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.1</TargetFramework>
5-
<Version>1.5.0</Version>
5+
<Version>1.5.1</Version>
66
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
77
<Authors>Microsoft</Authors>
88
<Company>Microsoft Corp.</Company>

PerfettoCds/Pipeline/Args.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,32 @@ namespace PerfettoCds
1212
{
1313
public class Args
1414
{
15-
public List<string> ArgKeys { get; private set; }
16-
public List<object> Values { get; private set; }
17-
18-
public static Args ParseArgs(IEnumerable<PerfettoArgEvent> perfettoArgEvents)
15+
public static Dictionary<string, object> ParseArgs(IEnumerable<PerfettoArgEvent> perfettoArgEvents)
1916
{
20-
var args = new Args();
21-
args.ArgKeys = new List<string>();
22-
args.Values = new List<object>();
17+
var args = new Dictionary<string, object>();
2318

2419
// Each event has multiple of these "debug annotations". They get stored in lists
2520
foreach (var arg in perfettoArgEvents)
2621
{
27-
args.ArgKeys.Add(Common.StringIntern(arg.ArgKey));
2822
switch (arg.ValueType)
2923
{
3024
case "json":
3125
case "string":
32-
args.Values.Add(Common.StringIntern(arg.StringValue));
26+
args.Add(arg.ArgKey, Common.StringIntern(arg.StringValue));
3327
break;
3428
case "bool":
3529
case "int":
36-
args.Values.Add(arg.IntValue);
30+
args.Add(arg.ArgKey, arg.IntValue);
3731
break;
3832
case "uint":
3933
case "pointer":
40-
args.Values.Add((uint)arg.IntValue);
34+
args.Add(arg.ArgKey, (uint)arg.IntValue);
4135
break;
4236
case "real":
43-
args.Values.Add(arg.RealValue);
37+
args.Add(arg.ArgKey, arg.RealValue);
38+
break;
39+
case "null":
40+
args.Add(arg.ArgKey, null);
4441
break;
4542
default:
4643
throw new Exception("Unexpected Perfetto value type");

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoCpuSchedEventCooker.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,11 @@ void PopulateCpuWakeEvents(IDataExtensionRetrieval requiredData, ProcessedEventD
153153
// Create events out of the joined results
154154
foreach (var wake in schedWakeData)
155155
{
156-
var wokenTid = uint.Parse(wake.Values[1]); // This field name is pid but it is woken thread's Tid.
156+
var wokenTid = uint.Parse(wake.Args.ElementAt(1).Value.ToString()); // This field name is pid but it is woken thread's Tid.
157157
PerfettoThreadEvent wokenThread = tidToThreadMap[wokenTid];
158158
string wokenThreadName = wokenThread.Name;
159159
var wokenPid = wokenThread.Upid;
160-
string wokenProcessName = wokenPid != null ? upidToProcessMap[wokenPid.Value].Name : wake.Values[0]; // This field name is comms but it is woken process name.
160+
string wokenProcessName = wokenPid != null ? upidToProcessMap[wokenPid.Value].Name : wake.Args.ElementAt(0).Value.ToString(); // This field name is comms but it is woken process name.
161161

162162
string wakerThreadName = wake.ThreadName;
163163
var wakerTid = wake.Tid;
@@ -176,10 +176,10 @@ void PopulateCpuWakeEvents(IDataExtensionRetrieval requiredData, ProcessedEventD
176176
wakerThreadName: wakerThreadName,
177177
wakerTid: wakerTid,
178178
timestamp: wake.StartTimestamp,
179-
success: int.Parse(wake.Values[3]), // Success is at index 3
179+
success: int.Parse(wake.Args.ElementAt(3).Value.ToString()), // Success is at index 3
180180
cpu: wake.Cpu,
181-
targetCpu: int.Parse(wake.Values[4]), // TargetCpu is at index 4
182-
priority: int.Parse(wake.Values[2]) // Priority is at index 2
181+
targetCpu: int.Parse(wake.Args.ElementAt(4).Value.ToString()), // TargetCpu is at index 4
182+
priority: int.Parse(wake.Args.ElementAt(2).Value.ToString()) // Priority is at index 2
183183
);
184184

185185
this.CpuWakeEvents.AddEvent(ev);

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoFtraceEventCooker.cs

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -73,35 +73,7 @@ join arg in argsData on raw.ArgSetId equals arg.ArgSetId into args
7373
foreach (var result in joined)
7474
{
7575
MaximumEventFieldCount = Math.Max(MaximumEventFieldCount, result.args.Count());
76-
77-
List<string> argKeys = new List<string>();
78-
List<string> values = new List<string>();
79-
80-
// Each event has multiple of these arguments. They get stored in lists
81-
foreach (var arg in result.args)
82-
{
83-
argKeys.Add(Common.StringIntern(arg.ArgKey));
84-
switch (arg.ValueType)
85-
{
86-
case "json":
87-
case "string":
88-
values.Add(Common.StringIntern(arg.StringValue));
89-
break;
90-
case "bool":
91-
case "int":
92-
values.Add(Common.StringIntern(arg.IntValue.ToString()));
93-
break;
94-
case "uint":
95-
case "pointer":
96-
values.Add(Common.StringIntern(((uint)arg.IntValue).ToString()));
97-
break;
98-
case "real":
99-
values.Add(Common.StringIntern(arg.RealValue.ToString()));
100-
break;
101-
default:
102-
throw new Exception("Unexpected Perfetto value type");
103-
}
104-
}
76+
var args = Args.ParseArgs(result.args);
10577

10678
// An event can have a thread+process or just a process
10779
string processFormattedName = string.Empty;
@@ -120,8 +92,7 @@ join arg in argsData on raw.ArgSetId equals arg.ArgSetId into args
12092
result.thread.Tid,
12193
result.raw.Cpu,
12294
result.raw.Name,
123-
values,
124-
argKeys
95+
args
12596
);
12697
this.FtraceEvents.AddEvent(ev);
12798
}

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoGenericEventCooker.cs

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -188,48 +188,25 @@ from processTrackProcess in pd2.DefaultIfEmpty()
188188
foreach (var result in joined)
189189
{
190190
MaximumEventFieldCount = Math.Max(MaximumEventFieldCount, result.args.Count());
191-
191+
var args = Args.ParseArgs(result.args);
192192
string provider = string.Empty;
193193

194-
// TODO - Replace with Args.ParseArgs
195-
List<string> argKeys = new List<string>();
196-
List<string> values = new List<string>();
197194
// Each event has multiple of these "debug annotations". They get stored in lists
198-
foreach (var arg in result.args)
199-
{
200-
argKeys.Add(Common.StringIntern(arg.ArgKey));
201-
switch (arg.ValueType)
202-
{
203-
case "json":
204-
case "string":
205-
values.Add(Common.StringIntern(arg.StringValue));
206-
207-
// Check if there are mappings present and if the arg key is the keyword we're looking for
208-
if (ProviderGuidMapping.Count > 0 && arg.ArgKey.ToLower().Contains(ProviderDebugAnnotationKey))
209-
{
210-
// The value for this key was flagged as containing a provider GUID that needs to be mapped to its provider name
211-
// Check if the mapping exists
212-
Guid guid = new Guid(arg.StringValue);
213-
if (ProviderGuidMapping.ContainsKey(guid))
214-
{
215-
HasProviders = true;
216-
provider = ProviderGuidMapping[guid];
217-
}
218-
}
219-
break;
220-
case "bool":
221-
case "int":
222-
values.Add(Common.StringIntern(arg.IntValue.ToString()));
223-
break;
224-
case "uint":
225-
case "pointer":
226-
values.Add(Common.StringIntern(((uint)arg.IntValue).ToString()));
227-
break;
228-
case "real":
229-
values.Add(Common.StringIntern(arg.RealValue.ToString()));
230-
break;
231-
default:
232-
throw new Exception("Unexpected Perfetto value type");
195+
foreach (var arg in args)
196+
{
197+
// Check if there are mappings present and if the arg key is the keyword we're looking for
198+
if (ProviderGuidMapping.Count > 0 && arg.Key.ToLower().Contains(ProviderDebugAnnotationKey))
199+
{
200+
// The value for this key was flagged as containing a provider GUID that needs to be mapped to its provider name
201+
// Check if the mapping exists
202+
if (Guid.TryParse(arg.Value.ToString(), out Guid guid))
203+
{
204+
if (ProviderGuidMapping.ContainsKey(guid))
205+
{
206+
HasProviders = true;
207+
provider = ProviderGuidMapping[guid];
208+
}
209+
}
233210
}
234211
}
235212

@@ -304,8 +281,7 @@ from processTrackProcess in pd2.DefaultIfEmpty()
304281
new Timestamp(result.slice.RelativeTimestamp + result.slice.Duration) :
305282
longestEndTs,
306283
result.slice.Category,
307-
values,
308-
argKeys,
284+
args,
309285
processName,
310286
processLabel,
311287
threadName,

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoProcessEventCooker.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ from parentProcess in pp.DefaultIfEmpty()
6969
foreach (var result in joined)
7070
{
7171
var args = Args.ParseArgs(result.args);
72-
MaximumArgsEventFieldCount = Math.Max(MaximumArgsEventFieldCount, args.ArgKeys.Count());
72+
MaximumArgsEventFieldCount = Math.Max(MaximumArgsEventFieldCount, args.Count);
7373

7474
const string ChromeProcessLabel = "chrome.process_label[0]";
7575
string processLabel = null;
76-
if (args.ArgKeys.Contains(ChromeProcessLabel))
76+
if (args.ContainsKey(ChromeProcessLabel))
7777
{
78-
processLabel = (string) args.Values[args.ArgKeys.IndexOf(ChromeProcessLabel)];
78+
processLabel = (string) args[ChromeProcessLabel];
7979
}
8080

8181
var ev = new PerfettoProcessEvent
@@ -93,8 +93,7 @@ from parentProcess in pp.DefaultIfEmpty()
9393
result.process.Uid,
9494
result.process.AndroidAppId,
9595
result.process.CmdLine,
96-
args.ArgKeys.ToArray(),
97-
args.Values.ToArray(),
96+
args,
9897
result.packageList
9998
);
10099
this.ProcessEvents.AddEvent(ev);

PerfettoCds/Pipeline/DataOutput/PerfettoFtraceEvent.cs.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ public readonly struct PerfettoFtraceEvent
1818
public uint Tid { get; }
1919
public uint Cpu { get; }
2020
// Name of the ftrace event
21-
public string Name { get; }
22-
23-
// From Args table. Variable number per event
24-
public string[] Values { get; }
25-
public string[] ArgKeys { get; }
21+
public string Name { get; }
22+
23+
// From Args table. Variable number per event
24+
public Dictionary<string, object> Args { get; }
2625

2726
public PerfettoFtraceEvent(Timestamp startTimestamp,
2827
string processFormattedName,
@@ -31,8 +30,7 @@ public PerfettoFtraceEvent(Timestamp startTimestamp,
3130
uint tid,
3231
uint cpu,
3332
string name,
34-
List<string> values,
35-
List<string> argKeys)
33+
Dictionary<string, object> args)
3634
{
3735
this.StartTimestamp = startTimestamp;
3836
this.ProcessFormattedName = Common.StringIntern(processFormattedName);
@@ -41,8 +39,7 @@ public PerfettoFtraceEvent(Timestamp startTimestamp,
4139
this.Tid = tid;
4240
this.Cpu = cpu;
4341
this.Name = Common.StringIntern(name);
44-
this.Values = values.ToArray();
45-
this.ArgKeys = argKeys.ToArray();
42+
this.Args = args;
4643
}
4744
}
4845
}

PerfettoCds/Pipeline/DataOutput/PerfettoGenericEvent.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public readonly struct PerfettoGenericEvent
2222
public string Category { get; }
2323

2424
// From Args table. The debug annotations for an event. Variable number per event
25-
public string[] Values { get; }
26-
public string[] ArgKeys { get; }
25+
public Dictionary<string, object> Args { get; }
2726

2827
// From Process table
2928
public string Process { get; }
@@ -47,9 +46,8 @@ public PerfettoGenericEvent(string eventName,
4746
TimestampDelta duration,
4847
Timestamp startTimestamp,
4948
Timestamp endTimestamp,
50-
string category,
51-
List<string> values,
52-
List<string> argKeys,
49+
string category,
50+
Dictionary<string, object> args,
5351
string process,
5452
string processLabel,
5553
string thread,
@@ -65,8 +63,7 @@ public PerfettoGenericEvent(string eventName,
6563
StartTimestamp = startTimestamp;
6664
EndTimestamp = endTimestamp;
6765
Category = Common.StringIntern(category);
68-
Values = values.ToArray();
69-
ArgKeys = argKeys.ToArray();
66+
Args = args;
7067
Process = Common.StringIntern(process);
7168
ProcessLabel = Common.StringIntern(processLabel);
7269
Thread = Common.StringIntern(thread);

PerfettoCds/Pipeline/DataOutput/PerfettoProcessEvent.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ public class PerfettoProcessEvent
7575
/// <summary>
7676
/// Extra args for this process
7777
/// </summary>
78-
public string[] ArgKeys { get; }
79-
public object[] Values { get; }
78+
public Dictionary<string, object> Args { get; }
8079
public PerfettoPackageListEvent PackageList { get; }
8180

82-
public PerfettoProcessEvent(long id, string type, uint upid, uint pid, string name, string label, Timestamp startTimestamp, Timestamp endTimestamp, uint? parentUpid, PerfettoProcessRawEvent parentProcess, uint? uid, uint? androidAppId, string cmdLine, string[] argKeys, object[] values, PerfettoPackageListEvent packageList)
81+
public PerfettoProcessEvent(long id, string type, uint upid, uint pid, string name, string label, Timestamp startTimestamp, Timestamp endTimestamp, uint? parentUpid, PerfettoProcessRawEvent parentProcess, uint? uid, uint? androidAppId, string cmdLine, Dictionary<string, object> args, PerfettoPackageListEvent packageList)
8382
{
8483
Id = id;
8584
Type = type;
@@ -94,8 +93,7 @@ public PerfettoProcessEvent(long id, string type, uint upid, uint pid, string na
9493
Uid = uid;
9594
AndroidAppId = androidAppId;
9695
CmdLine = cmdLine;
97-
ArgKeys = argKeys;
98-
Values = values;
96+
Args = Args;
9997
PackageList = packageList;
10098
}
10199
}

PerfettoCds/Pipeline/Tables/PerfettoFtraceEventTable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieva
100100
var colIndex = index; // This seems unncessary but causes odd runtime behavior if not done this way. Compiler is confused perhaps because w/o this func will index=event.FieldNames.Count every time. index is passed as ref but colIndex as value into func
101101
string fieldName = "Field " + (colIndex + 1);
102102

103-
var eventFieldNameProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.ArgKeys.Length ? ftraceEvent.ArgKeys[colIndex] : string.Empty);
103+
var eventFieldNameProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.Args?.Count ? ftraceEvent.Args.ElementAt(colIndex).Key : string.Empty);
104104

105105
// generate a column configuration
106106
var fieldColumnConfiguration = new ColumnConfiguration(
@@ -115,7 +115,7 @@ public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieva
115115
// Add this column to the column order
116116
fieldColumns.Add(fieldColumnConfiguration);
117117

118-
var eventFieldAsStringProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.Values.Length ? ftraceEvent.Values[colIndex] : string.Empty);
118+
var eventFieldAsStringProjection = eventProjection.Compose((ftraceEvent) => colIndex < ftraceEvent.Args?.Count ? ftraceEvent.Args.ElementAt(colIndex).Value : string.Empty);
119119

120120
tableGenerator.AddColumn(fieldColumnConfiguration, eventFieldAsStringProjection);
121121
}

0 commit comments

Comments
 (0)