Skip to content

Commit 0b3eb6f

Browse files
Add wake info in scheduling table. (#56)
* Add wake info in scheduling table. * Address feedback comments. * Address feedback comments 2. * Add launchSetting.json in .gitignore. * Update thread name in unit test.
1 parent c84d121 commit 0b3eb6f

9 files changed

+293
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*.user
1010
*.userosscache
1111
*.sln.docstates
12+
launchSettings.json
1213

1314
# User-specific files (MonoDevelop/Xamarin Studio)
1415
*.userprefs

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoCpuSchedEventCooker.cs

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,48 @@ public sealed class PerfettoCpuSchedEventCooker : CookedDataReflector, IComposit
2929
{
3030
PerfettoPluginConstants.ThreadCookerPath,
3131
PerfettoPluginConstants.ProcessCookerPath,
32-
PerfettoPluginConstants.SchedSliceCookerPath
32+
PerfettoPluginConstants.SchedSliceCookerPath,
33+
PerfettoPluginConstants.FtraceEventCookerPath
3334
};
3435

3536
[DataOutput]
3637
public ProcessedEventData<PerfettoCpuSchedEvent> CpuSchedEvents { get; }
3738

39+
[DataOutput]
40+
public ProcessedEventData<PerfettoCpuWakeEvent> CpuWakeEvents { get; }
41+
3842
public PerfettoCpuSchedEventCooker() : base(PerfettoPluginConstants.CpuSchedEventCookerPath)
3943
{
40-
this.CpuSchedEvents =
41-
new ProcessedEventData<PerfettoCpuSchedEvent>();
44+
this.CpuSchedEvents = new ProcessedEventData<PerfettoCpuSchedEvent>();
45+
this.CpuWakeEvents = new ProcessedEventData<PerfettoCpuWakeEvent>();
4246
}
4347

4448
public void OnDataAvailable(IDataExtensionRetrieval requiredData)
4549
{
4650
// Gather the data from all the SQL tables
4751
var threadData = requiredData.QueryOutput<ProcessedEventData<PerfettoThreadEvent>>(new DataOutputPath(PerfettoPluginConstants.ThreadCookerPath, nameof(PerfettoThreadCooker.ThreadEvents)));
4852
var processData = requiredData.QueryOutput<ProcessedEventData<PerfettoProcessEvent>>(new DataOutputPath(PerfettoPluginConstants.ProcessCookerPath, nameof(PerfettoProcessCooker.ProcessEvents)));
53+
PopulateCpuSchedulingEvents(requiredData, threadData, processData);
54+
}
55+
56+
void PopulateCpuSchedulingEvents(IDataExtensionRetrieval requiredData, ProcessedEventData<PerfettoThreadEvent> threadData, ProcessedEventData<PerfettoProcessEvent> processData)
57+
{
4958
var schedSliceData = requiredData.QueryOutput<ProcessedEventData<PerfettoSchedSliceEvent>>(new DataOutputPath(PerfettoPluginConstants.SchedSliceCookerPath, nameof(PerfettoSchedSliceCooker.SchedSliceEvents)));
5059

5160
// The sched slice data contains the timings, CPU, priority, and end state info. We get the process and thread from
5261
// those respective tables
5362
var joined = from schedSlice in schedSliceData
5463
join thread in threadData on schedSlice.Utid equals thread.Utid
55-
join threadProcess in processData on thread.Upid equals threadProcess.Upid into pd from threadProcess in pd.DefaultIfEmpty()
64+
join threadProcess in processData on thread.Upid equals threadProcess.Upid into pd
65+
from threadProcess in pd.DefaultIfEmpty()
5666
select new { schedSlice, thread, threadProcess };
5767

68+
// Populate CPU wake events to find associated wake events.
69+
PopulateCpuWakeEvents(requiredData, threadData, processData);
70+
Dictionary<long, List<PerfettoCpuWakeEvent>> wokenTidToWakeEventsMap = this.CpuWakeEvents
71+
.GroupBy(w => w.WokenTid)
72+
.ToDictionary(wg => wg.Key, wg => wg.OrderBy(w => w.Timestamp).ToList());
73+
5874
// Create events out of the joined results
5975
foreach (var result in joined)
6076
{
@@ -66,20 +82,116 @@ join threadProcess in processData on thread.Upid equals threadProcess.Upid into
6682
processName = $"{result.threadProcess.Name} ({result.threadProcess.Pid})";
6783
}
6884

85+
// Find associated CPU wake event.
86+
long tid = result.thread.Tid;
87+
Timestamp startTimestamp = new Timestamp(result.schedSlice.RelativeTimestamp);
88+
Timestamp endTimestamp = new Timestamp(result.schedSlice.RelativeTimestamp + result.schedSlice.Duration);
89+
90+
PerfettoCpuWakeEvent? wakeEvent = null;
91+
if (wokenTidToWakeEventsMap.TryGetValue(tid, out List<PerfettoCpuWakeEvent> wakeEvents))
92+
{
93+
wakeEvent = GetWakeEvent(wakeEvents, startTimestamp);
94+
}
95+
6996
PerfettoCpuSchedEvent ev = new PerfettoCpuSchedEvent
7097
(
7198
processName,
7299
threadName,
100+
tid,
73101
new TimestampDelta(result.schedSlice.Duration),
74-
new Timestamp(result.schedSlice.RelativeTimestamp),
75-
new Timestamp(result.schedSlice.RelativeTimestamp + result.schedSlice.Duration),
102+
startTimestamp,
103+
endTimestamp,
76104
result.schedSlice.Cpu,
77105
result.schedSlice.EndStateStr,
78-
result.schedSlice.Priority
106+
result.schedSlice.Priority,
107+
wakeEvent
79108
);
109+
80110
this.CpuSchedEvents.AddEvent(ev);
81111
}
112+
82113
this.CpuSchedEvents.FinalizeData();
83114
}
115+
116+
void PopulateCpuWakeEvents(IDataExtensionRetrieval requiredData, ProcessedEventData<PerfettoThreadEvent> threadData, ProcessedEventData<PerfettoProcessEvent> processData)
117+
{
118+
var schedWakeData = requiredData.QueryOutput<ProcessedEventData<PerfettoFtraceEvent>>(new DataOutputPath(PerfettoPluginConstants.FtraceEventCookerPath, nameof(PerfettoFtraceEventCooker.FtraceEvents)))
119+
.Where(f => f.Name == "sched_wakeup");
120+
121+
Dictionary<long, PerfettoThreadEvent> tidToThreadMap = threadData.ToDictionary(t => t.Tid);
122+
Dictionary<long, PerfettoProcessEvent> upidToProcessMap = processData.ToDictionary(p => p.Upid);
123+
124+
// Create events out of the joined results
125+
foreach (var wake in schedWakeData)
126+
{
127+
long wokenTid = long.Parse(wake.Values[1]); // This field name is pid but it is woken thread's Tid.
128+
PerfettoThreadEvent wokenThread = tidToThreadMap[wokenTid];
129+
string wokenThreadName = wokenThread.Name;
130+
long? wokenPid = wokenThread.Upid;
131+
string wokenProcessName = wokenPid != null ? upidToProcessMap[wokenPid.Value].Name : wake.Values[0]; // This field name is comms but it is woken process name.
132+
133+
string wakerThreadName = wake.ThreadName;
134+
long wakerTid = wake.Tid;
135+
PerfettoThreadEvent wakerThread = tidToThreadMap[wakerTid];
136+
long? wakerPid = wakerThread.Upid;
137+
string wakerProcessName = wakerPid != null ? upidToProcessMap[wakerPid.Value].Name : String.Empty;
138+
139+
PerfettoCpuWakeEvent ev = new PerfettoCpuWakeEvent
140+
(
141+
wokenProcessName: wokenProcessName,
142+
wokenPid: wokenPid,
143+
wokenThreadName: wokenThreadName,
144+
wokenTid: wokenTid,
145+
wakerProcessName: wakerProcessName,
146+
wakerPid: wakerPid,
147+
wakerThreadName: wakerThreadName,
148+
wakerTid: wakerTid,
149+
timestamp: wake.StartTimestamp,
150+
success: int.Parse(wake.Values[3]), // Success is at index 3
151+
cpu: wake.Cpu,
152+
targetCpu: int.Parse(wake.Values[4]), // TargetCpu is at index 4
153+
priority: int.Parse(wake.Values[2]) // Priority is at index 2
154+
);
155+
156+
this.CpuWakeEvents.AddEvent(ev);
157+
}
158+
159+
this.CpuWakeEvents.FinalizeData();
160+
}
161+
162+
/// <summary>
163+
/// Returns the wake event for the given just before the schedule timestamp.
164+
/// </summary>
165+
/// <param name="cpuWakeEvents">Timestamp sorted wake events for the woken thread.</param>
166+
/// <param name="time">Scheduling timestamp of the thread</param>
167+
/// <returns>CPU wake event if exists else null</returns>
168+
PerfettoCpuWakeEvent? GetWakeEvent(IList<PerfettoCpuWakeEvent> cpuWakeEvents, Timestamp time)
169+
{
170+
int min = 0;
171+
int max = cpuWakeEvents.Count;
172+
173+
while (min < max)
174+
{
175+
int mid = (min + max) / 2;
176+
177+
if (cpuWakeEvents[mid].Timestamp <= time &&
178+
(mid == 0 || // first
179+
mid == max - 1 || // last
180+
(mid + 1 < max && cpuWakeEvents[mid + 1].Timestamp > time))) // next one is greater
181+
{
182+
return cpuWakeEvents[mid];
183+
}
184+
else if (cpuWakeEvents[mid].Timestamp > time)
185+
{
186+
max = mid;
187+
}
188+
else
189+
{
190+
min = mid + 1;
191+
}
192+
}
193+
194+
return null;
195+
}
84196
}
85197
}

PerfettoCds/Pipeline/CompositeDataCookers/PerfettoFtraceEventCooker.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,20 @@ join arg in argsData on raw.ArgSetId equals arg.ArgSetId into args
103103
}
104104

105105
// An event can have a thread+process or just a process
106-
string processName = string.Empty;
107-
string threadName = $"{result.thread.Name} ({result.thread.Tid})";
106+
string processFormattedName = string.Empty;
107+
string threadFormattedName = $"{result.thread.Name} ({result.thread.Tid})";
108108
if (result.threadProcess != null)
109109
{
110-
processName = $"{result.threadProcess.Name} ({result.threadProcess.Pid})";
110+
processFormattedName = $"{result.threadProcess.Name} ({result.threadProcess.Pid})";
111111
}
112112

113113
PerfettoFtraceEvent ev = new PerfettoFtraceEvent
114114
(
115115
new Timestamp(result.raw.RelativeTimestamp),
116-
processName,
117-
threadName,
116+
processFormattedName,
117+
threadFormattedName,
118+
result.thread.Name,
119+
result.thread.Tid,
118120
result.raw.Cpu,
119121
result.raw.Name,
120122
values,

PerfettoCds/Pipeline/DataOutput/PerfettoCpuSchedEvent.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,36 @@ public readonly struct PerfettoCpuSchedEvent
1212
{
1313
public string ProcessName { get; }
1414
public string ThreadName { get; }
15+
public long Tid { get; }
1516
public TimestampDelta Duration { get; }
1617
public Timestamp StartTimestamp { get; }
1718
public Timestamp EndTimestamp { get; }
1819
public uint Cpu { get; }
1920
public string EndState { get; }
2021
public int Priority { get; }
22+
public PerfettoCpuWakeEvent? WakeEvent { get; }
2123

2224
public PerfettoCpuSchedEvent(string processName,
2325
string threadName,
26+
long tid,
2427
TimestampDelta duration,
2528
Timestamp startTimestamp,
2629
Timestamp endTimestamp,
2730
uint cpu,
2831
string endState,
29-
int priority)
32+
int priority,
33+
PerfettoCpuWakeEvent? wakeEvent)
3034
{
3135
this.ProcessName = Common.StringIntern(processName);
3236
this.ThreadName = Common.StringIntern(threadName);
37+
this.Tid = tid;
3338
this.Duration = duration;
3439
this.StartTimestamp = startTimestamp;
3540
this.EndTimestamp = endTimestamp;
3641
this.Cpu = cpu;
3742
this.EndState = endState;
3843
this.Priority = priority;
44+
this.WakeEvent = wakeEvent;
3945
}
4046
}
4147
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
using Microsoft.Performance.SDK;
4+
using Utilities;
5+
6+
namespace PerfettoCds.Pipeline.DataOutput
7+
{
8+
/// <summary>
9+
/// A CPU wake event that displays which process and threads were woken on which CPUs at specific times.
10+
/// </summary>
11+
public readonly struct PerfettoCpuWakeEvent
12+
{
13+
public string WokenProcessName { get; }
14+
public long? WokenPid { get; }
15+
public string WokenThreadName { get; }
16+
public long WokenTid { get; }
17+
18+
public string WakerProcessName { get; }
19+
public long? WakerPid { get; }
20+
public string WakerThreadName { get; }
21+
public long WakerTid { get; }
22+
23+
public Timestamp Timestamp { get; }
24+
public int Success { get; }
25+
public uint Cpu { get; }
26+
public int TargetCpu { get; }
27+
public int Priority { get; }
28+
29+
public PerfettoCpuWakeEvent(string wokenProcessName,
30+
long? wokenPid,
31+
string wokenThreadName,
32+
long wokenTid,
33+
string wakerProcessName,
34+
long? wakerPid,
35+
string wakerThreadName,
36+
long wakerTid,
37+
Timestamp timestamp,
38+
int success,
39+
uint cpu,
40+
int targetCpu,
41+
int priority)
42+
{
43+
this.WokenProcessName = Common.StringIntern(wokenProcessName);
44+
this.WokenPid = wokenPid;
45+
this.WokenThreadName = Common.StringIntern(wokenThreadName);
46+
this.WokenTid = wokenTid;
47+
48+
this.WakerProcessName = Common.StringIntern(wakerProcessName);
49+
this.WakerPid = wakerPid;
50+
this.WakerThreadName = Common.StringIntern(wakerThreadName);
51+
this.WakerTid = wakerTid;
52+
53+
this.Timestamp = timestamp;
54+
this.Success = success;
55+
this.Cpu = cpu;
56+
this.TargetCpu = targetCpu;
57+
this.Priority = priority;
58+
}
59+
}
60+
}

PerfettoCds/Pipeline/DataOutput/PerfettoFtraceEvent.cs.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ namespace PerfettoCds.Pipeline.DataOutput
1212
public readonly struct PerfettoFtraceEvent
1313
{
1414
public Timestamp StartTimestamp { get; }
15-
public string ProcessName { get; }
15+
public string ProcessFormattedName { get; }
16+
public string ThreadFormattedName { get; }
1617
public string ThreadName { get; }
18+
public long Tid { get; }
1719
public uint Cpu { get; }
1820
// Name of the ftrace event
1921
public string Name { get; }
@@ -23,16 +25,20 @@ public readonly struct PerfettoFtraceEvent
2325
public string[] ArgKeys { get; }
2426

2527
public PerfettoFtraceEvent(Timestamp startTimestamp,
26-
string processName,
28+
string processFormattedName,
29+
string threadFormattedName,
2730
string threadName,
31+
long tid,
2832
uint cpu,
2933
string name,
3034
List<string> values,
3135
List<string> argKeys)
3236
{
3337
this.StartTimestamp = startTimestamp;
34-
this.ProcessName = Common.StringIntern(processName);
38+
this.ProcessFormattedName = Common.StringIntern(processFormattedName);
39+
this.ThreadFormattedName = Common.StringIntern(threadFormattedName);
3540
this.ThreadName = Common.StringIntern(threadName);
41+
this.Tid = tid;
3642
this.Cpu = cpu;
3743
this.Name = Common.StringIntern(name);
3844
this.Values = values.ToArray();

0 commit comments

Comments
 (0)