using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Text.RegularExpressions; static class TracepointAnalysis { /// /// Reads in a file that contains the text from the debugger's output window and analyises the tracepoint callstacks /// /// Path to a file containing the saved output window text /// Optional delegate to filter out callstacks that are uninteresting /// Returns a list of all the callstacks in the output with their hit count /// Returns the roots of the tracepoint call trees static public void ReadFile(string path, CallstackFilter filter, out List stacks, out List callTreeRoots) { Dictionary callTreeRootMap = new Dictionary(); Dictionary callstackMap = new Dictionary(); stacks = new List(); StreamReader inputFile = File.OpenText(path); List currentCallstack = null; Regex callstackStartLineFormat = new Regex(@"CSTACK: \t.+\!.+\(.*\).*"); while (!inputFile.EndOfStream) { string line = inputFile.ReadLine(); if (currentCallstack == null) { Match match = callstackStartLineFormat.Match(line); if (match.Success && match.Index == 0) { currentCallstack = new List(); string s = line.Substring(line.IndexOf('\t') + 1); currentCallstack.Add(s); } } else { if (string.IsNullOrEmpty(line) || (line.Length == 1 && line[0] == '\t')) { string[] frames = currentCallstack.ToArray(); CallstackKey key = new CallstackKey(frames); if (filter == null || filter(key)) { Callstack.AddCallstack(stacks, callstackMap, key); CallTreeNode.AddCallstack(callTreeRootMap, key); } currentCallstack = null; } else if (line.Length > 1 && line[0] == '\t') { string s = line.Substring(1); currentCallstack.Add(s); } } } callTreeRoots = new List(callTreeRootMap.Values); } } /// /// Allows filtering the tracepoint callstacks that are interesting. /// /// Callstack that was read from the input file /// true if the callstack should be included in the stack database / call tree roots delegate bool CallstackFilter(CallstackKey stack); class CallstackKey : IEquatable, IComparable { public readonly string[] Frames; int m_hashCode; internal CallstackKey(string[] frames) { this.Frames = frames; // Using the default string array implementation of GetHashCode doesn't produce the desired effect foreach (string line in this.Frames) { m_hashCode ^= line.GetHashCode(); } } public override int GetHashCode() { return m_hashCode; } public int CompareTo(CallstackKey other) { int result = this.Frames.Length - other.Frames.Length; if (result != 0) return result; for (int i = 0; i < this.Frames.Length; i++) { result = string.CompareOrdinal(this.Frames[i], other.Frames[i]); if (result != 0) return result; } return 0; } // The default dictionary comparison will call this public bool Equals(CallstackKey other) { return (CompareTo(other) == 0); } public int IndexOf(string function) { int index = 0; foreach (string f in this.Frames) { if (f.Contains(function)) return index; index++; } return -1; } public bool Contains(string function) { return (IndexOf(function) >= 0); } }; class Callstack : CallstackKey { private int m_hitCount; public int HitCount { get { return m_hitCount; } } public Callstack(string[] frames) : base(frames) { m_hitCount = 1; } internal static Callstack AddCallstack(List stacks, Dictionary callstackMap, CallstackKey key) { Callstack callstackObj; if (callstackMap.TryGetValue(key, out callstackObj)) { callstackObj.m_hitCount++; } else { callstackObj = new Callstack(key.Frames); callstackMap.Add(callstackObj, callstackObj); stacks.Add(callstackObj); } return callstackObj; } public void Dump() { Console.WriteLine("Count = {0}", this.HitCount); foreach (string s in this.Frames) { Console.WriteLine(s); } Console.WriteLine(); } }; class CallTreeNode { public readonly string Function; public readonly Dictionary Children; private int m_hitCount; public int HitCount { get { return m_hitCount; } } private CallTreeNode(string function) { this.Function = function; this.Children = new Dictionary(); this.m_hitCount = 1; } static public CallTreeNode AddNode(Dictionary dict, string function) { CallTreeNode node; if (dict.TryGetValue(function, out node)) { node.m_hitCount++; return node; } else { node = new CallTreeNode(function); dict.Add(function, node); return node; } } public void Dump(int indent) { for (int i = 0; i < indent; i++) Console.Write(" "); Console.WriteLine("{0} ({1})", this.Function, this.m_hitCount); foreach (CallTreeNode child in this.Children.Values) { child.Dump(indent + 1); } } internal static void AddCallstack(Dictionary callTreeRootMap, CallstackKey key) { Dictionary map = callTreeRootMap; foreach (string function in key.Frames) { CallTreeNode node = AddNode(map, function); map = node.Children; } } }