Skip to content
This repository was archived by the owner on May 29, 2025. It is now read-only.

Commit 6a97aef

Browse files
committed
Update IdleWakeups.cs
1 parent c281422 commit 6a97aef

File tree

1 file changed

+184
-45
lines changed

1 file changed

+184
-45
lines changed

TraceProcessors/IdleWakeups/IdleWakeups.cs

Lines changed: 184 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,68 +24,207 @@ limitations under the License.
2424
// Note that this URL has changed once already, so caveat blog lector
2525

2626
using Microsoft.Windows.EventTracing;
27-
class IdleWakeups
27+
using Microsoft.Windows.EventTracing.Symbols;
28+
29+
namespace IdleWakeups
2830
{
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
3040
{
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)
4042
{
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)
4679
{
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")
4984
{
50-
chromeIdleSwitches++;
51-
if (callStack != null)
85+
chromeSwitches++;
86+
if (switchOutImageName == "Idle")
5287
{
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+
}
54119
}
55120
}
56121
}
57-
}
58-
Console.WriteLine("{0} idlewakeups out of {1} context switches ({2:P}).",
59-
chromeIdleSwitches, chromeSwitches,
60-
chromeIdleSwitches / (double)chromeSwitches);
61-
}
62122

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)
66131
{
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)
69135
{
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))
74155
{
75-
using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings))
76-
ProcessTrace(trace);
156+
Console.Error.WriteLine("File '{0}' does not exist.", traceName);
157+
return;
77158
}
78-
catch (TraceLostEventsException e)
159+
using (ITraceProcessor trace = TraceProcessor.Create(traceName))
79160
{
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_);
83216

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+
}
87227
}
88228
}
89229
}
90230
}
91-

0 commit comments

Comments
 (0)