public static ThreadSnapshotStats FromSnapshots(IEnumerable <ThreadSnapshot> snapshots) { ThreadSnapshotStats stats = new ThreadSnapshotStats(); stats.ThreadId = snapshots.First().Id; stats.TotalKernelTime = snapshots.Last().KernelTime - snapshots.First().KernelTime; stats.TotalUserTime = snapshots.Last().UserTime - snapshots.First().UserTime; stats.CommonStack = snapshots.First().StackTrace.ToList(); foreach (var stack in snapshots.Select(_ => _.StackTrace.ToList())) { while (stats.CommonStack.Count > stack.Count) { stats.CommonStack.RemoveAt(0); } while (stats.CommonStack.Count > 0 && stack.Count > 0 && stats.CommonStack[0] != stack[0]) { stats.CommonStack.RemoveAt(0); stack.RemoveAt(0); } } return(stats); }
static void Main(string[] args) { if (args.Length < 1) { Usage(); return; } int samples = 10; int sampleInterval = 1000; var state = ParseState.Unknown; foreach (var arg in args.Skip(1)) { switch (state) { case ParseState.Unknown: if (arg.ToLower() == "/s") { state = ParseState.Samples; } else if (arg.ToLower() == "/i") { state = ParseState.Interval; } else { Usage(); return; } break; case ParseState.Samples: if (!Int32.TryParse(arg, out samples)) { Usage(); return; } state = ParseState.Unknown; break; case ParseState.Interval: if (!Int32.TryParse(arg, out sampleInterval)) { Usage(); return; } state = ParseState.Unknown; break; default: break; } } string pidOrProcess = args[0]; var stats = new Dictionary <int, List <ThreadSnapshot> >(); var debugger = new MDbgEngine(); int pid = -1; var processes = Process.GetProcessesByName(pidOrProcess); if (processes.Length < 1) { try { pid = Int32.Parse(pidOrProcess); } catch { Console.WriteLine("Error: could not find any processes with that name or pid"); return; } } else { if (processes.Length > 1) { Console.WriteLine("Warning: multiple processes share that name, attaching to the first"); } pid = processes[0].Id; } MDbgProcess attached = null; try { attached = debugger.Attach(pid); } catch (Exception e) { Console.WriteLine("Error: failed to attach to process: " + e); return; } attached.Go().WaitOne(); for (int i = 0; i < samples; i++) { foreach (MDbgThread thread in attached.Threads) { var snapshot = ThreadSnapshot.GetThreadSnapshot(thread); List <ThreadSnapshot> snapshots; if (!stats.TryGetValue(snapshot.Id, out snapshots)) { snapshots = new List <ThreadSnapshot>(); stats[snapshot.Id] = snapshots; } snapshots.Add(snapshot); } attached.Go(); Thread.Sleep(sampleInterval); attached.AsyncStop().WaitOne(); } attached.Detach().WaitOne(); // perform basic analysis to see which are the top N stack traces observed, // weighted on cost Dictionary <Guid, long> costs = new Dictionary <Guid, long>(); Dictionary <Guid, string> stacks = new Dictionary <Guid, string>(); foreach (var stat in stats.Values) { long prevTime = -1; foreach (var snapshot in stat) { long time = snapshot.KernelTime + snapshot.UserTime; if (prevTime != -1) { foreach (var tuple in snapshot.StackHashes) { if (costs.ContainsKey(tuple.Item1)) { costs[tuple.Item1] += time - prevTime; } else { costs[tuple.Item1] = time - prevTime; stacks[tuple.Item1] = tuple.Item2; } } } prevTime = time; } } Console.WriteLine("Most expensive stacks"); Console.WriteLine("------------------------------------"); foreach (var group in costs.OrderByDescending(p => p.Value).GroupBy(p => p.Value)) { List <string> stacksToShow = new List <string>(); foreach (var pair in group.OrderByDescending(p => stacks[p.Key].Length)) { if (!stacksToShow.Any(s => s.Contains(stacks[pair.Key]))) { stacksToShow.Add(stacks[pair.Key]); } } foreach (var stack in stacksToShow) { Console.WriteLine(stack); Console.WriteLine("===> Cost ({0})", group.Key); Console.WriteLine(); } } var offenders = stats.Values .Select(_ => ThreadSnapshotStats.FromSnapshots(_)) .OrderBy(stat => stat.TotalKernelTime + stat.TotalUserTime) .Reverse(); foreach (var stat in offenders) { Console.WriteLine("------------------------------------"); Console.WriteLine(stat.ThreadId); Console.WriteLine("Kernel: {0} User: {1}", stat.TotalKernelTime, stat.TotalUserTime); foreach (var method in stat.CommonStack) { Console.WriteLine(method); } Console.WriteLine("Other Stacks:"); var prev = new List <string>(); foreach (var trace in stats[stat.ThreadId].Select(_ => _.StackTrace)) { if (!prev.SequenceEqual(trace)) { Console.WriteLine(); foreach (var method in trace) { Console.WriteLine(method); } } else { Console.WriteLine("<skipped>"); } prev = trace; } Console.WriteLine("------------------------------------"); } }