@@ -24,68 +24,207 @@ limitations under the License.
24
24
// Note that this URL has changed once already, so caveat blog lector
25
25
26
26
using Microsoft . Windows . EventTracing ;
27
- class IdleWakeups
27
+ using Microsoft . Windows . EventTracing . Symbols ;
28
+
29
+ namespace IdleWakeups
28
30
{
29
- static void ProcessTrace ( ITraceProcessor trace )
31
+ // A summary of a particular idle wakeup call stack including its count and all frames in the
32
+ // stack.
33
+ struct StackDetails
34
+ {
35
+ public long Count ;
36
+ public IReadOnlyList < StackFrame > Stack ;
37
+ }
38
+
39
+ class SnapshotSummary
30
40
{
31
- // Specify what data we want, process the trace, then get the data.
32
- var pendingContextSwitchData = trace . UseContextSwitchData ( ) ;
33
- trace . Process ( ) ;
34
- var csData = pendingContextSwitchData . Result ;
35
-
36
- long chromeSwitches = 0 ;
37
- long chromeIdleSwitches = 0 ;
38
- // Iterate through all context switches in the trace.
39
- foreach ( var contextSwitch in csData . ContextSwitches )
41
+ public SnapshotSummary ( Dictionary < string , StackDetails > idleWakeupsByStackId )
40
42
{
41
- var imageName = contextSwitch . SwitchIn . Process . ImageName ;
42
- var oldImageName = contextSwitch . SwitchOut . Process . ImageName ;
43
- // Gets the stack of the thread switching in.
44
- var callStack = contextSwitch . SwitchIn . Stack ;
45
- if ( imageName == "chrome.exe" )
43
+ idleWakeupsByStackId_ = idleWakeupsByStackId ;
44
+ }
45
+
46
+ public Dictionary < string , StackDetails > idleWakeupsByStackId_ ;
47
+ public Dictionary < Int32 , long > previousCStates_ = null ;
48
+ public long chromeSwitches_ = 0 ;
49
+ public long chromeIdleSwitches_ = 0 ;
50
+ }
51
+
52
+ class IdleWakeupsAnalysis
53
+ {
54
+ // Process a trace and create a summary.
55
+ private static SnapshotSummary GetIdleWakeupsSummary ( ITraceProcessor trace )
56
+ {
57
+ var pendingCpuSchedulingData = trace . UseCpuSchedulingData ( ) ;
58
+ var pendingSymbols = trace . UseSymbols ( ) ;
59
+
60
+ trace . Process ( ) ;
61
+
62
+ var cpuSchedData = pendingCpuSchedulingData . Result ;
63
+ var symbols = pendingSymbols . Result ;
64
+ symbols . LoadSymbolsForConsoleAsync ( SymCachePath . Automatic ) . GetAwaiter ( ) . GetResult ( ) ;
65
+
66
+ // Key: string which identifies a callstack.
67
+ // Value: counts the frequency of a given key and also stores a list of all stack
68
+ // frames for the callstack.
69
+ var idleWakeupsStack = new Dictionary < string , StackDetails > ( ) ;
70
+
71
+ long chromeSwitches = 0 ;
72
+ long chromeIdleSwitches = 0 ;
73
+
74
+ var previousCStates = new Dictionary < Int32 , long > ( ) ;
75
+
76
+ // Scan all context switches and build up the dictionary based on all detected idle
77
+ // wakeups where chrome.exe wakes up the Idle thread.
78
+ foreach ( var contextSwitch in cpuSchedData . ContextSwitches )
46
79
{
47
- chromeSwitches ++ ;
48
- if ( oldImageName == "Idle" )
80
+ var switchInImageName = contextSwitch . SwitchIn . Process . ImageName ;
81
+ var switchOutImageName = contextSwitch . SwitchOut . Process . ImageName ;
82
+ var callStackIn = contextSwitch . SwitchIn . Stack ;
83
+ if ( switchInImageName == "chrome.exe" )
49
84
{
50
- chromeIdleSwitches ++ ;
51
- if ( callStack != null )
85
+ chromeSwitches ++ ;
86
+ if ( switchOutImageName == "Idle" )
52
87
{
53
- Console . WriteLine ( "IThreadStack.IsIdle: {0}" , callStack . IsIdle ) ;
88
+ // Idle wakeup is detected.
89
+ chromeIdleSwitches ++ ;
90
+ var prevCState = contextSwitch . PreviousCState ;
91
+ if ( prevCState . HasValue )
92
+ {
93
+ previousCStates . TryGetValue ( prevCState . Value , out long count ) ;
94
+ count += 1 ;
95
+ previousCStates [ prevCState . Value ] = count ;
96
+ }
97
+ if ( callStackIn == null )
98
+ {
99
+ continue ;
100
+ }
101
+ try
102
+ {
103
+ // Get a string that allows us to identify the callstack and use it as
104
+ // key in a dictionary which stores count and the complete stack as
105
+ // value.
106
+ var analyzerStringId = callStackIn . GetAnalyzerString ( ) ;
107
+ // List of frames in the stack.
108
+ var stackFrames = callStackIn . Frames ;
109
+ // Update the dictionary.
110
+ idleWakeupsStack . TryGetValue ( analyzerStringId , out StackDetails value ) ;
111
+ value . Count += 1 ;
112
+ value . Stack = callStackIn . Frames ;
113
+ idleWakeupsStack [ analyzerStringId ] = value ;
114
+ }
115
+ catch ( Exception ex )
116
+ {
117
+ Console . WriteLine ( ex . ToString ( ) ) ;
118
+ }
54
119
}
55
120
}
56
121
}
57
- }
58
- Console . WriteLine ( "{0} idlewakeups out of {1} context switches ({2:P})." ,
59
- chromeIdleSwitches , chromeSwitches ,
60
- chromeIdleSwitches / ( double ) chromeSwitches ) ;
61
- }
62
122
63
- static void Main ( string [ ] args )
64
- {
65
- foreach ( string traceName in args )
123
+ var result = new SnapshotSummary ( idleWakeupsStack ) ;
124
+ result . previousCStates_ = previousCStates ;
125
+ result . chromeIdleSwitches_ = chromeIdleSwitches ;
126
+ result . chromeSwitches_ = chromeSwitches ;
127
+
128
+ return result ;
129
+ }
130
+ static void Main ( string [ ] args )
66
131
{
67
- Console . WriteLine ( "Processing trace '{0}'" , traceName ) ;
68
- var settings = new TraceProcessorSettings
132
+ string traceName = "" ;
133
+ bool showSummary = false ;
134
+ foreach ( string arg in args )
69
135
{
70
- // Don't print a setup message on first run.
71
- SuppressFirstTimeSetupMessage = true
72
- } ;
73
- try
136
+ if ( arg == "-s" || arg == "--summary" )
137
+ showSummary = true ;
138
+ else if ( traceName . Length == 0 )
139
+ traceName = arg ;
140
+ else
141
+ {
142
+ Console . Error . WriteLine ( "error: unrecognized arguments: {0}" , arg ) ;
143
+ return ;
144
+ }
145
+ }
146
+
147
+ if ( traceName . Length == 0 )
148
+ {
149
+ Console . Error . WriteLine ( "usage: IdleWakeups.exe [-s,--summary] <filename>" ) ;
150
+ Console . Error . WriteLine ( "error: too few arguments" ) ;
151
+ return ;
152
+ }
153
+
154
+ if ( ! File . Exists ( traceName ) )
74
155
{
75
- using ( ITraceProcessor trace = TraceProcessor . Create ( traceName , settings ) )
76
- ProcessTrace ( trace ) ;
156
+ Console . Error . WriteLine ( "File '{0}' does not exist." , traceName ) ;
157
+ return ;
77
158
}
78
- catch ( TraceLostEventsException e )
159
+ using ( ITraceProcessor trace = TraceProcessor . Create ( traceName ) )
79
160
{
80
- Console . WriteLine ( e . Message ) ;
81
- Console . WriteLine ( "Trying again with AllowLostEvents specified. Results may be less reliable." ) ;
82
- Console . WriteLine ( ) ;
161
+ Console . WriteLine ( "Processing trace '{0}'" , Path . GetFileName ( traceName ) ) ;
162
+
163
+ // Scan all context switches and store callstacks where Chrome causes an idle
164
+ // wakeup. Also store the frequency of each unique callstack.
165
+ var iwakeups = GetIdleWakeupsSummary ( trace ) ;
166
+
167
+ // Print out a summary of all callstacks that causes idle wakeups.
168
+ // Example (can be later be used as input to separate Go-script which converts the
169
+ // list into something that can be read by pprof):
170
+ //
171
+ // iwakeup:
172
+ // ntoskrnl.exe!SwapContext
173
+ // ntoskrnl.exe!KiSwapContext
174
+ // ...
175
+ // ntdll.dll!RtlUserThreadStart
176
+ // 1070
177
+ // iwakeup:
178
+ // ntoskrnl.exe!SwapContext
179
+ // ntoskrnl.exe!KiSwapContext
180
+ // ..
181
+ // ntdll.dll!RtlUserThreadStart
182
+ // 1011
183
+ const int maxPrinted = 40 ;
184
+ var sortedStackEntries =
185
+ new List < KeyValuePair < string , StackDetails > > ( iwakeups . idleWakeupsByStackId_ ) ;
186
+ sortedStackEntries . Sort ( ( x , y ) => y . Value . Count . CompareTo ( x . Value . Count ) ) ;
187
+ foreach ( KeyValuePair < string , StackDetails > kvp in sortedStackEntries . Take ( maxPrinted ) )
188
+ {
189
+ Console . WriteLine ( "iwakeup:" ) ;
190
+ foreach ( var entry in kvp . Value . Stack )
191
+ {
192
+ try
193
+ {
194
+ var stackFrame = entry . GetAnalyzerString ( ) ;
195
+ Console . WriteLine ( " {0}" , stackFrame ) ;
196
+ }
197
+ catch ( Exception ex )
198
+ {
199
+ // Console.WriteLine(ex.ToString());
200
+ }
201
+ }
202
+ Console . WriteLine ( " {0}" , kvp . Value . Count ) ;
203
+ }
204
+
205
+ // Only append a summary if it was explicitly specified using the '-s' argument.
206
+ if ( ! showSummary )
207
+ {
208
+ return ;
209
+ }
210
+
211
+ // Append a short summary of the relationship between the total amount of detected
212
+ // context switches caused by Chrome and those that caused an idle wakeup.
213
+ Console . WriteLine ( "{0} idlewakeups out of {1} context switches ({2:P})." ,
214
+ iwakeups . chromeIdleSwitches_ , iwakeups . chromeSwitches_ ,
215
+ iwakeups . chromeIdleSwitches_ / ( double ) iwakeups . chromeSwitches_ ) ;
83
216
84
- settings . AllowLostEvents = true ;
85
- using ( ITraceProcessor trace = TraceProcessor . Create ( traceName , settings ) )
86
- ProcessTrace ( trace ) ;
217
+ // Append summary of C-state residencies (@ Idle -> chrome.exe).
218
+ var list = iwakeups . previousCStates_ . Keys . ToList ( ) ;
219
+ list . Sort ( ) ;
220
+ Console . WriteLine ( "Previous C-State (Idle -> chrome.exe):" ) ;
221
+ foreach ( var key in list )
222
+ {
223
+ Console . WriteLine ( " C{0}: {1,7} ({2,6:P})" ,
224
+ key , iwakeups . previousCStates_ [ key ] ,
225
+ iwakeups . previousCStates_ [ key ] / ( double ) iwakeups . chromeIdleSwitches_ ) ;
226
+ }
87
227
}
88
228
}
89
229
}
90
230
}
91
-
0 commit comments