static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Specify the name of one trace to be summarized."); return; } var traceName = args[0]; if (!File.Exists(traceName)) { Console.Error.WriteLine("File '{0}' does not exist.", traceName); return; } var settings = new TraceProcessorSettings { // Don't print a setup message on first run. SuppressFirstTimeSetupMessage = true }; using (ITraceProcessor trace = TraceProcessor.Create(traceName, settings)) { // Get process details, including command lines. var pendingProcessData = trace.UseProcesses(); // Get CPU performance counters, on every context switch. var pendingCounterData = trace.UseProcessorCounters(); trace.Process(); var processData = pendingProcessData.Result; var counterData = pendingCounterData.Result; var countersByProcess = FindInterestingProcesses(processData); // Accumulate data for all of the interesting processes. foreach (var entry in counterData.ContextSwitchCounterDeltas) { // This sometimes happens - handle it. if (entry.Process == null) { continue; } Counters last; if (!countersByProcess.TryGetValue(entry.Process, out last)) { continue; } // Accumulate counter values and execution time. foreach (var key in entry.RawCounterDeltas.Keys) { last.counters.TryGetValue(key, out ulong lastCount); lastCount += entry.RawCounterDeltas[key]; last.counters[key] = lastCount; } last.runTime_ns += (entry.StopTime - entry.StartTime).Nanoseconds; last.contextSwitches += 1; countersByProcess[entry.Process] = last; } // Sort the data by CPU time and print it. var sortedCounterData = new List <KeyValuePair <IProcess, Counters> >(countersByProcess); sortedCounterData.Sort((x, y) => y.Value.runTime_ns.CompareTo(x.Value.runTime_ns)); bool printHeader = true; foreach (var entry in sortedCounterData) { if (printHeader) { Console.Write("{0,-29} - CPU time (s) - context switches", "Image name"); foreach (var counterName in entry.Value.counters.Keys) { int fieldWidth = Math.Max(13, counterName.Length); Console.Write(", {0}", counterName.PadLeft(fieldWidth)); } Console.WriteLine(); printHeader = false; } // Arbitrary cutoff for what is "interesting" if (entry.Value.runTime_ns < 100 * 1000 * 1000) { continue; } Console.Write("{0,-29} - {1,8:0.00} - {2,16}", entry.Value.description, entry.Value.runTime_ns / 1e9, entry.Value.contextSwitches); foreach (var counterName in entry.Value.counters.Keys) { int fieldWidth = Math.Max(13, counterName.Length); Console.Write(", {0}", entry.Value.counters[counterName].ToString().PadLeft(fieldWidth)); } Console.WriteLine(); } } }
public static int Main(string[] args) { if (args.Length != 1) { Console.Error.WriteLine("Usage: CyclesPerInstruction.exe <trace.etl>"); return(1); } string tracePath = args[0]; TraceProcessorSettings settings = new TraceProcessorSettings { AllowLostEvents = true }; using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings)) { IPendingResult <IProcessorCounterDataSource> pendingCounterData = trace.UseProcessorCounters(); trace.Process(); IProcessorCounterDataSource counterData = pendingCounterData.Result; if (!counterData.HasCycleCount) { Console.Error.WriteLine("Trace does not contain cycle count data."); return(2); } if (!counterData.HasInstructionCount) { Console.Error.WriteLine("Trace does not contain instruction count data."); return(2); } Dictionary <string, ulong> cyclesByProcess = new Dictionary <string, ulong>(); Dictionary <string, ulong> instructionsByProcess = new Dictionary <string, ulong>(); foreach (IProcessorCounterContextSwitchDelta delta in counterData.ContextSwitchCounterDeltas) { string processName = delta.Thread?.Process?.ImageName ?? "Unknown"; if (!cyclesByProcess.ContainsKey(processName)) { cyclesByProcess.Add(processName, 0); instructionsByProcess.Add(processName, 0); } cyclesByProcess[processName] += delta.CycleCount.Value; instructionsByProcess[processName] += delta.InstructionCount.Value; } foreach (string processName in cyclesByProcess.Keys.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)) { ulong cycles = cyclesByProcess[processName]; ulong instructions = instructionsByProcess[processName]; decimal cyclesPerInstruction = ((decimal)cycles) / instructions; Console.WriteLine($"{processName}: Cycles: {cycles}; Instructions: {instructions}; " + $"CPI: {cyclesPerInstruction:0.####}"); } return(0); } }