@@ -29,32 +29,48 @@ public sealed class PerfettoCpuSchedEventCooker : CookedDataReflector, IComposit
29
29
{
30
30
PerfettoPluginConstants . ThreadCookerPath ,
31
31
PerfettoPluginConstants . ProcessCookerPath ,
32
- PerfettoPluginConstants . SchedSliceCookerPath
32
+ PerfettoPluginConstants . SchedSliceCookerPath ,
33
+ PerfettoPluginConstants . FtraceEventCookerPath
33
34
} ;
34
35
35
36
[ DataOutput ]
36
37
public ProcessedEventData < PerfettoCpuSchedEvent > CpuSchedEvents { get ; }
37
38
39
+ [ DataOutput ]
40
+ public ProcessedEventData < PerfettoCpuWakeEvent > CpuWakeEvents { get ; }
41
+
38
42
public PerfettoCpuSchedEventCooker ( ) : base ( PerfettoPluginConstants . CpuSchedEventCookerPath )
39
43
{
40
- this . CpuSchedEvents =
41
- new ProcessedEventData < PerfettoCpuSchedEvent > ( ) ;
44
+ this . CpuSchedEvents = new ProcessedEventData < PerfettoCpuSchedEvent > ( ) ;
45
+ this . CpuWakeEvents = new ProcessedEventData < PerfettoCpuWakeEvent > ( ) ;
42
46
}
43
47
44
48
public void OnDataAvailable ( IDataExtensionRetrieval requiredData )
45
49
{
46
50
// Gather the data from all the SQL tables
47
51
var threadData = requiredData . QueryOutput < ProcessedEventData < PerfettoThreadEvent > > ( new DataOutputPath ( PerfettoPluginConstants . ThreadCookerPath , nameof ( PerfettoThreadCooker . ThreadEvents ) ) ) ;
48
52
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
+ {
49
58
var schedSliceData = requiredData . QueryOutput < ProcessedEventData < PerfettoSchedSliceEvent > > ( new DataOutputPath ( PerfettoPluginConstants . SchedSliceCookerPath , nameof ( PerfettoSchedSliceCooker . SchedSliceEvents ) ) ) ;
50
59
51
60
// The sched slice data contains the timings, CPU, priority, and end state info. We get the process and thread from
52
61
// those respective tables
53
62
var joined = from schedSlice in schedSliceData
54
63
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 ( )
56
66
select new { schedSlice , thread , threadProcess } ;
57
67
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
+
58
74
// Create events out of the joined results
59
75
foreach ( var result in joined )
60
76
{
@@ -66,20 +82,116 @@ join threadProcess in processData on thread.Upid equals threadProcess.Upid into
66
82
processName = $ "{ result . threadProcess . Name } ({ result . threadProcess . Pid } )";
67
83
}
68
84
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
+
69
96
PerfettoCpuSchedEvent ev = new PerfettoCpuSchedEvent
70
97
(
71
98
processName ,
72
99
threadName ,
100
+ tid ,
73
101
new TimestampDelta ( result . schedSlice . Duration ) ,
74
- new Timestamp ( result . schedSlice . RelativeTimestamp ) ,
75
- new Timestamp ( result . schedSlice . RelativeTimestamp + result . schedSlice . Duration ) ,
102
+ startTimestamp ,
103
+ endTimestamp ,
76
104
result . schedSlice . Cpu ,
77
105
result . schedSlice . EndStateStr ,
78
- result . schedSlice . Priority
106
+ result . schedSlice . Priority ,
107
+ wakeEvent
79
108
) ;
109
+
80
110
this . CpuSchedEvents . AddEvent ( ev ) ;
81
111
}
112
+
82
113
this . CpuSchedEvents . FinalizeData ( ) ;
83
114
}
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
+ }
84
196
}
85
197
}
0 commit comments