public EventThreadView() { InitializeComponent(); ThreadToolsPanel.Visibility = Visibility.Collapsed; foreach (CallStackReason reason in Enum.GetValues(typeof(CallStackReason))) { CallstackFilter.Add(new CallstackFilterItem() { IsChecked = true, Reason = reason }); } CallstackFilterPopup.DataContext = CallstackFilter; ThreadViewControl.OnShowPopup += OnShowPopup; }
static void Main(string[] args) { List <Callstack> stacks; List <CallTreeNode> callTreeRoots; CallstackFilter filter = delegate(CallstackKey key) { // This function can be used to remove functions that you think are unrelated to the leak. // Example: // if (key.Contains("FunctionWithBalancedAddRefsAndReleases")) // return false; return(true); }; TracepointAnalysis.MaxCallstackDepth = 50; TracepointAnalysis.ReadFile(@"C:\Users\greggm\Desktop\leakdiag.txt", filter, out stacks, out callTreeRoots); TracepointAnalysis.ComputeAddRefReleaseDeltas(callTreeRoots); int totalDelta = callTreeRoots[0].Function.DeltaValue + callTreeRoots[1].Function.DeltaValue; const int expectedDelta = 2; if (totalDelta != expectedDelta) { if (totalDelta <= 0) { Console.WriteLine("AddRef/Release problem went away"); } else { Console.WriteLine("AddRef/Release problem is bigger than expected"); } return; } CallstackFunction.DumpFunctionsWithDelta(2); DumpStacks(stacks); //DumpCallTree(callTreeRoots); }
/// <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); }