public void Visit(DeltaInfo delta) { lock (this) { if (VisitedTracebacks.Contains(delta.TracebackID)) { return; } else { VisitedTracebacks.Add(delta.TracebackID); } Allocations += delta.CountDelta.GetValueOrDefault(0); BytesRequested += delta.BytesDelta; } }
public static IEnumerator <object> FromFile(string filename, IProgressListener progress) { progress.Status = "Loading diff..."; Future <string> fText; // We could stream the lines in from the IO thread while we parse them, but this // part of the load is usually pretty quick even on a regular hard disk, and // loading the whole diff at once eliminates some context switches using (var fda = new FileDataAdapter( filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 128 )) { var fBytes = fda.ReadToEnd(); yield return(fBytes); fText = Future.RunInThread( () => Encoding.ASCII.GetString(fBytes.Result) ); yield return(fText); } yield return(fText); var lr = new LineReader(fText.Result); LineReader.Line line; progress.Status = "Parsing diff..."; var frames = new List <TracebackFrame>(); var moduleNames = new NameTable(StringComparer.Ordinal); var symbolTypes = new NameTable(StringComparer.Ordinal); var functionNames = new NameTable(StringComparer.Ordinal); var deltas = new List <DeltaInfo>(); var tracebacks = new Dictionary <UInt32, TracebackInfo>(); var regexes = new Regexes(); // Regex.Groups[string] does an inefficient lookup, so we do that lookup once here int groupModule = regexes.DiffModule.GroupNumberFromName("module"); int groupSymbolType = regexes.DiffModule.GroupNumberFromName("symbol_type"); int groupTraceId = regexes.BytesDelta.GroupNumberFromName("trace_id"); int groupType = regexes.BytesDelta.GroupNumberFromName("type"); int groupDeltaBytes = regexes.BytesDelta.GroupNumberFromName("delta_bytes"); int groupNewBytes = regexes.BytesDelta.GroupNumberFromName("new_bytes"); int groupOldBytes = regexes.BytesDelta.GroupNumberFromName("old_bytes"); int groupNewCount = regexes.BytesDelta.GroupNumberFromName("new_count"); int groupOldCount = regexes.CountDelta.GroupNumberFromName("old_count"); int groupCountDelta = regexes.CountDelta.GroupNumberFromName("delta_count"); int groupTracebackModule = regexes.TracebackFrame.GroupNumberFromName("module"); int groupTracebackFunction = regexes.TracebackFrame.GroupNumberFromName("function"); int groupTracebackOffset = regexes.TracebackFrame.GroupNumberFromName("offset"); int groupTracebackOffset2 = regexes.TracebackFrame.GroupNumberFromName("offset2"); int groupTracebackPath = regexes.TracebackFrame.GroupNumberFromName("path"); int groupTracebackLine = regexes.TracebackFrame.GroupNumberFromName("line"); var delay = new Sleep(0.01); int i = 0; while (lr.ReadLine(out line)) { retryFromHere: i += 1; if (i % ProgressInterval == 0) { progress.Maximum = lr.Length; progress.Progress = lr.Position; // Suspend processing for a bit yield return(delay); } Match m; if (regexes.DiffModule.TryMatch(ref line, out m)) { moduleNames.Add(m.Groups[groupModule].Value); } else if (regexes.BytesDelta.TryMatch(ref line, out m)) { var added = (m.Groups[groupType].Value == "+"); var traceId = UInt32.Parse(m.Groups[groupTraceId].Value, NumberStyles.HexNumber); var info = new DeltaInfo { BytesDelta = int.Parse(m.Groups[groupDeltaBytes].Value, NumberStyles.HexNumber) * (added ? 1 : -1), NewBytes = int.Parse(m.Groups[groupNewBytes].Value, NumberStyles.HexNumber), OldBytes = int.Parse(m.Groups[groupOldBytes].Value, NumberStyles.HexNumber), NewCount = int.Parse(m.Groups[groupNewCount].Value, NumberStyles.HexNumber), }; if (lr.ReadLine(out line)) { if (regexes.CountDelta.TryMatch(ref line, out m)) { info.OldCount = int.Parse(m.Groups[groupOldCount].Value, NumberStyles.HexNumber); info.CountDelta = int.Parse(m.Groups[groupCountDelta].Value, NumberStyles.HexNumber) * (added ? 1 : -1); } } bool readingLeadingWhitespace = true, doRetry = false; frames.Clear(); var itemModules = new NameTable(StringComparer.Ordinal); var itemFunctions = new NameTable(StringComparer.Ordinal); while (lr.ReadLine(out line)) { if (line.ToString().Trim().Length == 0) { if (readingLeadingWhitespace) { continue; } else { break; } } else if (regexes.TracebackFrame.TryMatch(ref line, out m)) { readingLeadingWhitespace = false; var moduleName = moduleNames[m.Groups[groupTracebackModule].Value]; itemModules.Add(moduleName); var functionName = functionNames[m.Groups[groupTracebackFunction].Value]; itemFunctions.Add(functionName); var frame = new TracebackFrame { Module = moduleName, Function = functionName, Offset = UInt32.Parse(m.Groups[groupTracebackOffset].Value, NumberStyles.HexNumber) }; if (m.Groups[groupTracebackOffset2].Success) { frame.Offset2 = UInt32.Parse(m.Groups[groupTracebackOffset2].Value, NumberStyles.HexNumber); } if (m.Groups[groupTracebackPath].Success) { frame.SourceFile = m.Groups[groupTracebackPath].Value; } if (m.Groups[groupTracebackLine].Success) { frame.SourceLine = int.Parse(m.Groups[groupTracebackLine].Value); } frames.Add(frame); } else { // We hit the beginning of a new allocation, so make sure it gets parsed doRetry = true; break; } } if (tracebacks.ContainsKey(traceId)) { info.Traceback = tracebacks[traceId]; Program.ErrorList.ReportError("Duplicate traceback for id {0}!", traceId); } else { var frameArray = ImmutableArrayPool <TracebackFrame> .Allocate(frames.Count); frames.CopyTo(frameArray.Array, frameArray.Offset); info.Traceback = tracebacks[traceId] = new TracebackInfo { TraceId = traceId, Frames = frameArray, Modules = itemModules, Functions = itemFunctions }; } deltas.Add(info); if (doRetry) { goto retryFromHere; } } else if (line.StartsWith("//")) { // Comment, ignore it } else if (line.StartsWith("Total increase") || line.StartsWith("Total decrease")) { // Ignore this too } else if (line.StartsWith(" ") && (line.EndsWith(".pdb"))) { // Symbol path for a module, ignore it } else { Program.ErrorList.ReportError("Unrecognized diff content: {0}", line.ToString()); } } var result = new HeapDiff( filename, moduleNames, functionNames, deltas, tracebacks ); yield return(new Result(result)); }
public DeltaInfoTooltipContent(DeltaInfo delta, DeltaInfo.RenderParams renderParams) { Delta = delta; RenderParams = renderParams; }
public float Render(Graphics g, ref DeltaInfo.RenderParams rp, string headerText) { var lineHeight = g.MeasureString( "AaBbYyZz", rp.Font, rp.ContentRegion.Width, rp.StringFormat ).Height; g.ResetClip(); g.FillRectangle(rp.ShadeBrush, 0, rp.ContentRegion.Y, rp.ContentRegion.Width, lineHeight - 1); var y = 0.0f; g.DrawString(headerText, rp.Font, rp.TextBrush, 0.0f, rp.ContentRegion.Top + y, rp.StringFormat); y += g.MeasureString(headerText, rp.Font, rp.ContentRegion.Width, rp.StringFormat).Height; int f = 0; for (int i = 0, c = Frames.Count, o = Frames.Offset; i < c; i++) { var frame = Frames.Array[i + o]; var text = frame.ToString(); var layoutRect = new RectangleF( 0.0f, rp.ContentRegion.Top + y, rp.ContentRegion.Width, lineHeight ); Region[] fillRegions = null; g.ResetClip(); Match m; if ((rp.FunctionFilter != null) && (frame.Function != null) && rp.FunctionFilter.TryMatch(frame.Function, out m)) { var startIndex = m.Index + text.IndexOf(frame.Function); var endIndex = startIndex + m.Length; if ((endIndex > startIndex) && (startIndex >= 0)) { rp.StringFormat.SetMeasurableCharacterRanges(new[] { new CharacterRange(startIndex, endIndex - startIndex) }); fillRegions = g.MeasureCharacterRanges( text, rp.Font, layoutRect, rp.StringFormat ); foreach (var fillRegion in fillRegions) { g.SetClip(fillRegion, System.Drawing.Drawing2D.CombineMode.Replace); g.FillRegion(rp.FunctionHighlightBrush, fillRegion); g.DrawString(text, rp.Font, rp.FunctionHighlightTextBrush, layoutRect, rp.StringFormat); } g.ResetClip(); foreach (var fillRegion in fillRegions) { g.ExcludeClip(fillRegion); } } } g.DrawString(text, rp.Font, rp.TextBrush, layoutRect, rp.StringFormat); y += g.MeasureString(text, rp.Font, rp.ContentRegion.Width, rp.StringFormat).Height; f += 1; if ((f == 2) && !rp.IsExpanded) { if (Frames.Count > 2) { rp.StringFormat.Alignment = StringAlignment.Far; var elideString = String.Format( "(+{0} frame(s))", Frames.Count - 2 ); rp.StringFormat.SetMeasurableCharacterRanges( new[] { new CharacterRange(0, elideString.Length) } ); var regions = g.MeasureCharacterRanges(elideString, rp.Font, layoutRect, rp.StringFormat); foreach (var region in regions) g.FillRegion(rp.ElideBackgroundBrush, region); g.DrawString(elideString, rp.Font, rp.ElideTextBrush, layoutRect, rp.StringFormat); rp.StringFormat.Alignment = StringAlignment.Near; } break; } } return y; }
public static IEnumerator<object> FromFile(string filename, IProgressListener progress) { progress.Status = "Loading diff..."; Future<string> fText; // We could stream the lines in from the IO thread while we parse them, but this // part of the load is usually pretty quick even on a regular hard disk, and // loading the whole diff at once eliminates some context switches using (var fda = new FileDataAdapter( filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 128 )) { var fBytes = fda.ReadToEnd(); yield return fBytes; fText = Future.RunInThread( () => Encoding.ASCII.GetString(fBytes.Result) ); yield return fText; } yield return fText; var lr = new LineReader(fText.Result); LineReader.Line line; progress.Status = "Parsing diff..."; var frames = new List<TracebackFrame>(); var moduleNames = new NameTable(StringComparer.Ordinal); var symbolTypes = new NameTable(StringComparer.Ordinal); var functionNames = new NameTable(StringComparer.Ordinal); var deltas = new List<DeltaInfo>(); var tracebacks = new Dictionary<UInt32, TracebackInfo>(); var regexes = new Regexes(); // Regex.Groups[string] does an inefficient lookup, so we do that lookup once here int groupModule = regexes.DiffModule.GroupNumberFromName("module"); int groupSymbolType = regexes.DiffModule.GroupNumberFromName("symbol_type"); int groupTraceId = regexes.BytesDelta.GroupNumberFromName("trace_id"); int groupType = regexes.BytesDelta.GroupNumberFromName("type"); int groupDeltaBytes = regexes.BytesDelta.GroupNumberFromName("delta_bytes"); int groupNewBytes = regexes.BytesDelta.GroupNumberFromName("new_bytes"); int groupOldBytes = regexes.BytesDelta.GroupNumberFromName("old_bytes"); int groupNewCount = regexes.BytesDelta.GroupNumberFromName("new_count"); int groupOldCount = regexes.CountDelta.GroupNumberFromName("old_count"); int groupCountDelta = regexes.CountDelta.GroupNumberFromName("delta_count"); int groupTracebackModule = regexes.TracebackFrame.GroupNumberFromName("module"); int groupTracebackFunction = regexes.TracebackFrame.GroupNumberFromName("function"); int groupTracebackOffset = regexes.TracebackFrame.GroupNumberFromName("offset"); int groupTracebackOffset2 = regexes.TracebackFrame.GroupNumberFromName("offset2"); int groupTracebackPath = regexes.TracebackFrame.GroupNumberFromName("path"); int groupTracebackLine = regexes.TracebackFrame.GroupNumberFromName("line"); int i = 0; while (lr.ReadLine(out line)) { if (i % ProgressInterval == 0) { progress.Maximum = lr.Length; progress.Progress = lr.Position; // Suspend processing until any messages in the windows message queue have been processed yield return new Yield(); } retryFromHere: Match m; if (regexes.DiffModule.TryMatch(ref line, out m)) { moduleNames.Add(m.Groups[groupModule].Value); } else if (regexes.BytesDelta.TryMatch(ref line, out m)) { var traceId = UInt32.Parse(m.Groups[groupTraceId].Value, NumberStyles.HexNumber); var info = new DeltaInfo { Added = (m.Groups[groupType].Value == "+"), BytesDelta = int.Parse(m.Groups[groupDeltaBytes].Value, NumberStyles.HexNumber), NewBytes = int.Parse(m.Groups[groupNewBytes].Value, NumberStyles.HexNumber), OldBytes = int.Parse(m.Groups[groupOldBytes].Value, NumberStyles.HexNumber), NewCount = int.Parse(m.Groups[groupNewCount].Value, NumberStyles.HexNumber), }; if (lr.ReadLine(out line)) { if (regexes.CountDelta.TryMatch(ref line, out m)) { info.OldCount = int.Parse(m.Groups[groupOldCount].Value, NumberStyles.HexNumber); info.CountDelta = int.Parse(m.Groups[groupCountDelta].Value, NumberStyles.HexNumber); } } bool readingLeadingWhitespace = true, doRetry = false; frames.Clear(); var itemModules = new NameTable(StringComparer.Ordinal); var itemFunctions = new NameTable(StringComparer.Ordinal); while (lr.ReadLine(out line)) { if (line.ToString().Trim().Length == 0) { if (readingLeadingWhitespace) continue; else break; } else if (regexes.TracebackFrame.TryMatch(ref line, out m)) { readingLeadingWhitespace = false; var moduleName = moduleNames[m.Groups[groupTracebackModule].Value]; itemModules.Add(moduleName); var functionName = functionNames[m.Groups[groupTracebackFunction].Value]; itemFunctions.Add(functionName); var frame = new TracebackFrame { Module = moduleName, Function = functionName, Offset = UInt32.Parse(m.Groups[groupTracebackOffset].Value, NumberStyles.HexNumber) }; if (m.Groups[groupTracebackOffset2].Success) frame.Offset2 = UInt32.Parse(m.Groups[groupTracebackOffset2].Value, NumberStyles.HexNumber); if (m.Groups[groupTracebackPath].Success) frame.SourceFile = m.Groups[groupTracebackPath].Value; if (m.Groups[groupTracebackLine].Success) frame.SourceLine = int.Parse(m.Groups[groupTracebackLine].Value); frames.Add(frame); } else { // We hit the beginning of a new allocation, so make sure it gets parsed doRetry = true; break; } } if (tracebacks.ContainsKey(traceId)) { info.Traceback = tracebacks[traceId]; Console.WriteLine("Duplicate traceback for id {0}!", traceId); } else { var frameArray = ImmutableArrayPool<TracebackFrame>.Allocate(frames.Count); frames.CopyTo(frameArray.Array, frameArray.Offset); info.Traceback = tracebacks[traceId] = new TracebackInfo { TraceId = traceId, Frames = frameArray, Modules = itemModules, Functions = itemFunctions }; } deltas.Add(info); if (doRetry) goto retryFromHere; } else if (line.StartsWith("//")) { // Comment, ignore it } else if (line.StartsWith("Total increase") || line.StartsWith("Total decrease")) { // Ignore this too } else if (line.StartsWith(" ") && (line.EndsWith(".pdb"))) { // Symbol path for a module, ignore it } else { Console.WriteLine("Unrecognized diff content: {0}", line.ToString()); } } var result = new HeapDiff( filename, moduleNames, functionNames, deltas, tracebacks ); yield return new Result(result); }