public ulong GetTaskStateFromAddress(ulong address) { const string stateFieldName = "m_stateFlags"; var type = _heap.GetObjectType(address); if ((type != null) && (type.Name.StartsWith("System.Threading.Tasks.Task"))) { // could be other Task-prefixed types in the same namespace such as TaskCompletionSource if (type.GetFieldByName(stateFieldName) == null) { return(0); } return((ulong)_heap.GetObject(address).ReadField <int>(stateFieldName)); } return(0); }
private void CalculateTypeStatisticsPhase2(ClrHeap heap, Regex typesToReportStatisticsOnRegex, HashSet <ulong> markedObjects, TypeStatistics typeStatistics) { typeStatistics.MemoryProfilingStopwatch.Start(); HashSet <ulong> visitedObjects = new HashSet <ulong>(); Queue <ClrObject> objectQueue = new Queue <ClrObject>(); // Start with exclusive = inclusive and walk the heap from roots looking for objects to subtract from this number. typeStatistics.ExclusiveRetainedBytes = typeStatistics.InclusiveRetainedBytes; foreach (ClrRoot root in heap.EnumerateRoots()) { // Interested only in roots outside of our marked inclusive graph. if (!markedObjects.Contains(root.Object)) { ClrObject rootObj = heap.GetObject(root.Object); objectQueue.Enqueue(rootObj); visitedObjects.Add(root.Object); while (objectQueue.Count > 0) { ClrObject obj = objectQueue.Dequeue(); if (obj.IsNull || obj.Type == null) { continue; } // We stop the walk when we see an object of a type we are reporting statistics on. if (!typesToReportStatisticsOnRegex.IsMatch(obj.Type.Name)) { if (markedObjects.Contains(obj.Address)) { // Not an object of a type we are reporting statistics on but it is part of the inclusive object graph. // This means that it must not be reported in the exclusive bytes. typeStatistics.ExclusiveRetainedBytes -= obj.Size; markedObjects.Remove(obj.Address); } // Follow all references. foreach (var reference in obj.EnumerateObjectReferences()) { if (!reference.IsNull && !visitedObjects.Contains(reference.Address)) { visitedObjects.Add(reference.Address); objectQueue.Enqueue(reference); } } } } } } typeStatistics.MemoryProfilingStopwatch.Stop(); }
private void DebugObjectFields() { ClrHeap heap = Context.Heap; ClrObject clrObject = heap.GetObject(0x2910237db80); Debug.WriteLine("Name Dec Offset Hex Offset Type"); foreach (var field in clrObject.Type.Fields) { Debug.WriteLine($"{field.Name,-25} {field.Offset,10} {field.Offset,10:x} {field.Type?.Name}"); } }
static void Main(string[] args) { if (args.Length != 3) { Console.WriteLine("Usage: DumpDict [dump] [objref]"); Environment.Exit(1); } if (ulong.TryParse(args[2], System.Globalization.NumberStyles.HexNumber, null, out ulong objAddr)) { Console.WriteLine($"Could not parse object ref '{args[2]}'."); Environment.Exit(1); } string dumpFileName = args[0]; string dacPath = args[1]; using DataTarget dataTarget = DataTarget.LoadDump(dumpFileName); using ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrObject obj = heap.GetObject(objAddr); if (!obj.IsValidObject) { Console.WriteLine("Invalid object {0:X}", obj); return; } if (!obj.Type.Name.StartsWith("System.Collections.Generic.Dictionary")) { Console.WriteLine("Error: Expected object {0:X} to be a dictionary, instead it's of type '{1}'."); return; } // Get the entries field. ClrArray entries = obj.ReadObjectField("entries").AsArray(); Console.WriteLine("{0,8} {1,16} : {2}", "hash", "key", "value"); for (int i = 0; i < entries.Length; ++i) { ClrObject entry = entries.GetObjectValue(i); // TODO: Need to handle non-object references int hashCode = entry.ReadField <int>("hashCode"); ClrObject key = entry.ReadObjectField("key"); ClrObject value = entry.ReadObjectField("value"); Console.WriteLine($"{hashCode:x} {key} -> {value}"); } }
public void PrevObject() { using DataTarget dt = TestTargets.Types.LoadFullDump(); using ClrRuntime runtime = dt.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; foreach (ClrSegment seg in heap.Segments) { ClrObject prev = heap.GetObject(seg.FirstObjectAddress); Assert.Equal(0ul, seg.GetPreviousObjectAddress(prev)); foreach (ClrObject curr in seg.EnumerateObjects().Skip(1)) { Assert.Equal(prev.Address, seg.GetPreviousObjectAddress(curr)); prev = curr; } } }
/// <summary> /// Enumerates all objects reachable from GC roots in the given heap. Each object is returned exactly once. /// </summary> private IEnumerable <ClrObject> EnumerateRootedObjects(ClrHeap heap) { HashSet <ulong> visitedObjects = new HashSet <ulong>(); Queue <ClrObject> objectQueue = new Queue <ClrObject>(); foreach (ClrRoot root in heap.EnumerateRoots()) { if (!visitedObjects.Contains(root.Object)) { ClrObject rootObj = heap.GetObject(root.Object); objectQueue.Enqueue(rootObj); visitedObjects.Add(root.Object); if (rootObj.Type != null) { yield return(rootObj); } while (objectQueue.Count > 0) { ClrObject obj = objectQueue.Dequeue(); if (obj.IsNull || obj.Type == null) { continue; } // Follow all references. foreach (var reference in obj.EnumerateObjectReferences()) { if (!reference.IsNull && !visitedObjects.Contains(reference.Address)) { objectQueue.Enqueue(reference); visitedObjects.Add(reference.Address); yield return(reference); } } } } } }
public HeapIndex(ClrHeap heap) { // Evaluation stack Stack <ulong> eval = new Stack <ulong>(); _roots = new ObjectSet(heap); _walkableFromRoot = new ObjectSet(heap); foreach (var clrRoot in heap.EnumerateRoots()) { _roots.Add(clrRoot.Object); eval.Push(clrRoot.Object); } while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. var address = eval.Pop(); if (_walkableFromRoot.Contains(address)) { continue; } _walkableFromRoot.Add(address); // Grab the type. We will only get null here in the case of heap corruption. ClrType?type = heap.GetObjectType(address); if (type == null) { continue; } // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. if (type.IsArray) { EnumerateArrayElements(address); } else { EnumerateFields(type, address); } } void EnumerateArrayElements(ulong address) { var obj = heap.GetObject(address); var array = obj.AsArray(); var componentType = ((ClrmdArrayType)array.Type).ComponentType; if (componentType.IsObjectReference) { foreach (var arrayElement in ArrayProxy.EnumerateObjectItems(array)) { if (!arrayElement.IsNull) { AddReference(address, arrayElement.Address); eval.Push(arrayElement.Address); } } } else { // throw new NotSupportedException($"Enumerating array of {componentType} type is not supported"); } } void EnumerateFields(ClrType?type, ulong address) { foreach (var instanceField in type.Fields) { if (instanceField.IsObjectReference) { var fieldObject = instanceField.ReadObject(address, false); if (!fieldObject.IsNull) { AddReference(address, fieldObject.Address); eval.Push(fieldObject.Address); } } } } }
private static (uint, ulong) PopulateReferencedObjects(ClrHeap heap, ClrObjectModel clrObjModel) { // Evaluation stack var eval = new Stack <ulong>(); var addressToClrObjectModel = new Dictionary <ulong, ClrObjectModel> { { clrObjModel.Address, clrObjModel } }; // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. Note the ObjectSet here is basically just "HashSet<ulong>". // However, HashSet<ulong> is *extremely* memory inefficient. So we use our own to // avoid OOMs. var currentObj = clrObjModel.Address; var considered = new ObjectSet(heap); uint count = 0; ulong size = 0; eval.Push(currentObj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. currentObj = eval.Pop(); if (considered.Contains(currentObj)) { continue; } considered.Add(currentObj); // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(currentObj); if (type == null) { continue; } count++; size += type.GetSize(currentObj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(currentObj, delegate(ulong child, int offset) { if (child != 0 && !considered.Contains(child)) { var childObj = heap.GetObject(child); ClrObjectModel childClrModel = null; if (addressToClrObjectModel.ContainsKey(childObj)) { childClrModel = addressToClrObjectModel[childObj]; } else { childClrModel = new ClrObjectModel { TypeName = childObj.Type.Name, Address = childObj, ReferencedObjects = new List <ClrObjectModel>(), }; addressToClrObjectModel[currentObj] = childClrModel; } addressToClrObjectModel[currentObj].ReferencedObjects.Add(childClrModel); eval.Push(child); } }); } return(count, size); }
public static void LogTaskObjects(RuntimeContext runtimeContext, ILogger logger, bool includeCompleted, bool statOnly) { ClrHeap heap = runtimeContext.Heap; logger.LogInformation("Tasks:"); // TODO System.Threading.Tasks.VoidTaskResult var taskQuery = from address in heap.EnumerateObjects() let type = address.Type where type != null && !type.IsFree && type.Name != null && // TODO find better way (type.Name == "System.Threading.Tasks.Task" || (type.Name.StartsWith("System.Threading.Tasks.Task<", StringComparison.Ordinal) && type.Name.EndsWith(">", StringComparison.Ordinal))) && (type.BaseType.Name == "System.Threading.Tasks.Task" || type.EnumerateInterfaces().Any(i => i.Name == "System.IAsyncResult")) select new { Type = type, Address = address }; var taskStat = new Dictionary <string, int>(); foreach (var taskInfo in taskQuery) { var taskObject = heap.GetObject(taskInfo.Address); var taskProxy = new TaskProxy(runtimeContext, taskObject); var taskIsCompleted = taskProxy.IsCompleted; if (!includeCompleted) { if (taskIsCompleted) { continue; } } if (statOnly) { var taskTypeName = taskInfo.Type.Name !; taskStat.IncrementValue(taskTypeName); } else { logger.LogInformation($"{taskInfo.Address:X} {taskInfo.Type} taskIsCompleted={taskIsCompleted}"); var actionObject = taskObject.ReadObjectField("m_action"); if (!actionObject.IsNull && actionObject.Type.Name == "System.Action") { var ptr = actionObject.ReadValueTypeField("_methodPtr") .ReadField <UIntPtr>("m_value") .ToUInt64(); var clrMethod = heap.Runtime.GetMethodByInstructionPointer(ptr); if (clrMethod != null) { logger.LogInformation($"method = {clrMethod.Name}"); } } foreach (var clrInstanceField in taskInfo.Type.Fields) { if (clrInstanceField.Name == "m_parent") { continue; } if (clrInstanceField.Name == "m_stateObject") { continue; } if (clrInstanceField.Name == "m_taskScheduler") { continue; } if (clrInstanceField.Name == "m_contingentProperties") { continue; } if (clrInstanceField.Name == "m_taskId") { continue; } // logger.LogInformation($"{clrInstanceField.Name} ----------------------"); // var fieldValue = clrInstanceField.GetValue(taskInfo.Address); if (clrInstanceField.IsPrimitive) { if (clrInstanceField.Name == "m_stateFlags") { var stateValue = clrInstanceField.Read <int>(taskInfo.Address, false); logger.LogInformation($"{clrInstanceField.Name} = "); foreach (var statePair in TaskProxy.TaskStates) { if ((statePair.Key & stateValue) == statePair.Key) { logger.LogInformation($"{statePair.Value} |"); } } continue; } // logger.LogInformation($"{clrInstanceField.Name} = {fieldValue}"); // continue; } // if (clrInstanceField.IsObjectReference) // { // var fieldAaddress = (ulong) fieldValue; // if (fieldAaddress == 0) // { // logger.LogInformation($"{clrInstanceField.Name} = {{null}}"); // continue; // } // // var fieldType = heap.GetObjectType(fieldAaddress); // logger.LogInformation($"{clrInstanceField.Name} = {fieldAaddress:X} {fieldType}"); // if (fieldType.Name == "System.Action") // { // //heap.PrintObjectFields(fieldAaddress, fieldType, 1); // // var targetField = fieldType.GetFieldByName("_target"); // var targetFieldAddress = (ulong) targetField.GetValue(fieldAaddress); // if (targetFieldAddress == 0) // { // logger.LogInformation("\t_target = {null}"); // } // else // { // logger.LogInformation($"\t_target = {heap.GetObjectType(targetFieldAddress)}"); // // // TODO if it is "System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner" then take "m_stateMachine" field // } // // var methodPtrField = fieldType.GetFieldByName("_methodPtr"); // var methodPtrValue = (long) methodPtrField.GetValue(fieldAaddress); // // var methodPtrWithOffset = 5UL + (ulong) methodPtrValue; // heap.ReadPointer(methodPtrWithOffset + 2, out ulong someVal1); // heap.ReadPointer(methodPtrWithOffset + 1, out ulong someVal2); // heap.ReadPointer(methodPtrWithOffset + (someVal1 & 0xFF) * 8 + 3, out ulong baseMethodDesc); // var offset = (someVal2 & 0xFF) * 8; // var handle = baseMethodDesc + offset; // var method = runtime.GetMethodByHandle(handle); // // //var methodByAddress = runtime.GetMethodByAddress(methodPtrValue); // //var methodByHandle = runtime.GetMethodByHandle(methodPtrValue); // // //logger.LogInformation($"methodByAddress = {methodByAddress}"); // PrintIndention(1); // logger.LogInformation($"methodByHandle = {method}"); // } // // continue; // } // // if (clrInstanceField.IsValueClass) // { // logger.LogInformation($"{clrInstanceField.Name} {clrInstanceField.Type}"); // continue; // } // // logger.LogInformation($"{clrInstanceField.Name} UNKNOWN TYPE"); } } } if (statOnly) { foreach (var i in taskStat.OrderByDescending(t => t.Value)) { logger.LogInformation($"{i.Key} {i.Value}"); } } }