private static void ChainStateMachinesBasedOnJointableTasks(DebuggerContext context, List <AsyncStateMachine> allStateMachines) { foreach (AsyncStateMachine?stateMachine in allStateMachines) { if (stateMachine.Previous is null) { try { ClrObject joinableTask = stateMachine.StateMachine.TryGetObjectField("<>4__this"); ClrObject wrappedTask = joinableTask.TryGetObjectField("wrappedTask"); if (!wrappedTask.IsNull) { AsyncStateMachine?previousStateMachine = allStateMachines .FirstOrDefault(s => s.Task.Address == wrappedTask.Address); if (previousStateMachine is object && stateMachine != previousStateMachine) { stateMachine.Previous = previousStateMachine; previousStateMachine.Next = stateMachine; previousStateMachine.DependentCount++; } } } #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 fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}"); } } } }
private static void ChainStateMachinesBasedOnTaskContinuations(DebuggerContext context, Dictionary <ulong, AsyncStateMachine> knownStateMachines) { foreach (AsyncStateMachine?stateMachine in knownStateMachines.Values) { ClrObject taskObject = stateMachine.Task; try { while (!taskObject.IsNull) { // 3 cases in order to get the _target: // 1. m_continuationObject.m_action._target // 2. m_continuationObject._target // 3. m_continuationObject.m_task.m_stateObject._target ClrObject continuationObject = taskObject.TryGetObjectField("m_continuationObject"); if (continuationObject.IsNull) { break; } ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, continuationObject); taskObject = continuationObject; } } #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 fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}"); } } }
private static void MarkThreadingBlockTasks(DebuggerContext context, List <AsyncStateMachine> allStateMachines) { foreach (var thread in context.Runtime.Threads) { var stackFrame = thread.EnumerateStackTrace().Take(50).FirstOrDefault( f => f.Method is { } method && string.Equals(f.Method.Name, "CompleteOnCurrentThread", StringComparison.Ordinal) && string.Equals(f.Method.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal)); if (stackFrame is object) { var visitedObjects = new HashSet <ulong>(); foreach (IClrStackRoot stackRoot in thread.EnumerateStackRoots()) { ClrObject stackObject = stackRoot.Object; if (string.Equals(stackObject.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal) || string.Equals(stackObject.Type?.BaseType?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal)) { if (visitedObjects.Add(stackObject.Address)) { var joinableTaskObject = new ClrObject(stackObject.Address, stackObject.Type); int state = joinableTaskObject.ReadField <int>("state"); if ((state & 0x10) == 0x10) { // This flag indicates the JTF is blocking the thread var wrappedTask = joinableTaskObject.TryGetObjectField("wrappedTask"); if (!wrappedTask.IsNull) { var blockingStateMachine = allStateMachines .FirstOrDefault(s => s.Task.Address == wrappedTask.Address); if (blockingStateMachine is object) { blockingStateMachine.BlockedThread = thread.OSThreadId; blockingStateMachine.BlockedJoinableTask = joinableTaskObject; } } break; } } } } } } }
private static void ChainStateMachineBasedOnTaskContinuations(Dictionary <ulong, AsyncStateMachine> knownStateMachines, AsyncStateMachine stateMachine, ClrObject continuationObject) { var continuationAction = continuationObject.TryGetObjectField("m_action"); // case 1 var 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 var 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 { var nextContinuation = continuationTarget.TryGetObjectField("m_continuation"); continuationTarget = nextContinuation.TryGetObjectField("_target"); } } var items = continuationObject.TryGetObjectField("_items"); if (!items.IsNull && items.IsArray && items.ContainsPointers) { foreach (var promise in items.EnumerateReferences(true)) { if (!promise.IsNull) { var innerContinuationObject = promise.TryGetObjectField("m_continuationObject"); if (!innerContinuationObject.IsNull) { ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, innerContinuationObject); } else { ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, promise); } } } } }