private void GCRootsImpl(GCRoot gcroot) { ClrHeap heap = gcroot.Heap; ulong target = heap.GetObjectsOfType("TargetType").Single(); RootPath[] rootPaths = gcroot.EnumerateGCRoots(target, false, CancellationToken.None).ToArray(); Assert.IsTrue(rootPaths.Length >= 2); foreach (RootPath rootPath in rootPaths) { AssertPathIsCorrect(heap, rootPath.Path.ToArray(), rootPath.Path.First().Address, target); } bool hasThread = false, hasStatic = false; foreach (RootPath rootPath in rootPaths) { if (rootPath.Root.Kind == GCRootKind.Pinning) { hasStatic = true; } else if (rootPath.Root.Kind == GCRootKind.LocalVar) { hasThread = true; } } Assert.IsTrue(hasThread); Assert.IsTrue(hasStatic); }
private static void GetKnownSourceAndTarget(ClrHeap heap, out ulong source, out ulong target) { ClrModule module = heap.Runtime.GetMainModule(); ClrType mainType = module.GetTypeByName("GCRootTarget"); source = mainType.GetStaticObjectValue("TheRoot").Address; target = heap.GetObjectsOfType("TargetType").Single(); }
private ClrObject FindFirstInstanceOfType(ClrHeap heap, string typeName) { var type = heap.GetTypeByName(typeName); if (type is null) { throw new InvalidOperationException($"Could not find {typeName} in {TestTarget.Source} source."); } return(new ClrObject(heap.GetObjectsOfType(typeName).First(), type)); }
private void GCRootsImpl(GCRoot gcroot) { ClrHeap heap = gcroot.Heap; ulong target = heap.GetObjectsOfType("TargetType").Single(); GCRootsImpl(gcroot, heap, target, parallelism: 1, unique: false); GCRootsImpl(gcroot, heap, target, parallelism: 16, unique: false); GCRootsImpl(gcroot, heap, target, parallelism: 1, unique: true); GCRootsImpl(gcroot, heap, target, parallelism: 16, unique: true); }
public void EnumerateGCRefs() { using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump(); using ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrObject obj = heap.GetObjectsOfType("DoubleRef").Single(); Assert.False(obj.IsNull); ValidateRefs(obj.EnumerateReferences().ToArray()); }
public void EnumerateGCRefs() { using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump(); ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ulong obj = heap.GetObjectsOfType("DoubleRef").Single(); ClrType type = heap.GetObjectType(obj); ClrObject[] refs = type.EnumerateObjectReferences(obj).ToArray(); ValidateRefs(refs); }
public void GCRootsIndirectHandles() { using DataTarget dataTarget = TestTargets.GCRoot2.LoadFullDump(); ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; GCRoot gcroot = new GCRoot(heap); ulong target = heap.GetObjectsOfType("IndirectTarget").Single(); _ = Assert.Single(gcroot.EnumerateGCRoots(target, unique: true, CancellationToken.None)); Assert.Equal(2, gcroot.EnumerateGCRoots(target, unique: false, CancellationToken.None).Count()); }
public void GCRoots() { using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump(); using ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; GCRoot gcroot = new GCRoot(heap); ulong target = heap.GetObjectsOfType("TargetType").Single(); GCRootsImpl(gcroot, heap, target, parallelism: 1, unique: false); GCRootsImpl(gcroot, heap, target, parallelism: 16, unique: false); GCRootsImpl(gcroot, heap, target, parallelism: 1, unique: true); GCRootsImpl(gcroot, heap, target, parallelism: 16, unique: true); }
private void GCRootsImpl(GCRoot gcroot) { ClrHeap heap = gcroot.Heap; ulong target = heap.GetObjectsOfType("TargetType").Single(); GCRootPath[] rootPaths = gcroot.EnumerateGCRoots(target, unique: false, 1, CancellationToken.None).ToArray(); CheckRootPaths(heap, target, rootPaths, mustContainStatic: true); rootPaths = gcroot.EnumerateGCRoots(target, unique: false, 16, CancellationToken.None).ToArray(); CheckRootPaths(heap, target, rootPaths, mustContainStatic: true); rootPaths = gcroot.EnumerateGCRoots(target, unique: true, 1, CancellationToken.None).ToArray(); CheckRootPaths(heap, target, rootPaths, mustContainStatic: true); // In the case where we say we only want unique rooting chains AND we want to look in parallel, // we cannot guarantee that we will pick the static roots over the stack ones. Hence we don't // ensure that the static variable is enumerated. rootPaths = gcroot.EnumerateGCRoots(target, unique: true, 16, CancellationToken.None).ToArray(); CheckRootPaths(heap, target, rootPaths, mustContainStatic: false); }
public void EnumerateGCRefs() { using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump(); using ClrRuntime runtime = dataTarget.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrObject doubleRef = heap.GetObjectsOfType("DoubleRef").Single(); Assert.False(doubleRef.IsNull); ClrObject[] refs = doubleRef.EnumerateReferences().ToArray(); // Should contain one SingleRef and one TripleRef object. Assert.Equal(2, refs.Length); Assert.Equal(1, refs.Count(r => r.Type.Name == "SingleRef")); Assert.Equal(1, refs.Count(r => r.Type.Name == "TripleRef")); foreach (ClrObject obj in refs) { Assert.NotEqual(0ul, obj.Address); Assert.Equal(obj.Type.Heap.GetObjectType(obj.Address), obj.Type); } }
private static void GetAllStateMachines(DebuggerContext context, ClrHeap heap, List <AsyncStateMachine> allStateMachines, Dictionary <ulong, AsyncStateMachine> knownStateMachines) { foreach (var obj in heap.GetObjectsOfType("System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner")) { try { var stateMachine = obj.ReadObjectField("m_stateMachine"); if (!knownStateMachines.ContainsKey(stateMachine.Address)) { try { var state = stateMachine.ReadField <int>("<>1__state"); if (state >= -1) { ClrObject taskField = default(ClrObject); var asyncBuilder = stateMachine.TryGetValueClassField("<>t__builder"); if (asyncBuilder.HasValue) { while (asyncBuilder.HasValue) { taskField = asyncBuilder.TryGetObjectField("m_task"); if (!taskField.IsNull) { break; } asyncBuilder = asyncBuilder.TryGetValueClassField("m_builder"); } } else { // CLR debugger may not be able to access t__builder, when NGEN assemblies are being used, and the type of the field could be lost. // Our workaround is to pick up the first Task object referenced by the state machine, which seems to be correct. // That function works with the raw data structure (like how GC scans the object, so it doesn't depend on symbols. foreach (ClrObject referencedObject in stateMachine.EnumerateReferences(true)) { if (!referencedObject.IsNull && referencedObject.Type is object) { if (string.Equals(referencedObject.Type.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal) || string.Equals(referencedObject.Type.BaseType?.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal)) { taskField = referencedObject; break; } } } } var asyncState = new AsyncStateMachine(state, stateMachine, taskField); allStateMachines.Add(asyncState); knownStateMachines.Add(stateMachine.Address, asyncState); if (stateMachine.Type is object) { foreach (var method in stateMachine.Type.Methods) { if (method.Name == "MoveNext" && method.NativeCode != ulong.MaxValue) { asyncState.CodeAddress = method.NativeCode; } } } } } catch (Exception ex) { context.Output.WriteLine($"Fail to process state machine {stateMachine.Address:x} Type:'{stateMachine.Type?.Name}' Module:'{stateMachine.Type?.Module?.Name}' Error: {ex.Message}"); } } } catch (Exception ex) { context.Output.WriteLine($"Fail to process AsyncStateMachine Runner {obj.Address:x} Error: {ex.Message}"); } } }
private static void GetAllStateMachines(DebuggerContext context, ClrHeap heap, List <AsyncStateMachine> allStateMachines, Dictionary <ulong, AsyncStateMachine> knownStateMachines) { foreach (ClrObject obj in heap.GetObjectsOfType("System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner")) { try { ClrObject stateMachine = obj.ReadObjectField("m_stateMachine"); if (!knownStateMachines.ContainsKey(stateMachine.Address)) { try { var state = stateMachine.ReadField <int>("<>1__state"); if (state >= -1) { ClrObject taskField = default(ClrObject); ClrValueType?asyncBuilder = stateMachine.TryGetValueClassField("<>t__builder"); if (asyncBuilder.HasValue) { while (asyncBuilder.HasValue) { taskField = asyncBuilder.TryGetObjectField("m_task"); if (!taskField.IsNull) { break; } ClrValueType?nextAsyncBuilder = asyncBuilder.TryGetValueClassField("m_builder"); if (nextAsyncBuilder is null) { asyncBuilder = asyncBuilder.TryGetValueClassField("_methodBuilder"); } else { asyncBuilder = nextAsyncBuilder; } } } else { // CLR debugger may not be able to access t__builder, when NGEN assemblies are being used, and the type of the field could be lost. // Our workaround is to pick up the first Task object referenced by the state machine, which seems to be correct. // That function works with the raw data structure (like how GC scans the object, so it doesn't depend on symbols. // // However, one problem of that is we can pick up tasks from other reference fields of the same structure. So, we go through fields which we have symbols // and remember references encounted, and we skip them when we go through GC references. // Note: we can do better by going through other value structures, and extract references from them here, which we can consider when we have a real scenario. var previousReferences = new Dictionary <ulong, int>(); if (stateMachine.Type?.GetFieldByName("<>t__builder") is not null) { foreach (ClrInstanceField field in stateMachine.Type.Fields) { if (string.Equals(field.Name, "<>t__builder", StringComparison.Ordinal)) { break; } if (field.IsObjectReference) { ClrObject referencedValue = field.ReadObject(stateMachine.Address, interior: false); if (!referencedValue.IsNull) { if (previousReferences.TryGetValue(referencedValue.Address, out int refCount)) { previousReferences[referencedValue.Address] = refCount + 1; } else { previousReferences[referencedValue.Address] = 1; } } } } } foreach (ClrObject referencedObject in stateMachine.EnumerateReferences(true)) { if (!referencedObject.IsNull) { if (previousReferences.TryGetValue(referencedObject.Address, out int refCount) && refCount > 0) { if (refCount == 1) { previousReferences.Remove(referencedObject.Address); } else { previousReferences[referencedObject.Address] = refCount - 1; } continue; } else if (previousReferences.Count > 0) { continue; } if (referencedObject.Type is object && (string.Equals(referencedObject.Type.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal) || string.Equals(referencedObject.Type.BaseType?.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal))) { taskField = referencedObject; break; } } } } var asyncState = new AsyncStateMachine(state, stateMachine, taskField); allStateMachines.Add(asyncState); knownStateMachines.Add(stateMachine.Address, asyncState); if (stateMachine.Type is object) { foreach (ClrMethod?method in stateMachine.Type.Methods) { if (method.Name == "MoveNext" && method.NativeCode != ulong.MaxValue) { asyncState.CodeAddress = method.NativeCode; } } } } } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to process state machine {stateMachine.Address:x} Type:'{stateMachine.Type?.Name}' Module:'{stateMachine.Type?.Module?.Name}' Error: {ex.Message}"); } } } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to process AsyncStateMachine Runner {obj.Address:x} Error: {ex.Message}"); } } }