@@ -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}
0 commit comments