Fix DumpAsync to understand ContinuationWrapper (dotnet/coreclr#22913)
authorStephen Toub <stoub@microsoft.com>
Thu, 28 Feb 2019 21:44:26 +0000 (16:44 -0500)
committerGitHub <noreply@github.com>
Thu, 28 Feb 2019 21:44:26 +0000 (16:44 -0500)
Continuations created while the debugger is attached or certain EventSource events are enabled end up getting wrapped in an extra ContinuationWrapper object that carries some additional information.  DumpAsync currently isn't unwrapping these but should.  This just teaches it to look for a known field name so that it can "see through" these wrappers while following async "stacks".

Commit migrated from https://github.com/dotnet/coreclr/commit/3a0367c75a9b5cebbcc2f2c9609aa05536ed921b

src/coreclr/src/ToolBox/SOS/Strike/strike.cpp
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs

index 82af32e..7d74458 100644 (file)
@@ -4273,13 +4273,32 @@ void ResolveContinuation(CLRDATA_ADDRESS* contAddr)
                 }
             }
 
-            // If it was, or if it's storing an action, try to follow through to the action's target.
+            // If we now have an Action, try to follow through to the delegate's target.
             if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0)
             {
                 MOVE(*contAddr, contObj.GetAddress() + offset);
                 if (sos::IsObject(*contAddr, false))
                 {
                     contObj = TO_TADDR(*contAddr);
+
+                    // In some cases, the delegate's target might be a ContinuationWrapper, in which case we want to unwrap that as well.
+                    if (_wcsncmp(contObj.GetTypeName(), W("System.Runtime.CompilerServices.AsyncMethodBuilderCore+ContinuationWrapper"), 74) == 0 &&
+                        (offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_continuation"))) != 0)
+                    {
+                        MOVE(*contAddr, contObj.GetAddress() + offset);
+                        if (sos::IsObject(*contAddr, false))
+                        {
+                            contObj = TO_TADDR(*contAddr);
+                            if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0)
+                            {
+                                MOVE(*contAddr, contObj.GetAddress() + offset);
+                                if (sos::IsObject(*contAddr, false))
+                                {
+                                    contObj = TO_TADDR(*contAddr);
+                                }
+                            }
+                        }
+                    }
                 }
             }
         }
index f72493e..5d95c3d 100644 (file)
@@ -1100,10 +1100,10 @@ namespace System.Runtime.CompilerServices
         /// (like the action after that (which is also a ContinuationWrapper and thus form a linked list).  
         ///  We also store that task if the action is associate with at task.  
         /// </summary>
-        private sealed class ContinuationWrapper
+        private sealed class ContinuationWrapper // SOS DumpAsync command depends on this name
         {
             private readonly Action<Action, Task> _invokeAction; // This wrapper is an action that wraps another action, this is that Action.  
-            internal readonly Action _continuation;              // This is continuation which will happen after m_invokeAction  (and is probably a ContinuationWrapper)
+            internal readonly Action _continuation;              // This is continuation which will happen after m_invokeAction  (and is probably a ContinuationWrapper). SOS DumpAsync command depends on this name.
             internal readonly Task _innerTask;                   // If the continuation is logically going to invoke a task, this is that task (may be null)
 
             internal ContinuationWrapper(Action continuation, Action<Action, Task> invokeAction, Task innerTask)