private void SetCurrentSnapshot(HeapSnapshot snapshot) { // sanity checks if (snapshot == null) { return; } // remove current "Current item" SnapshotListViewItem currentItem = GetSnapshotItem(SnapshotListViewItemState.Current); if (currentItem != null) { currentItem.State = SnapshotListViewItemState.None; currentItem.ImageIndex = -1; // Note: the item must be redrawn when the image is reset to -1 lvSnapshots.RedrawItems(currentItem.Index, currentItem.Index, "force redraw" == null); } // update the new "current" item SnapshotListViewItem item = GetSnapshotItem(snapshot); item.State = SnapshotListViewItemState.Current; item.ImageIndex = CurrentImageIndex; _current = snapshot; }
private void _listener_Changed(object sender, ClipboardChangedEventArgs e) { // Note: in WinDBG, the same content is copied to the clipboard TWICE. // so we need to skip the notifications that occurs too close from // the previous one. var now = DateTime.UtcNow; if (now.Subtract(_lastClipboardNotificationTime) < MIN_ELAPSE_SINCE_CLIPBOARD_NOTIFICATION) { return; } _lastClipboardNotificationTime = now; // TODO: make this "maybe long" treatment asynchronous if needed // // Check if this is a supported format HeapSnapshot snapshot = GetSnapshotFromClipboard(); if (snapshot == null) { return; } AddOneSnapshot(snapshot); }
private void SetReferenceSnapshot(HeapSnapshot snapshot) { // sanity checks if (snapshot == null) { return; } // remove current "reference" item SnapshotListViewItem referenceItem = GetSnapshotItem(SnapshotListViewItemState.Reference); if (referenceItem != null) { referenceItem.State = SnapshotListViewItemState.None; referenceItem.ImageIndex = -1; // Note: the item must be redrawn when the image is reset to -1 lvSnapshots.RedrawItems(referenceItem.Index, referenceItem.Index, "force redraw" == null); } // update the new "reference" item SnapshotListViewItem item = GetSnapshotItem(snapshot); item.State = SnapshotListViewItemState.Reference; item.ImageIndex = ReferenceImageIndex; _reference = snapshot; }
private bool IsFilteredType(TypeEntry typeEntry) { string key = HeapSnapshot.GetKey(typeEntry); return (_filteredEntries.Any(e => (string.Compare(HeapSnapshot.GetKey(e), key) == 0))); }
public static HeapSnapshot CreateFromHeapStat(string dumpheap) { // sanity checks if (string.IsNullOrEmpty(dumpheap)) { throw InvalidFormatException; } if (!dumpheap.Contains(Header)) { throw InvalidFormatException; } DumpHeapParsingState state = DumpHeapParsingState.Init; using (TextReader reader = new StringReader(dumpheap)) { HeapSnapshot snapshot = new HeapSnapshot(); while (true) { string line = reader.ReadLine(); if (string.IsNullOrEmpty(line)) { if (state == DumpHeapParsingState.End) { return(snapshot); } throw InvalidFormatException; } switch (state) { case DumpHeapParsingState.Init: if (line.Contains(Header)) { state = DumpHeapParsingState.TypeEntries; } break; case DumpHeapParsingState.TypeEntries: state = ParseAndAddTypeEntry(line, snapshot); break; case DumpHeapParsingState.End: return(snapshot); case DumpHeapParsingState.Error: return(null); default: throw new InvalidOperationException($"Invalid parsing state {(int)state}."); } } } }
private SnapshotListViewItem GetSnapshotItem(HeapSnapshot snapshot) { foreach (SnapshotListViewItem item in lvSnapshots.Items) { if (item.Tag == snapshot) { return(item); } } return(null); }
private TypeEntryListViewItem GetItemFromEntry(ListView listview, TypeEntry entry) { foreach (TypeEntryListViewItem item in listview.Items) { TypeEntry tagEntry = (TypeEntry)item.Tag; if (string.Compare(HeapSnapshot.GetKey(tagEntry), HeapSnapshot.GetKey(entry)) == 0) { return(item); } } return(null); }
public static void DumpSnapshot(HeapSnapshot snapshot) { Debug.WriteLine("HeapSnapshot at {0}", snapshot.TimeStamp); Debug.WriteLine(" {0} objects for {1} Mb", snapshot.ObjectCount, snapshot.Size / 1024 / 1024); var entries = from e in snapshot.TypeStats.AsParallel() orderby e.Value.Name select e.Value ; foreach (var entry in entries) { Debug.WriteLine(" {0}", entry); } }
private void AddSnapshot(HeapSnapshot snapshot) { // update the list SnapshotListViewItem lvSnapshot = new SnapshotListViewItem(); lvSnapshot.Tag = snapshot; lvSnapshot.SubItems.Add(snapshot.ObjectCount.ToString("#,#")); lvSnapshot.SubItems.Add(snapshot.Size.ToString("#,#")); lvSnapshots.Items.Add(lvSnapshot); // update the chart chartCount.Series["Series2"].Points.AddY(snapshot.ObjectCount); chartSize.Series["Series1"].Points.AddY(snapshot.Size); }
private void Clear() { // empty UI lvSnapshots.Items.Clear(); lvCompare.Items.Clear(); lvFiltered.Items.Clear(); tbResult.Text = ""; chartCount.Series["Series2"].Points.Clear(); chartSize.Series["Series1"].Points.Clear(); // same for data _reference = null; _current = null; _filteredEntries.Clear(); }
private void AddOneSnapshot(HeapSnapshot snapshot) { // add the snapshot into the list AddSnapshot(snapshot); // set the reference if not already set if (_reference == null) { SetReferenceSnapshot(snapshot); } else { SetCurrentSnapshot(snapshot); CompareSnapshots(); } }
public static HeapSnapshot Compare(HeapSnapshot reference, HeapSnapshot current) { HeapSnapshot compare = new HeapSnapshot(); // loop on each entry in the current snapshot and compare with the reference foreach (var item in current.TypeStats) { // look for this class in the reference if (reference.TypeStats.ContainsKey(item.Key)) { TypeEntry referenceEntry = reference.TypeStats[item.Key]; if (referenceEntry.Count < item.Value.Count) { var entry = new TypeEntry(); entry.Name = string.Intern(referenceEntry.Name); entry.MethodTable = referenceEntry.MethodTable; entry.Count = item.Value.Count - referenceEntry.Count; entry.TotalSize = item.Value.TotalSize - referenceEntry.TotalSize; compare.TypeStats.Add(HeapSnapshot.GetKey(entry), entry); compare.ObjectCount += entry.Count; compare.Size += entry.TotalSize; } } else // don't forget the case where brand new type instances appear in current snapshot { var entry = new TypeEntry(); entry.Name = string.Intern(item.Value.Name); entry.MethodTable = item.Value.MethodTable; entry.Count = item.Value.Count; entry.TotalSize = item.Value.TotalSize; compare.TypeStats.Add(HeapSnapshot.GetKey(entry), entry); compare.ObjectCount += entry.Count; compare.Size += entry.TotalSize; } } return(compare); }
private static HeapSnapshot Compute(ClrHeap heap) { var snapshot = new HeapSnapshot(); foreach (ulong objAddress in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(objAddress); if (type == null) { continue; } var mt = type.MethodTable.ToString(); var name = type.Name; var key = HeapSnapshot.GetKey(name, mt); if (!snapshot.TypeStats.TryGetValue(key, out var entry)) { entry = new TypeEntry() { Name = name, MethodTable = mt }; snapshot.TypeStats[key] = entry; } entry.Count = entry.Count + 1; snapshot.ObjectCount = snapshot.ObjectCount + 1; var size = type.GetSize(objAddress); entry.TotalSize = entry.TotalSize + (long)size; snapshot.Size = snapshot.Size + (long)size; } return(snapshot); }
private static DumpHeapParsingState ParseAndAddTypeEntry(string line, HeapSnapshot snapshot) { if (line.StartsWith("Total")) { return(DumpHeapParsingState.End); } // MT Count TotalSize Class Name // 651d7ffc 1 12 System.Configuration.Internal.ConfigurationManagerInternal // // or in x64 // // 0x000007ff01221408 1,448 57,920 System.Xml.XmlQualifiedName // TypeEntry entry = new TypeEntry(); var pos = 0; var end = 0; string field; // 1. look for the MT end = line.IndexOf(SPACE); if (end == -1) { Debug.WriteLine("impossible to find the end of the MT field"); return(DumpHeapParsingState.Error); } field = line.Substring(pos, end - pos); entry.MethodTable = field; if (!SkipSpaces(line, ref end)) { Debug.WriteLine("impossible to find the start of the Count field"); return(DumpHeapParsingState.Error); } pos = end; // 2. look for the count end = line.IndexOf(SPACE, pos); if (end == -1) { Debug.WriteLine("impossible to find the end of the Count field"); return(DumpHeapParsingState.Error); } field = line.Substring(pos, end - pos); if (!long.TryParse(GetNumberFromString(field), out var count)) { Debug.WriteLine("invalid decimal value for the Count field"); return(DumpHeapParsingState.Error); } entry.Count = count; if (!SkipSpaces(line, ref end)) { Debug.WriteLine("impossible to find the start of the TotalSize field"); return(DumpHeapParsingState.Error); } pos = end; // 3. look for the total size end = line.IndexOf(SPACE, pos); if (end == -1) { Debug.WriteLine("impossible to find the end of the MT field"); return(DumpHeapParsingState.Error); } field = line.Substring(pos, end - pos); if (!long.TryParse(GetNumberFromString(field), out var totalSize)) { Debug.WriteLine("invalid decimal value for the TotalSize field"); return(DumpHeapParsingState.Error); } entry.TotalSize = totalSize; if (!SkipSpaces(line, ref end)) { Debug.WriteLine("impossible to find the start of the TotalSize field"); return(DumpHeapParsingState.Error); } pos = end; // 4. look for the class name field = line.Substring(pos); entry.Name = string.Intern(field); snapshot.ObjectCount += entry.Count; snapshot.Size += entry.TotalSize; string key = HeapSnapshot.GetKey(entry); if (!snapshot.TypeStats.ContainsKey(key)) { snapshot.TypeStats.Add(key, entry); } else { Debug.Fail("This should never happen"); throw new InvalidOperationException($"Impossible to find {key} for the entry"); } return(DumpHeapParsingState.TypeEntries); }
private void CompareSnapshots() { HeapSnapshot compare = HeapSnapshotFactory.Compare(_reference, _current); HeapSnapshotFactory.DumpSnapshot(compare); // if the check box is checked, don't show the System.* types bool dontShowBCLType = cbDontShowBCLTypes.Checked; // update the Raw UI StringBuilder sb = new StringBuilder(); sb.AppendFormat("HeapSnapshot at {0}\r\n", compare.TimeStamp); sb.AppendFormat(" {0} objects for {1} bytes\r\n", compare.ObjectCount, compare.Size); var entries = from item in compare.TypeStats.AsParallel() where !dontShowBCLType || !IsBclType(item.Value.Name) orderby item.Value.Name select item.Value ; foreach (var entry in entries) { sb.AppendFormat(" {0}\r\n", entry); } tbResult.Text = sb.ToString(); // update the List UI lvCompare.BeginUpdate(); lvFiltered.BeginUpdate(); lvCompare.Items.Clear(); lvFiltered.Items.Clear(); foreach (var entry in compare.TypeStats) { TypeEntryListViewItem item; // Create a wrapping ListViewItem for the filtered list if needed // Note: always present even though DontShowBCLType is checked if (IsFilteredType(entry.Value)) { AddFilteredEntry(entry.Value); } // skip BCL when needed if (dontShowBCLType) { if (IsBclType(entry.Value.Name)) { continue; } } // Create a wrapping ListViewItem for the compare list item = new TypeEntryListViewItem(""); item.Tag = entry.Value; item.SubItems.Add(entry.Value.Count.ToString("#,#")); item.SubItems.Add(entry.Value.TotalSize.ToString("#,#")); item.SubItems.Add(entry.Value.Name); lvCompare.Items.Add(item); // Create a wrapping ListViewItem for the filtered list if needed if (IsFilteredType(entry.Value)) { item.Filtered = true; item.ImageIndex = 0; } } lvCompare.Sort(); lvFiltered.Sort(); lvFiltered.EndUpdate(); lvCompare.EndUpdate(); }