Beispiel #1
0
        private void AssertPathIsCorrect(ClrHeap heap, IReadOnlyList <ClrObject> path, ulong source, ulong target)
        {
            Assert.NotNull(path);
            Assert.True(path.Count > 0);

            ClrObject first = path.First();

            Assert.Equal(source, first.Address);

            for (int i = 0; i < path.Count - 1; i++)
            {
                ClrObject curr = path[i];
                Assert.Equal(curr.Type, heap.GetObjectType(curr.Address));

                IEnumerable <ClrObject> refs = curr.EnumerateReferences();

                ClrObject next = path[i + 1];
                Assert.Contains(next, refs);
            }

            ClrObject last = path.Last();

            Assert.Equal(last.Type, heap.GetObjectType(last.Address));
            Assert.Equal(target, last.Address);
        }
Beispiel #2
0
        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());
        }
Beispiel #3
0
        public void EnumerateGCRefsArray()
        {
            using DataTarget dataTarget = TestTargets.GCRoot.LoadFullDump();
            using ClrRuntime runtime    = dataTarget.ClrVersions.Single().CreateRuntime();
            ClrHeap heap = runtime.Heap;

            ClrModule module   = heap.Runtime.GetMainModule();
            ClrType   mainType = module.GetTypeByName("GCRootTarget");

            ClrObject obj = mainType.GetStaticObjectValue("TheRoot");

            obj = obj.GetObjectField("Item1");

            Assert.Equal("System.Object[]", obj.Type.Name);

            ClrObject[] refs = obj.EnumerateReferences(false).ToArray();
            Assert.Single(refs);
            Assert.Equal("DoubleRef", refs[0].Type.Name);
        }
Beispiel #4
0
        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 (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}");
                }
            }
        }
        private static void ChainStateMachineBasedOnTaskContinuations(Dictionary <ulong, AsyncStateMachine> knownStateMachines, AsyncStateMachine stateMachine, ClrObject continuationObject)
        {
            ClrObject continuationAction = continuationObject.TryGetObjectField("m_action");

            // case 1
            ClrObject continuationTarget = continuationAction.TryGetObjectField("_target");

            if (continuationTarget.IsNull)
            {
                // case 2
                continuationTarget = continuationObject.TryGetObjectField("_target");
                if (continuationTarget.IsNull)
                {
                    // case 3
                    continuationTarget = continuationObject.TryGetObjectField("m_task").TryGetObjectField("m_stateObject").TryGetObjectField("_target");
                }
            }

            while (!continuationTarget.IsNull)
            {
                // now get the continuation from the target
                ClrObject continuationTargetStateMachine = continuationTarget.TryGetObjectField("m_stateMachine");
                if (!continuationTargetStateMachine.IsNull)
                {
                    AsyncStateMachine targetAsyncState;
                    if (knownStateMachines.TryGetValue(continuationTargetStateMachine.Address, out targetAsyncState) && targetAsyncState != stateMachine)
                    {
                        stateMachine.Next = targetAsyncState;
                        stateMachine.DependentCount++;
                        targetAsyncState.Previous = stateMachine;
                    }

                    break;
                }
                else
                {
                    ClrObject nextContinuation = continuationTarget.TryGetObjectField("m_continuation");
                    continuationTarget = nextContinuation.TryGetObjectField("_target");
                }
            }

            ClrObject items = continuationObject.TryGetObjectField("_items");

            if (!items.IsNull && items.IsArray && items.ContainsPointers)
            {
                foreach (ClrObject promise in items.EnumerateReferences(true))
                {
                    if (!promise.IsNull)
                    {
                        ClrObject innerContinuationObject = promise.TryGetObjectField("m_continuationObject");
                        if (!innerContinuationObject.IsNull)
                        {
                            ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, innerContinuationObject);
                        }
                        else
                        {
                            ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, promise);
                        }
                    }
                }
            }
        }
Beispiel #7
0
        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;
                                        }

                                        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 (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}");
                }
            }
        }