public static object GetSimpleValue(ulong objAddress, ClrType clrType, bool isInterior = false) { if (objAddress == 0) { throw new NullReferenceException("ClrObject at is pointing to null address."); } ClrHeap heap = clrType.Heap; if (clrType.IsEnum) { var val = clrType.GetValue(objAddress); return(clrType.GetEnumName(val)); } if (clrType.IsPrimitive || clrType.IsString) { return(clrType.GetValue(objAddress)); } ulong address = isInterior ? objAddress : objAddress + (ulong)heap.PointerSize; switch (clrType.Name) { case GuidTypeName: { byte[] buffer = ReadBuffer(heap, address, 16); return(new Guid(buffer)); } case TimeSpanTypeName: { byte[] buffer = ReadBuffer(heap, address, 8); long ticks = BitConverter.ToInt64(buffer, 0); return(new TimeSpan(ticks)); } case DateTimeTypeName: { byte[] buffer = ReadBuffer(heap, address, 8); ulong dateData = BitConverter.ToUInt64(buffer, 0); return(GetDateTime(dateData)); } case IPAddressTypeName: { return(GetIPAddress(new ClrObject(objAddress, clrType, isInterior))); } } throw new InvalidOperationException(string.Format("SimpleValue not available for type '{0}'", clrType.Name)); }
public override string ToString() { if (IsNull()) { return("<null>"); } if (m_type.IsString) { return(m_type.GetValue(m_addr).ToString()); } return(string.Format(@"[{0:x16} {1}]", m_addr, m_type.Name)); }
public override bool TryConvert(ConvertBinder binder, out object result) { if (m_type == null) { return(ClrNullValue.GetDefaultNullValue(binder.Type, out result)); } if (binder.Type == typeof(ClrType)) { // Special case: The caller is requesting the GCHeapType of what we are inspecting. result = m_type; return(true); } if (binder.Type.IsPrimitive && m_type.IsPrimitive && m_type.HasSimpleValue) { result = Convert.ChangeType(m_type.GetValue(m_addr), binder.Type); return(true); } if (binder.Type == typeof(ulong)) { result = m_addr; return(true); } if (binder.Type == typeof(long)) { result = (long)m_addr; return(true); } if (binder.Type == typeof(string)) { if (m_type.IsString) { result = m_type.GetValue(m_addr); } else { result = ToString(); } return(true); } result = null; return(false); }
public static object GetPrimitiveValueNonBoxed(this ClrType type, ulong address) { // The ClrType.GetValue implementation assumes that if a primitive // is passed in, then it must be boxed, and adds 4/8 bytes to the address // specified. We compensate by subtracting 4/8 bytes as necessary. return(type.GetValue(address - (ulong)IntPtr.Size)); }
public object Read() { ulong address = Address; // This is required due to a bug in Microsoft.Diagnostics.Runtime if (Type.IsPrimitive) { address -= (ulong)((long)this.Heap.PointerSize); } return(Type.GetValue(address)); }
public static void AddSimpleValueColumn(this ObjectListView listView, Func <object, ulong> addressGetter, ClrDump dump, Func <object, ClrType> typeGetter) { var col = new OLVColumn("Value", null) { Width = 150 }; col.AspectGetter = o => { ClrType type = typeGetter(o); ulong address = addressGetter(o); object result = dump.Eval( () => { if (type.IsPrimitive || type.IsString) { return(type.GetValue(address)); } return(address); } ); return(result); }; listView.AllColumns.Add(col); var menuItem = new ToolStripMenuItem("Copy Value"); listView.ContextMenuStrip.Items.Add(menuItem); menuItem.Click += (o, e) => { if (listView.SelectedItem == null) { return; } int index = listView.SelectedItem.Index; var modelObject = listView.GetModelObject(index); string val = col.GetStringValue(modelObject); string escapeVal = StringHelpers.Escape(val); if (string.IsNullOrEmpty(escapeVal)) { Clipboard.SetText("null"); } else { Clipboard.SetText(escapeVal); } }; }
public static object GetSimpleValue(ClrObject obj) { if (obj.IsNull()) { return(null); } ClrType type = obj.Type; ClrHeap heap = type.Heap; if (type.IsPrimitive || type.IsString) { return(type.GetValue(obj.Address)); } ulong address = obj.IsInterior ? obj.Address : obj.Address + (ulong)heap.PointerSize; switch (type.Name) { case GuidTypeName: { byte[] buffer = ReadBuffer(heap, address, 16); return(new Guid(buffer)); } case TimeSpanTypeName: { byte[] buffer = ReadBuffer(heap, address, 8); long ticks = BitConverter.ToInt64(buffer, 0); return(new TimeSpan(ticks)); } case DateTimeTypeName: { byte[] buffer = ReadBuffer(heap, address, 8); ulong dateData = BitConverter.ToUInt64(buffer, 0); return(GetDateTime(dateData)); } case IPAddressTypeName: { return(GetIPAddress(obj)); } } throw new InvalidOperationException(string.Format("SimpleValue not available for type '{0}'", type.Name)); }
private IClrObject ReadFromNotNullAddress(ClrType type, ulong address) { if (type.IsArray) { return(new ClrArrayObject(type, address)); } if (type.IsValueClass) { return(new ClrStructObject(null, type, address)); } if (type.IsPrimitive || type.IsString) { var value = type.GetValue(address); return(WrapPrimitiveValue(type, value)); } return(new ClrClassObject(type, address)); }
public string ValueRaw() { if (ClrType != null) { // NOTE ObjectAddress could be 0 while the value is simply unavailable. // Display strings inline. if (ClrType.IsString && Value != null) { return(ClrType.GetValue(ObjectAddress).ToStringOrNull()); } // Display objects (non-strings) as an address. if (ClrType.IsObjectReference && Value != null) { return(String.Format("{0:x16}", ObjectAddress)); } if (HasNonTrivialValueToDisplay) { return("VALTYPE"); } // Display primitive value types inline. if (ClrType.HasSimpleValue && Location != 0) { return(ClrType.GetPrimitiveValueNonBoxed(Location).ToStringOrNull()); } } if (Value == null || Value.Length == 0) { return("<unavailable>"); } StringBuilder result = new StringBuilder(); foreach (byte b in Value) { result.AppendFormat("{0:x2} ", b); } return(result.ToString()); }
static (int length, string text) ReadString(ClrType type, ulong obj) { ClrInstanceField lengthField = type.GetFieldByName("_stringLength"); if (lengthField == null) { return(0, String.Empty); } var stringLength = (int)lengthField.GetValue(obj); if (stringLength <= 0) { return(stringLength, String.Empty); } var text = (string)type.GetValue(obj); return(stringLength, text); }
public static long GetEnumValue(ulong addr, ClrType clrType, ClrElementType enumElem) { object enumVal = clrType.GetValue(addr); if (enumVal is int) { return((long)(int)enumVal); } else { // The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong. switch (enumElem) { case ClrElementType.UInt32: return((long)((uint)enumVal)); case ClrElementType.UInt8: return((long)(byte)enumVal); case ClrElementType.Int8: return((long)(sbyte)enumVal); case ClrElementType.Int16: return((long)(short)enumVal); case ClrElementType.UInt16: return((long)(ushort)enumVal); case ClrElementType.Int64: return((long)enumVal); case ClrElementType.UInt64: return((long)(ulong)enumVal); default: return(EnumValues.InvalidEnumValue); } } }
static void ReadEnum(ClrType type, ulong obj) { int objValue = (int)type.GetValue(obj); bool found = false; foreach (var name in type.GetEnumNames()) { int value; if (type.TryGetEnumValue(name, out value) && objValue == value) { Console.WriteLine("{0} - {1}", value, name); found = true; break; } } if (!found) { Console.WriteLine("{0} - {1}", objValue, "Unknown"); } }
public void IntegerObjectClrType() { using (DataTarget dt = TestTargets.Types.LoadFullDump()) { ClrRuntime runtime = dt.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrStaticField field = runtime.GetModule("types.exe").GetTypeByName("Types").GetStaticFieldByName("s_i"); ulong addr = (ulong)field.GetValue(runtime.AppDomains.Single()); ClrType type = heap.GetObjectType(addr); Assert.True(type.IsPrimitive); Assert.False(type.IsObjectReference); Assert.False(type.IsValueClass); object value = type.GetValue(addr); Assert.Equal("42", value.ToString()); Assert.IsType <int>(value); Assert.Equal(42, (int)value); Assert.Contains(addr, heap.EnumerateObjectAddresses()); } }
private static void ExamineProcessHeap(ClrRuntime runtime, ClrHeap heap, bool showGcHeapInfo) { if (!heap.CanWalkHeap) { Console.WriteLine("Cannot walk the heap!"); return; } ulong totalStringObjectSize = 0, stringObjectCounter = 0, byteArraySize = 0; ulong asciiStringSize = 0, unicodeStringSize = 0, isoStringSize = 0; //, utf8StringSize = 0; ulong asciiStringCount = 0, unicodeStringCount = 0, isoStringCount = 0; //, utf8StringCount = 0; ulong compressedStringSize = 0, uncompressedStringSize = 0; foreach (var obj in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(obj); // If heap corruption, continue past this object. Or if it's NOT a String we also ignore it if (type == null || type.IsString == false) { continue; } stringObjectCounter++; var text = (string)type.GetValue(obj); var rawBytes = Encoding.Unicode.GetBytes(text); totalStringObjectSize += type.GetSize(obj); byteArraySize += (ulong)rawBytes.Length; VerifyStringObjectSize(runtime, type, obj, text); // Try each encoding in order, so we find the most-compact encoding that the text would fit in byte[] textAsBytes = null; if (IsASCII(text, out textAsBytes)) { asciiStringSize += (ulong)rawBytes.Length; asciiStringCount++; // ASCII is compressed as ISO-8859-1 (Latin-1) NOT ASCII if (IsIsoLatin1(text, out textAsBytes)) { compressedStringSize += (ulong)textAsBytes.Length; } else { Console.WriteLine("ERROR: \"{0}\" is ASCII but can't be encoded as ISO-8859-1 (Latin-1)", text); } } // From http://stackoverflow.com/questions/7048745/what-is-the-difference-between-utf-8-and-iso-8859-1 // "ISO 8859-1 is a single-byte encoding that can represent the first 256 Unicode characters" else if (IsIsoLatin1(text, out textAsBytes)) { isoStringSize += (ulong)rawBytes.Length; isoStringCount++; compressedStringSize += (ulong)textAsBytes.Length; } // UTF-8 and UTF-16 can both support the same range of text/character values ("Code Points"), they just store it in different ways // From http://stackoverflow.com/questions/4655250/difference-between-utf-8-and-utf-16/4655335#4655335 // "Both UTF-8 and UTF-16 are variable length (multi-byte) encodings. // However, in UTF-8 a character may occupy a minimum of 8 bits, while in UTF-16 character length starts with 16 bits." //else if (IsUTF8(text, out textAsBytes)) //{ // utf8StringSize += (ulong)rawBytes.Length; // utf8StringCount++; // compressedStringSize += (ulong)textAsBytes.Length; //} else { unicodeStringSize += (ulong)rawBytes.Length; unicodeStringCount++; uncompressedStringSize += (ulong)rawBytes.Length; } } Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("\n\"System.String\" memory usage info"); Console.ResetColor(); Console.WriteLine("Overall {0:N0} \"System.String\" objects take up {1:N0} bytes ({2:N2} MB)", stringObjectCounter, totalStringObjectSize, totalStringObjectSize / 1024.0 / 1024.0); Console.WriteLine("Of this underlying byte arrays (as Unicode) take up {0:N0} bytes ({1:N2} MB)", byteArraySize, byteArraySize / 1024.0 / 1024.0); Console.WriteLine("Remaining data (object headers, other fields, etc) are {0:N0} bytes ({1:N2} MB), at {2:0.##} bytes per object\n", totalStringObjectSize - byteArraySize, (totalStringObjectSize - byteArraySize) / 1024.0 / 1024.0, (totalStringObjectSize - byteArraySize) / (double)stringObjectCounter); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Actual Encoding that the \"System.String\" could be stored as (with corresponding data size)"); Console.ResetColor(); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as ASCII", asciiStringSize, asciiStringCount); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as ISO-8859-1 (Latin-1)", isoStringSize, isoStringCount); //Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) are UTF-8", utf8StringSize, utf8StringCount); Console.WriteLine(" {0,15:N0} bytes ({1,8:N0} strings) as Unicode", unicodeStringSize, unicodeStringCount); Console.WriteLine("Total: {0:N0} bytes (expected: {1:N0}{2})\n", asciiStringSize + isoStringSize + unicodeStringSize, byteArraySize, (asciiStringSize + isoStringSize + unicodeStringSize != byteArraySize) ? " - ERROR" : ""); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Compression Summary:"); Console.ResetColor(); Console.WriteLine(" {0,15:N0} bytes Compressed (to ISO-8859-1 (Latin-1))", compressedStringSize); Console.WriteLine(" {0,15:N0} bytes Uncompressed (as Unicode)", uncompressedStringSize); Console.WriteLine(" {0,15:N0} bytes EXTRA to enable compression (1-byte field, per \"System.String\" object)", stringObjectCounter); var totalBytesUsed = compressedStringSize + uncompressedStringSize + stringObjectCounter; var totalBytesSaved = byteArraySize - totalBytesUsed; Console.WriteLine("\nTotal Usage: {0:N0} bytes ({1:N2} MB), compared to {2:N0} ({3:N2} MB) before compression", totalBytesUsed, totalBytesUsed / 1024.0 / 1024.0, byteArraySize, byteArraySize / 1024.0 / 1024.0); Console.WriteLine("Total Saving: {0:N0} bytes ({1:N2} MB)\n", totalBytesSaved, totalBytesSaved / 1024.0 / 1024.0); }
public override object GetValue(Address objRef, bool interior) { return(Type.GetValue(GetAddress(objRef, interior))); }
private List <ClrObjectModel> GetValues(ulong obj, ClrType type, string baseName, int offset, bool inner, List <ClrObjectModel> values) { if (type.Name == "System.String") { object value; try { value = type.GetValue(obj); } catch (Exception ex) { value = ex.Message; } values.Add(new ClrObjectModel { Address = obj, BaseName = baseName, TypeName = type.Name, Value = value }); values.AddRange(type.Fields.Select(field => new ClrObjectModel { Address = obj, BaseName = baseName, FieldName = field.Name, Offset = field.Offset + offset, TypeName = field.Type.Name, Value = field.GetValue(obj, inner).ToString() })); } else if (type.IsArray) { int len = type.GetArrayLength(obj); if (type.ComponentType == null) { try { for (int i = 0; i < len; i++) { values.Add(new ClrObjectModel { Address = obj, BaseName = baseName, TypeName = type.Name, Value = type.GetArrayElementValue(obj, i) }); } } catch { } } else if (type.ComponentType.HasSimpleValue) { for (int i = 0; i < len; i++) { values.Add(new ClrObjectModel { Address = obj, BaseName = baseName, TypeName = type.Name, Value = type.GetArrayElementValue(obj, i) }); } } else { for (int i = 0; i < len; i++) { ulong arrAddress = type.GetArrayElementAddress(obj, i); foreach (var field in type.ComponentType.Fields) { string value; if (field.HasSimpleValue) { value = field.GetValue(arrAddress, inner).ToString(); // an embedded struct } else { value = field.GetAddress(arrAddress, inner).ToString(); } values.Add(new ClrObjectModel { Address = obj, BaseName = baseName, FieldName = field.Name, Offset = field.Offset + offset, TypeName = field.Type.Name, Value = value }); if (field.ElementType == ClrElementType.Struct) { values.AddRange(GetValues(arrAddress, field.Type, baseName + field.Name, offset + field.Offset, true, new List <ClrObjectModel>())); } } } } } else { foreach (var field in type.Fields) { ulong addr = field.GetAddress(obj, inner); object value; if (field.HasSimpleValue) { try { value = field.GetValue(obj, inner); } catch (Exception) { value = "{Unknown}"; } } else { value = addr; } string sValue = value?.ToString() ?? "{Null}"; values.Add(new ClrObjectModel { Address = obj, BaseName = baseName, FieldName = field.Name, Offset = field.Offset + offset, TypeName = field.Type.Name, Value = sValue }); if (field.ElementType == ClrElementType.Struct) { values.AddRange(GetValues(addr, field.Type, baseName + field.Name, offset + field.Offset, true, new List <ClrObjectModel>())); } } } return(values); }
private void DumpFields(ulong Address, ClrType type = null) { StartRuntime(); ClrType obj; if (type == null) { obj = m_heap.GetObjectType(Address); } else { obj = type; } if (cache == null) { cache = new HeapCache(m_runtime, ShowProgress); } MDType tp = new MDType(obj); MD_TypeData data; tp.GetHeader(Address, out data); int count = 0; tp.GetAllFieldsDataRawCount(out count); int temp = 0; MD_FieldData[] fields = new MD_FieldData[count]; tp.GetAllFieldsDataRaw(data.isValueType ? 1 : 0, count, fields, out temp); for (int i = 0; i < count; i++) { string typeName; string Name; tp.GetRawFieldTypeAndName(i, out typeName, out Name); MD_TypeData fd; ClrType ftp = AdHoc.GetTypeFromMT(m_runtime, fields[i].MethodTable); MDType ft = new MDType(ftp); ulong pointer = 0; tp.GetRawFieldAddress(Address, data.isValueType ? 1 : 0, i, out pointer); if (fields[i].isValueType) { ft.GetHeader(pointer, out fd); } else { ft.GetHeader(ReadPointer(pointer), out fd); } Write("{0:x16} {1:x4} {5:x16} {6} +{2:x4} {3,30} {4,30} {7} ", fd.module, fd.token, fields[i].offset, TrimRight(typeName, 30), Name, data.MethodTable, fields[i].isThreadStatic ? " thread " : fields[i].isStatic ? " Static " : " ", fields[i].isEnum ? AdHoc.GetEnumName(ftp, ReadPointer(pointer) & (fd.size == 4 ? 0x0000FFFF : ulong.MaxValue)) : "" /*cache.GetFieldValue(Address, Name, obj)*/); ulong effAddress = pointer; if (fd.isValueType) { if (fields[i].isEnum) { WriteLine(""); continue; } try { WriteLine("{0}", ftp.GetValue(pointer)); } catch { WriteLine("{0}", ReadPointer(pointer) & (fd.size == 4 ? 0xFFFFFFFF : fd.size == 8 ? 0x00000000FFFFFFFF : ulong.MaxValue)); } continue; } else { if (pointer != 0) { effAddress = ReadPointer(pointer); } Write("({0:x16}) ", effAddress); if (effAddress == 0) { WriteLine(""); continue; } if (fd.isString) { string str; tp.GetString(effAddress, out str); Write("{0}", str); } WriteLine(""); } } /* * var fields = * from f in obj.Fields * orderby f.Offset * select f; * * foreach (var field in fields) * { * WriteLine("{0:x16} {1:x4} +{2:x4} {3,30} {4,30}", field.Type.Module.AssemblyId, * field.Type.MetadataToken, field.Offset, TrimRight(field.Type.Name, 30), field.Name); * * * } * if (obj.StaticFields.Count > 0) * { * var statFields = * from s in obj.StaticFields * orderby s.Offset * select s; * WriteLine("Static Fields:"); * foreach (var field in statFields) * { * WriteLine("{0:x16} {1:x4} +{2:x4} {3,30} {4,30}", field.Type.Module.AssemblyId, * field.Type.MetadataToken, field.Offset, TrimRight(field.Type.Name, 30), field.Name); * } * } * if (obj.ThreadStaticFields.Count > 0) * { * var threadFields = * from t in obj.ThreadStaticFields * orderby t.Offset * select t; * WriteLine("Thread Static Fields:"); * foreach (var field in threadFields) * { * WriteLine("{0:x16} {1:x4} +{2:x4} {3,30} {4,30}", field.Type.Module.AssemblyId, * field.Type.MetadataToken, field.Offset, TrimRight(field.Type.Name, 30), field.Name); * } * * } */ }
private void InspectProcess(int pId) { using (DataTarget target = DataTarget.AttachToProcess(pId, 10000)) { ShowCLRRuntimeInformation(target); ClrRuntime runtime = target.ClrVersions.First().CreateRuntime(); LoadThreads(runtime); Dictionary <string, TypeEntry> types = new Dictionary <string, TypeEntry>(); Dictionary <string, StringInfor> stringCount = new Dictionary <string, StringInfor>(); ClrHeap heap = runtime.Heap; var finalizerQueueObjects = runtime.EnumerateFinalizerQueueObjectAddresses().ToList(); var pinnedObjects = runtime.EnumerateHandles().Where(h => h.IsPinned).Select(h => h.Object).ToList(); var blockingObjects = heap.EnumerateBlockingObjects().Select(x => x.Object).ToList(); //var blockingObjects = runtime.Threads.SelectMany(x => x.BlockingObjects).Select(x => x.Object).ToList(); foreach (ulong obj in heap.EnumerateObjects()) { ClrType type = heap.GetObjectType(obj); var size = type.GetSize(obj); var gen = heap.GetGeneration(obj); var isInFinalizerQueue = finalizerQueueObjects.Contains(obj); var isPinned = pinnedObjects.Contains(obj); var isBlocking = blockingObjects.Contains(obj); if (types.ContainsKey(type.Name)) { types[type.Name].Count++; types[type.Name].MinSize = Math.Min(types[type.Name].MinSize, size); types[type.Name].MaxSize = Math.Max(types[type.Name].MaxSize, size); types[type.Name].TotalSize += size; types[type.Name].Generation0 += gen == 0 ? 1 : 0; types[type.Name].Generation1 += gen == 1 ? 1 : 0; types[type.Name].Generation2 += gen == 2 ? 1 : 0; types[type.Name].FinalizerQueueCount += isInFinalizerQueue ? 1 : 0; types[type.Name].PinnedCount += isPinned ? 1 : 0; types[type.Name].BlockingCount += isBlocking ? 1 : 0; } else { types[type.Name] = new TypeEntry { Name = type.Name, Count = 1, MinSize = size, MaxSize = size, TotalSize = size, Generation0 = gen == 0 ? 1 : 0, Generation1 = gen == 1 ? 1 : 0, Generation2 = gen == 2 ? 1 : 0, FinalizerQueueCount = isInFinalizerQueue ? 1 : 0, PinnedCount = isPinned ? 1 : 0, BlockingCount = isBlocking ? 1 : 0, }; } if (type.IsString) { var text = (string)type.GetValue(obj); if (stringCount.ContainsKey(text)) { stringCount[text].Count++; stringCount[text].Generation0 += gen == 0 ? 1 : 0; stringCount[text].Generation1 += gen == 1 ? 1 : 0; stringCount[text].Generation2 += gen == 2 ? 1 : 0; } else { stringCount[text] = new StringInfor { Text = text, Count = 1, Generation0 = gen == 0 ? 1 : 0, Generation1 = gen == 1 ? 1 : 0, Generation2 = gen == 2 ? 1 : 0, }; } } } var sortOrder = heapObjectsGrid.SortOrder == SortOrder.Ascending ? ListSortDirection.Ascending : ListSortDirection.Descending; var sortCol = heapObjectsGrid.SortedColumn; int rowcount = 0; heapObjectsGrid.Rows.Clear(); foreach (var val in types.Where(x => x.Value.Count > 1)) { var infor = val.Value; DataGridViewRow gridrow = new DataGridViewRow(); gridrow.DefaultCellStyle.SelectionBackColor = Color.Black; if (rowcount % 2 > 0) { gridrow.DefaultCellStyle.BackColor = Color.AliceBlue; } rowcount++; DataGridViewTextBoxCell name = new DataGridViewTextBoxCell(); name.Value = infor.Name; gridrow.Cells.Add(name); DataGridViewTextBoxCell count = new DataGridViewTextBoxCell(); count.Style.Alignment = DataGridViewContentAlignment.BottomRight; count.Style.Format = "n0"; count.Value = infor.Count; gridrow.Cells.Add(count); DataGridViewTextBoxCell minSize = new DataGridViewTextBoxCell(); minSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; minSize.Style.Format = "n0"; if (infor.MinSize >= 85000) { minSize.Style.ForeColor = Color.Orange; minSize.ToolTipText = "Large Object Heap"; } minSize.Value = infor.MinSize; gridrow.Cells.Add(minSize); DataGridViewTextBoxCell maxSize = new DataGridViewTextBoxCell(); maxSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; maxSize.Style.Format = "n0"; if (infor.MaxSize >= 85000) { maxSize.Style.ForeColor = Color.Orange; maxSize.ToolTipText = "Large Object Heap"; } maxSize.Value = infor.MaxSize; gridrow.Cells.Add(maxSize); DataGridViewTextBoxCell totalSize = new DataGridViewTextBoxCell(); totalSize.Style.Alignment = DataGridViewContentAlignment.BottomRight; totalSize.Style.Format = "n0"; totalSize.Value = infor.TotalSize; gridrow.Cells.Add(totalSize); DataGridViewTextBoxCell generation0 = new DataGridViewTextBoxCell(); generation0.Value = infor.Generation0; generation0.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation0.Style.Format = "n0"; gridrow.Cells.Add(generation0); DataGridViewTextBoxCell generation1 = new DataGridViewTextBoxCell(); generation1.Value = infor.Generation1; generation1.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation1.Style.Format = "n0"; gridrow.Cells.Add(generation1); DataGridViewTextBoxCell generation2 = new DataGridViewTextBoxCell(); generation2.Value = infor.Generation2; generation2.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation2.Style.Format = "n0"; gridrow.Cells.Add(generation2); DataGridViewTextBoxCell finalizerQueue = new DataGridViewTextBoxCell(); finalizerQueue.Value = infor.FinalizerQueueCount; finalizerQueue.Style.Alignment = DataGridViewContentAlignment.BottomRight; finalizerQueue.Style.Format = "n0"; gridrow.Cells.Add(finalizerQueue); DataGridViewTextBoxCell pinned = new DataGridViewTextBoxCell(); pinned.Value = infor.PinnedCount; pinned.Style.Alignment = DataGridViewContentAlignment.BottomRight; pinned.Style.Format = "n0"; gridrow.Cells.Add(pinned); DataGridViewTextBoxCell blocking = new DataGridViewTextBoxCell(); blocking.Value = infor.BlockingCount; blocking.Style.Alignment = DataGridViewContentAlignment.BottomRight; blocking.Style.Format = "n0"; gridrow.Cells.Add(blocking); heapObjectsGrid.Rows.Add(gridrow); } heapObjectsGrid.Columns[1].HeaderCell.ToolTipText = $"Total: { types.Values.Sum(x => x.Count).ToString("N0")} objects"; heapObjectsGrid.Columns[4].HeaderCell.ToolTipText = $"Total: {types.Values.Sum(x => (long)x.TotalSize).ToString("N0")} bytes"; if (sortCol != null) { heapObjectsGrid.Sort(sortCol, sortOrder); } // Strings Grid View: rowcount = 0; stringsGrid.Rows.Clear(); foreach (var str in stringCount) { DataGridViewRow gridrow = new DataGridViewRow(); gridrow.DefaultCellStyle.SelectionBackColor = Color.Black; if (rowcount % 2 > 0) { gridrow.DefaultCellStyle.BackColor = Color.AliceBlue; } rowcount++; DataGridViewTextBoxCell name = new DataGridViewTextBoxCell(); name.Value = str.Key; gridrow.Cells.Add(name); DataGridViewTextBoxCell length = new DataGridViewTextBoxCell(); length.Style.Alignment = DataGridViewContentAlignment.BottomRight; length.Style.Format = "n0"; length.Value = str.Key.Length; gridrow.Cells.Add(length); DataGridViewTextBoxCell count = new DataGridViewTextBoxCell(); count.Style.Alignment = DataGridViewContentAlignment.BottomRight; count.Style.Format = "n0"; count.Value = str.Value.Count; gridrow.Cells.Add(count); DataGridViewTextBoxCell generation0 = new DataGridViewTextBoxCell(); generation0.Value = str.Value.Generation0; generation0.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation0.Style.Format = "n0"; gridrow.Cells.Add(generation0); DataGridViewTextBoxCell generation1 = new DataGridViewTextBoxCell(); generation1.Value = str.Value.Generation1; generation1.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation1.Style.Format = "n0"; gridrow.Cells.Add(generation1); DataGridViewTextBoxCell generation2 = new DataGridViewTextBoxCell(); generation2.Value = str.Value.Generation2; generation2.Style.Alignment = DataGridViewContentAlignment.BottomRight; generation2.Style.Format = "n0"; gridrow.Cells.Add(generation2); stringsGrid.Rows.Add(gridrow); } } }
/// <summary> /// Find all Strings in Heap an generate a dublicate string report /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonStrings_Click(object sender, EventArgs e) { if (Process == null) { return; } saveFileDialogStrings.FileName = String.Format("clrmd_{0:yyyyMMdd_HHmmss}", DateTime.Now); if (saveFileDialogStrings.ShowDialog() != DialogResult.OK) { return; } if (File.Exists(saveFileDialogStrings.FileName)) { File.Delete(saveFileDialogStrings.FileName); } Cursor.Current = Cursors.WaitCursor; HashSet <StringObjectHelper> stringObjectList = new HashSet <StringObjectHelper>(); ulong fullSize = 0; int objectCount = 0; using (StreamWriter writer = new StreamWriter(saveFileDialogStrings.FileName, true, Encoding.UTF8)) { using (DataTarget dataTarget = DataTarget.AttachToProcess(Process.Id, dataTargetTimeOut, dataTargetAttachFlag)) { ClrInfo clrVersion = dataTarget.ClrVersions.First(); ClrRuntime runtime = clrVersion.CreateRuntime(); if (runtime.Heap.CanWalkHeap) { foreach (ulong ptr in runtime.Heap.EnumerateObjectAddresses()) { ClrType type = runtime.Heap.GetObjectType(ptr); if (type == null || type.IsString == false) { continue; } StringObjectHelper stringObject = new StringObjectHelper(); stringObject.String = (string)type.GetValue(ptr); stringObject.Size = type.GetSize(ptr); StringObjectHelper stringObjectFound; if (stringObjectList.TryGetValue(stringObject, out stringObjectFound)) { stringObjectFound.PtrList.Add(ptr); } else { stringObject.PtrList.Add(ptr); stringObjectList.Add(stringObject); } objectCount++; fullSize += stringObject.Size; //write all strings writer.WriteLine("**{0}#{1:X}#G{2}#{3:n0}", objectCount, ptr, type.Heap.GetGeneration(ptr), stringObject.Size); writer.WriteLine(stringObject.String); } } writer.WriteLine(); writer.WriteLine("**Position#HeapPtr#Generation#Size"); writer.WriteLine(); writer.WriteLine("{0:n0} String Objects", objectCount); writer.WriteLine("{0:n0} String unique Strings", stringObjectList.Count); writer.WriteLine("{0:n0} Bytes", fullSize); } } //write dublicate info var orderedList = stringObjectList.OrderByDescending(x => x.PtrList.Count); using (StreamWriter writer = new StreamWriter(saveFileDialogStrings.FileName + ".unique.csv", true, Encoding.UTF8)) { writer.WriteLine("Count;Size;FullSize;Content;Ptrs"); foreach (StringObjectHelper so in orderedList) { string pointerList = String.Join(",", so.PtrList.Select(x => String.Format("x{0:X}", x))); writer.WriteLine(String.Format("{0};{1};{2};{3};{4}", so.PtrList.Count, so.Size, so.Size * (ulong)so.PtrList.Count, (so.String.Length > 50 ? so.String.Substring(0, 50) + " ..." : so.String).Replace("'", "").Replace(";", "").Replace("\r", " ").Replace("\n", ""), pointerList.Length > 50 ? pointerList.Substring(0, 50) + " ..." : pointerList)); } } Cursor.Current = Cursors.Default; }
/// <summary> /// Returns the simple value of an instance of this type. Undefined behavior if HasSimpleValue returns false. /// For example ELEMENT_TYPE_I4 is an "int" and the return value of this function would be an int. /// </summary> /// <param name="address">The address of an instance of this type.</param> /// <returns>System.Object.</returns> public object GetValue(ulong address) => ClrType.GetValue(address);