public void Execute(CommandExecutionContext context) { _context = context; if (!CommandHelpers.VerifyValidObjectAddress(context, ObjectAddress)) return; _heap = context.Heap; if (!_heap.CanWalkHeap) { context.WriteErrorLine("The heap is not in a walkable state."); return; } var type = _heap.GetObjectType(ObjectAddress); _visitedObjects = new ObjectSet(_heap); _targets[ObjectAddress] = new Node(ObjectAddress, type); FillRootDictionary(); foreach (var root in _roots) { if (_visitedRoots.Contains(root) || _visitedObjects.Contains(root.Object)) continue; Node path = TryFindPathToTarget(root); if (path != null) PrintOnePath(path); } }
// Returns all objects being kept alive by the input object // Based off the code at https://github.com/Microsoft/clrmd/blob/master/Documentation/WalkingTheHeap.md public static List<ulong> GetReferencedObjects(ClrHeap heap, ulong obj) { List<ulong> references = new List<ulong>(); Stack<ulong> eval = new Stack<ulong>(); HashSet<ulong> considered = new HashSet<ulong>(); eval.Push(obj); while (eval.Count > 0) { obj = eval.Pop(); if (considered.Contains(obj)) continue; considered.Add(obj); // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(obj); if (type == null) continue; references.Add(obj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObjectCarefully(obj, delegate (ulong child, int offset) { if (child != 0 && !considered.Contains(child)) eval.Push(child); }); } return references; }
public ClrObject(ClrHeap heap, ClrType type, ulong addr, bool inner) { if (heap == null) throw new ArgumentNullException("heap"); m_addr = addr; m_inner = inner; m_heap = heap; // For interior pointers (structs inside other objects), we simply have to trust the caller // gave us the right thing. m_type = inner ? type : heap.GetObjectType(addr); }
public static MultiElementDictionary2<ulong> Build(ClrHeap heap) { var result = new MultiElementDictionary2<ulong>(); foreach (var objRef in heap.EnumerateObjects()) { var type = heap.GetObjectType(objRef); result.Add(type.Name, objRef); } return result; }
static object Format(ClrHeap heap, object val) { if (val == null) return "{null}"; if (val is ulong) { ulong addr = (ulong)val; var type = heap.GetObjectType(addr); if (type != null && type.Name == "System.String") return type.GetValue(addr); else return ((ulong)val).ToString("X"); } return val; }
public ClrObject(ClrHeap heap, ClrType type, ulong addr) { if (heap == null) throw new ArgumentNullException("heap"); m_addr = addr; m_heap = heap; if (addr != 0) { var gcType = heap.GetObjectType(addr); if (gcType != null) type = gcType; } m_type = type; }
private void GetObjSize(ClrHeap heap, ulong obj, out uint count, out ulong size) { // Evaluation stack var eval = new Stack<ulong>(); // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. var considered = new HashSet<ulong>(); count = 0; size = 0; eval.Push(obj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. obj = eval.Pop(); if (!considered.Add(obj)) continue; // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(obj); if (type == null) continue; count++; size += type.GetSize(obj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(obj, (child, offset) => { if (child != 0 && !considered.Contains(child)) eval.Push(child); }); } }
public async Task<IEnumerable<object>> Execute(Models.OperationModel model, CancellationToken token, object customeParameter) { return await DebuggerSession.Instance.ExecuteOperation(() => { _token = token; _heap = DebuggerSession.Instance.Runtime.GetHeap(); _found = false; var stack = new Stack<ulong>(); foreach (var root in _heap.EnumerateRoots()) { stack.Clear(); stack.Push(root.Object); if (token.IsCancellationRequested) break; GetRefChainFromRootToObject(model.ObjectAddress, stack, new HashSet<ulong>()); if (_found) break; } var enumerable = from address in stack orderby address ascending let type = _heap.GetObjectType(address) select new { Address = address, Type = type.Name, MetadataToken = type.MetadataToken, }; return enumerable.ToList(); }); }
private static IEnumerable<Tuple<ulong, ClrType>> EnumerateFromObjectSet(ClrHeap heap, IEnumerable<ulong> objects) { var toGoThrough = new Stack<ulong>(objects); var seen = new ObjectSet(heap); while (toGoThrough.Count > 0) { var obj = toGoThrough.Pop(); if (seen.Contains(obj)) continue; seen.Add(obj); var type = heap.GetObjectType(obj); if (type == null || type.IsFree || String.IsNullOrEmpty(type.Name)) continue; yield return new Tuple<ulong, ClrType>(obj, type); type.EnumerateRefsOfObject(obj, (child, _) => { if (child != 0 && !seen.Contains(child)) { toGoThrough.Push(child); } }); } }
private static Node FindPathToTarget(ClrHeap heap, ClrRoot root) { ClrType type = heap.GetObjectType(root.Object); if (type == null) return null; List<ulong> refList = new List<ulong>(); List<int> offsetList = new List<int>(); Node curr = new Node(root.Object, type); while (curr != null) { if (curr.Children == null) { refList.Clear(); offsetList.Clear(); curr.Type.EnumerateRefsOfObject(curr.Object, delegate(ulong child, int offset) { if (child != 0) { refList.Add(child); offsetList.Add(offset); } }); curr.Children = refList.ToArray(); curr.Offsets = offsetList.ToArray(); } else { if (curr.Curr < curr.Children.Length) { ulong nextObj = curr.Children[curr.Curr]; int offset = curr.Offsets[curr.Curr]; curr.Curr++; if (m_considered.Contains(nextObj)) continue; m_considered.Add(nextObj); Node next = null; if (m_targets.TryGetValue(nextObj, out next)) { curr.Next = next; next.Prev = curr; next.Offset = offset; while (curr.Prev != null) { m_targets[curr.Object] = curr; curr = curr.Prev; } m_targets[curr.Object] = curr; return curr; } type = heap.GetObjectType(nextObj); if (type != null && type.ContainsPointers) { curr = new Node(nextObj, type, curr); curr.Offset = offset; } } else { curr = curr.Prev; if (curr != null) curr.Next = null; } } } return null; }
public void Execute(CommandExecutionContext context) { if (!String.IsNullOrEmpty(TypeRegex)) { try { new Regex(TypeRegex); } catch (ArgumentException) { context.WriteErrorLine("The regular expression specified for --type is not valid; did you forget to escape regex characters?"); return; } } _heap = context.Runtime.GetHeap(); if (!_heap.CanWalkHeap) { context.WriteErrorLine("The heap is not in a walkable state."); return; } var typeInfos = new Dictionary<ulong, TypeInfo>(); // MT to TypeInfo long totalObjectCount = 0; if (!StatisticsOnly) { context.WriteLine("{0,-20} {1,-20} {2}", "MT", "Address", "Size"); } foreach (var obj in _heap.EnumerateObjectAddresses()) { ulong mt = 0; context.Runtime.ReadPointer(obj, out mt); if (!FilterObject(obj, mt)) continue; var type = _heap.GetObjectType(obj); if (type == null || String.IsNullOrEmpty(type.Name)) continue; var size = type.GetSize(obj); if (!StatisticsOnly) { context.WriteLine("{0,-20:x16} {1,-20:x16} {2,-10}", mt, obj, size); } if (typeInfos.ContainsKey(mt)) { var current = typeInfos[mt]; current.Count += 1; current.Size += size; typeInfos[mt] = current; } else { var objType = _heap.GetObjectType(obj); var objTypeName = objType != null ? objType.Name : "<no name>"; typeInfos.Add(mt, new TypeInfo { Size = size, Count = 1, TypeName = objTypeName }); } ++totalObjectCount; } context.WriteLine("Statistics:"); context.WriteLine("{0,-20} {1,-10} {2,-10} {3}", "MT", "Count", "TotalSize", "Class Name"); foreach (var kvp in (from e in typeInfos orderby e.Value.Size ascending select e)) { context.WriteLine("{0,-20:x16} {1,-10} {2,-10} {3}", kvp.Key, kvp.Value.Count, kvp.Value.Size, kvp.Value.TypeName); } context.WriteLine("Total {0} objects", totalObjectCount); }
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); }
private static ObjectSet GetLiveObjects(ClrHeap heap) { ObjectSet considered = new ObjectSet(heap); Stack<ulong> eval = new Stack<ulong>(); foreach (var root in heap.EnumerateRoots()) eval.Push(root.Object); while (eval.Count > 0) { ulong obj = eval.Pop(); if (considered.Contains(obj)) continue; considered.Add(obj); var type = heap.GetObjectType(obj); if (type == null) // Only if heap corruption continue; type.EnumerateRefsOfObject(obj, delegate(ulong child, int offset) { if (child != 0 && !considered.Contains(child)) eval.Push(child); }); } return considered; }
internal ClrHandle(Microsoft.Diagnostics.Runtime.Desktop.V45Runtime clr, ClrHeap heap, Microsoft.Diagnostics.Runtime.Desktop.HandleData handleData) { Address obj; Address = handleData.Handle; clr.ReadPointer(Address, out obj); Object = obj; Type = heap.GetObjectType(obj); uint refCount = 0; if (handleData.Type == (int)HandleType.RefCount) { if (handleData.IsPegged != 0) refCount = handleData.JupiterRefCount; if (refCount < handleData.RefCount) refCount = handleData.RefCount; if (Type != null) { if (Type.IsCCW(obj)) { CcwData data = Type.GetCCWData(obj); if (data != null && refCount < data.RefCount) refCount = (uint)data.RefCount; } else if (Type.IsRCW(obj)) { RcwData data = Type.GetRCWData(obj); if (data != null && refCount < data.RefCount) refCount = (uint)data.RefCount; } } RefCount = refCount; } HandleType = (HandleType)handleData.Type; AppDomain = clr.GetAppDomainByAddress(handleData.AppDomain); if (HandleType == HandleType.Dependent) { DependentTarget = handleData.Secondary; DependentType = heap.GetObjectType(handleData.Secondary); } }
public static KcpUserAccount GetKcpUserAccountInfo(ulong KcpUserAccountAddr, ClrType KcpUserAccountType, ClrHeap Heap, string databaseLocation) { KcpUserAccount UserAccountInfo = new KcpUserAccount(); // Get the embedded ProtectedBinary ClrInstanceField KcpProtectedBinaryField = KcpUserAccountType.GetFieldByName("m_pbKeyData"); ulong KcpProtectedBinaryAddr = KcpProtectedBinaryField.GetAddress(KcpUserAccountAddr); ulong KcpProtectedBinaryObjAddr = (ulong)KcpProtectedBinaryField.GetValue(KcpUserAccountAddr); ClrInstanceField EncDataField = KcpProtectedBinaryField.Type.GetFieldByName("m_pbData"); ulong EncDataAddr = EncDataField.GetAddress(KcpProtectedBinaryObjAddr); ulong EncDataArrayAddr = (ulong)EncDataField.GetValue(KcpProtectedBinaryObjAddr); ClrType EncDataArrayType = Heap.GetObjectType(EncDataArrayAddr); int len = EncDataField.Type.GetArrayLength(EncDataArrayAddr); if (len <= 0 || len % 16 != 0) // Small sanity check to make sure everything's ok return null; byte[] EncData = new byte[len]; for (int i = 0; i < len; i++) { EncData[i] = (byte)EncDataArrayType.GetArrayElementValue(EncDataArrayAddr, i); } UserAccountInfo.databaseLocation = databaseLocation; UserAccountInfo.encryptedBlob = EncData; UserAccountInfo.encryptedBlobAddress = (IntPtr)KcpUserAccountType.GetArrayElementAddress(EncDataArrayAddr, 0); UserAccountInfo.encryptedBlobLen = len; return UserAccountInfo; }