/// <summary> /// Reads in a file that contains the text from the debugger's output window and analyises the tracepoint callstacks /// </summary> /// <param name="path">Path to a file containing the saved output window text</param> /// <param name="filter">Optional delegate to filter out callstacks that are uninteresting</param> /// <param name="stacks">Returns a list of all the callstacks in the output with their hit count</param> /// <param name="callTreeRoots">Returns the roots of the tracepoint call trees</param> static public void ReadFile(string path, CallstackFilter filter, out List <Callstack> stacks, out List <CallTreeNode> callTreeRoots) { int currentLine = 0; int currentRefCount = 0; Dictionary <CallstackFunction, CallTreeNode> callTreeRootMap = new Dictionary <CallstackFunction, CallTreeNode>(); Dictionary <CallstackKey, Callstack> callstackMap = new Dictionary <CallstackKey, Callstack>(); stacks = new List <Callstack>(); StreamReader inputFile = File.OpenText(path); List <string> currentCallstack = null; Regex callstackStartLineFormat = new Regex(@"[0-9]+\:\ ?\t.+(\.dll|\.exe)\!.+"); while (!inputFile.EndOfStream) { currentLine++; string line = inputFile.ReadLine(); if (currentCallstack == null) { Match match = callstackStartLineFormat.Match(line); if (match.Success && match.Index == 0) { string s = line.Substring(line.IndexOf('\t') + 1).Trim(); //Additional code to validate the debug output. Useful when figuring out why a //call stack may have gotten dropped. string refcountStr = line.Substring(0, line.IndexOf(':')); int newRefCount = int.Parse(refcountStr); // mscordbi - ignore the 'InterlockedCompareExchange frame' if (inputFile.EndOfStream) { Debugger.Break(); } currentLine++; line = inputFile.ReadLine(); s = line.Substring(line.IndexOf('\t') + 1).Trim(); // Is an AddRef frame? if (s.EndsWith("::AddRef") || s.Contains("InterlockedIncrement")) { if (newRefCount != currentRefCount + 1) { //Debugger.Break(); } currentRefCount++; } // Is a release frame? else if (s.EndsWith("::Release") || s.Contains("InterlockedDecrement")) { if (newRefCount != currentRefCount - 1) { //Debugger.Break(); } currentRefCount--; } else { Debugger.Break(); } // Create a new call stack currentCallstack = new List <string>(); currentCallstack.Add(s); currentRefCount = newRefCount; } } else { if (IsOnlyWhiteSpace(line)) { CallstackFunction[] frames = CallstackFunction.GetObjects(currentCallstack); 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 <CallTreeNode>(callTreeRootMap.Values); }