diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs index d411609e365f..ec8af8c086b9 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs @@ -498,7 +498,7 @@ private IAsyncStateMachineBox GetStateMachineBox( /// event about the state machine if it's being finalized without having been completed. /// /// Specifies the type of the state machine. - private sealed class DebugFinalizableAsyncStateMachineBox : + private sealed class DebugFinalizableAsyncStateMachineBox : // SOS DumpAsync command depends on this name AsyncStateMachineBox where TStateMachine : IAsyncStateMachine { @@ -517,7 +517,7 @@ private sealed class DebugFinalizableAsyncStateMachineBox : /// A strongly-typed box for Task-based async state machines. /// Specifies the type of the state machine. /// Specifies the type of the Task's result. - private class AsyncStateMachineBox : + private class AsyncStateMachineBox : // SOS DumpAsync command depends on this name Task, IAsyncStateMachineBox where TStateMachine : IAsyncStateMachine { @@ -527,7 +527,7 @@ private class AsyncStateMachineBox : /// A delegate to the method. private Action _moveNextAction; /// The state machine itself. - public TStateMachine StateMachine; // mutable struct; do not make this readonly + public TStateMachine StateMachine; // mutable struct; do not make this readonly. SOS DumpAsync command depends on this name. /// Captured ExecutionContext with which to invoke ; may be null. public ExecutionContext Context; diff --git a/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 4b98612ab0fc..b9455611c892 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -193,7 +193,7 @@ public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel, // in that order. The logic arround this object assumes it will never regress to a previous state. - private volatile object m_continuationObject = null; + private volatile object m_continuationObject = null; // SOS DumpAsync command depends on this name // m_continuationObject is set to this when the task completes. private static readonly object s_taskCompletionSentinel = new object(); diff --git a/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/ToolBox/SOS/Strike/apollososdocs.txt index 29dd8b62629e..34e7e4d84644 100644 --- a/src/ToolBox/SOS/Strike/apollososdocs.txt +++ b/src/ToolBox/SOS/Strike/apollososdocs.txt @@ -25,16 +25,16 @@ Object Inspection Examining code and stacks ----------------------------- ----------------------------- DumpObj (do) Threads DumpArray (da) ThreadState -DumpStackObjects (dso) IP2MD -DumpHeap U -DumpVC DumpStack -GCRoot EEStack -ObjSize CLRStack -FinalizeQueue GCInfo -PrintException (pe) EHInfo -TraverseHeap BPMD -Watch COMState - StopOnCatch +DumpAsync IP2MD +DumpStackObjects (dso) U +DumpHeap DumpStack +DumpVC EEStack +GCRoot CLRStack +ObjSize GCInfo +FinalizeQueue EHInfo +PrintException (pe) BPMD +TraverseHeap COMState +Watch StopOnCatch SuppressJitOptimization Examining CLR data structures Diagnostic Utilities @@ -340,6 +340,62 @@ The arguments in detail: 5b9a628c 4000002 4 System.Int32 instance 8 y 5b9a628c 4000003 8 System.Int32 instance 12 z +\\ + +COMMAND: dumpasync. +!DumpAsync [-mt ] + [-type ]] + +!DumpAsync traverses the garbage collected heap, looking for objects representing +async state machines as created when an async method's state is transferred to the +heap. This command recognizes async state machines defined as "async void", "async Task", +"async Task", "async ValueTask", and "async ValueTask". + +The output includes a block of details for each async state machine object found. +These details include: + - a line for the type of the async state machine object, including its MethodTable address, + its object address, its size, and its type name. + - a line for the state machine type name as contained in the object. + - a listing of each field on the state machine. + - a line for a continuation from this state machine object, if one or more has been registered. + - discovered GC roots for this async state machine object. + +For example: + + 0:011> !DumpAsync + #0 + 000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + StateMachine: Program+d__4 (struct) + MT Field Offset Type VT Attr Value Name + 00007ff8d3df4b80 400000d 0 System.Int32 1 instance 0 <>1__state + 00007ff8d3e082c0 400000e 8 ...TaskMethodBuilder 1 instance 000001989f413e38 <>t__builder + 00007ff8d3dfea90 400000f 10 ...vices.TaskAwaiter 1 instance 000001989f413e40 <>u__1 + Continuation: 000001989f413e50 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]]) + GC roots: + Thread 2936c: + 000000071a37e050 00007ff8d3ac1657 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) [d:\repos\coreclr\src\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs @ 2977] + rbp+10: 000000071a37e0c0 + -> 000001989f413fa0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+
d__0, test]] + -> 000001989f413f30 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__1, test]] + -> 000001989f413ec0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__2, test]] + -> 000001989f413e50 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]] + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + HandleTable: + 000001989d8415f8 (pinned handle) + -> 00000198af3e1038 System.Object[] + -> 000001989f413410 System.Threading.TimerQueue[] + -> 000001989f413468 System.Threading.TimerQueue + -> 000001989f413330 System.Threading.TimerQueueTimer + -> 000001989f412e40 System.Threading.Tasks.Task+DelayPromise + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + ... + + +The arguments in detail: + +-mt List only those state machine objects with the MethodTable given. +-type List only those state machine objects whose type name is a + substring match of the string provided. \\ diff --git a/src/ToolBox/SOS/Strike/sos.def b/src/ToolBox/SOS/Strike/sos.def index c8d08e73195a..9ef5d2e7ed44 100644 --- a/src/ToolBox/SOS/Strike/sos.def +++ b/src/ToolBox/SOS/Strike/sos.def @@ -15,6 +15,8 @@ EXPORTS dumparray=DumpArray DumpAssembly dumpassembly=DumpAssembly + DumpAsync + dumpasync=DumpAsync DumpClass dumpclass=DumpClass DumpDomain diff --git a/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/ToolBox/SOS/Strike/sos_unixexports.src index a8cc71228b25..463c33697666 100644 --- a/src/ToolBox/SOS/Strike/sos_unixexports.src +++ b/src/ToolBox/SOS/Strike/sos_unixexports.src @@ -7,6 +7,7 @@ ClrStack CreateDump DumpArray DumpAssembly +DumpAsync DumpClass DumpDomain DumpGCData diff --git a/src/ToolBox/SOS/Strike/sosdocs.txt b/src/ToolBox/SOS/Strike/sosdocs.txt index 924d5f51b9fc..cbf7e073f277 100644 --- a/src/ToolBox/SOS/Strike/sosdocs.txt +++ b/src/ToolBox/SOS/Strike/sosdocs.txt @@ -25,15 +25,15 @@ Object Inspection Examining code and stacks ----------------------------- ----------------------------- DumpObj (do) Threads DumpArray (da) ThreadState -DumpStackObjects (dso) IP2MD -DumpHeap U -DumpVC DumpStack -GCRoot EEStack -ObjSize CLRStack -FinalizeQueue GCInfo -PrintException (pe) EHInfo -TraverseHeap BPMD - COMState +DumpAsync IP2MD +DumpStackObjects (dso) U +DumpHeap DumpStack +DumpVC EEStack +GCRoot CLRStack +ObjSize GCInfo +FinalizeQueue EHInfo +PrintException (pe) BPMD +TraverseHeap COMState Examining CLR data structures Diagnostic Utilities ----------------------------- ----------------------------- @@ -338,6 +338,62 @@ The arguments in detail: 5b9a628c 4000002 4 System.Int32 instance 8 y 5b9a628c 4000003 8 System.Int32 instance 12 z +\\ + +COMMAND: dumpasync. +!DumpAsync [-mt ] + [-type ]] + +!DumpAsync traverses the garbage collected heap, looking for objects representing +async state machines as created when an async method's state is transferred to the +heap. This command recognizes async state machines defined as "async void", "async Task", +"async Task", "async ValueTask", and "async ValueTask". + +The output includes a block of details for each async state machine object found. +These details include: + - a line for the type of the async state machine object, including its MethodTable address, + its object address, its size, and its type name. + - a line for the state machine type name as contained in the object. + - a listing of each field on the state machine. + - a line for a continuation from this state machine object, if one or more has been registered. + - discovered GC roots for this async state machine object. + +For example: + + 0:011> !DumpAsync + #0 + 000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + StateMachine: Program+d__4 (struct) + MT Field Offset Type VT Attr Value Name + 00007ff8d3df4b80 400000d 0 System.Int32 1 instance 0 <>1__state + 00007ff8d3e082c0 400000e 8 ...TaskMethodBuilder 1 instance 000001989f413e38 <>t__builder + 00007ff8d3dfea90 400000f 10 ...vices.TaskAwaiter 1 instance 000001989f413e40 <>u__1 + Continuation: 000001989f413e50 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]]) + GC roots: + Thread 2936c: + 000000071a37e050 00007ff8d3ac1657 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) [d:\repos\coreclr\src\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs @ 2977] + rbp+10: 000000071a37e0c0 + -> 000001989f413fa0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+
d__0, test]] + -> 000001989f413f30 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__1, test]] + -> 000001989f413ec0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__2, test]] + -> 000001989f413e50 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]] + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + HandleTable: + 000001989d8415f8 (pinned handle) + -> 00000198af3e1038 System.Object[] + -> 000001989f413410 System.Threading.TimerQueue[] + -> 000001989f413468 System.Threading.TimerQueue + -> 000001989f413330 System.Threading.TimerQueueTimer + -> 000001989f412e40 System.Threading.Tasks.Task+DelayPromise + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + ... + + +The arguments in detail: + +-mt List only those state machine objects with the MethodTable given. +-type List only those state machine objects whose type name is a + substring match of the string provided. \\ diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt index d0f19e323571..574ff4853739 100644 --- a/src/ToolBox/SOS/Strike/sosdocsunix.txt +++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt @@ -25,12 +25,12 @@ Object Inspection Examining code and stacks ----------------------------- ----------------------------- DumpObj (dumpobj) Threads (clrthreads) DumpArray ThreadState -DumpStackObjects (dso) IP2MD (ip2md) -DumpHeap (dumpheap) u (clru) -DumpVC DumpStack (dumpstack) -GCRoot (gcroot) EEStack (eestack) -PrintException (pe) ClrStack (clrstack) - GCInfo +DumpAsync (dumpasync) IP2MD (ip2md) +DumpStackObjects (dso) u (clru) +DumpHeap (dumpheap) DumpStack (dumpstack) +DumpVC EEStack (eestack) +GCRoot (gcroot) CLRStack (clrstack) +PrintException (pe) GCInfo EHInfo bpmd (bpmd) @@ -199,6 +199,62 @@ The arguments in detail: 5b9a628c 4000002 4 System.Int32 instance 8 y 5b9a628c 4000003 8 System.Int32 instance 12 z +\\ + +COMMAND: dumpasync. +!DumpAsync [-mt ] + [-type ]] + +!DumpAsync traverses the garbage collected heap, looking for objects representing +async state machines as created when an async method's state is transferred to the +heap. This command recognizes async state machines defined as "async void", "async Task", +"async Task", "async ValueTask", and "async ValueTask". + +The output includes a block of details for each async state machine object found. +These details include: + - a line for the type of the async state machine object, including its MethodTable address, + its object address, its size, and its type name. + - a line for the state machine type name as contained in the object. + - a listing of each field on the state machine. + - a line for a continuation from this state machine object, if one or more has been registered. + - discovered GC roots for this async state machine object. + +For example: + + (lldb) dumpasync + #0 + 000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + StateMachine: Program+d__4 (struct) + MT Field Offset Type VT Attr Value Name + 00007ff8d3df4b80 400000d 0 System.Int32 1 instance 0 <>1__state + 00007ff8d3e082c0 400000e 8 ...TaskMethodBuilder 1 instance 000001989f413e38 <>t__builder + 00007ff8d3dfea90 400000f 10 ...vices.TaskAwaiter 1 instance 000001989f413e40 <>u__1 + Continuation: 000001989f413e50 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]]) + GC roots: + Thread 2936c: + 000000071a37e050 00007ff8d3ac1657 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) [d:\repos\coreclr\src\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs @ 2977] + rbp+10: 000000071a37e0c0 + -> 000001989f413fa0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+
d__0, test]] + -> 000001989f413f30 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__1, test]] + -> 000001989f413ec0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__2, test]] + -> 000001989f413e50 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__3, test]] + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + HandleTable: + 000001989d8415f8 (pinned handle) + -> 00000198af3e1038 System.Object[] + -> 000001989f413410 System.Threading.TimerQueue[] + -> 000001989f413468 System.Threading.TimerQueue + -> 000001989f413330 System.Threading.TimerQueueTimer + -> 000001989f412e40 System.Threading.Tasks.Task+DelayPromise + -> 000001989f413de0 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+d__4, test]] + ... + + +The arguments in detail: + +-mt List only those state machine objects with the MethodTable given. +-type List only those state machine objects whose type name is a + substring match of the string provided. \\ diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp index 5637432d6346..6f5c1618da71 100644 --- a/src/ToolBox/SOS/Strike/strike.cpp +++ b/src/ToolBox/SOS/Strike/strike.cpp @@ -4098,6 +4098,188 @@ class DumpHeapImpl #endif }; +/**********************************************************************\ +* Routine Description: * +* * +* This function dumps async state machines on GC heap, * +* displaying details about each async operation found. * +* (May not work if GC is in progress.) * +* * +\**********************************************************************/ +DECLARE_API(DumpAsync) +{ + INIT_API(); + MINIDUMP_NOT_SUPPORTED(); + if (!g_snapshot.Build()) + { + ExtOut("Unable to build snapshot of the garbage collector state\n"); + return E_FAIL; + } + + try + { + // Process command-line arguments. + size_t nArg = 0; + TADDR mt = NULL; + ArrayHolder ansiType = NULL; + ArrayHolder type = NULL; + BOOL dml = FALSE; + CMDOption option[] = + { // name, vptr, type, hasValue + { "-mt", &mt, COHEX, TRUE }, // dump state machines only with a given MethodTable + { "-type", &ansiType, COSTRING, TRUE }, // dump state machines only that contain the specified type substring +#ifndef FEATURE_PAL + { "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language +#endif + }; + if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg)) + { + sos::Throw("Usage: DumpAsync [-mt MethodTableAddr] [-type TypeName] [-waiting]"); + } + if (nArg != 0) + { + sos::Throw("Unexpected command-line arguments."); + } + if (ansiType != NULL) + { + if (mt != NULL) + { + sos::Throw("Cannot specify both -mt and -type"); + } + + size_t ansiTypeLen = strlen(ansiType) + 1; + type = new WCHAR[ansiTypeLen]; + MultiByteToWideChar(CP_ACP, 0, ansiType, -1, type, (int)ansiTypeLen); + } + EnableDMLHolder dmlHolder(dml); + + // Display a message if the heap isn't verified. + sos::GCHeap gcheap; + if (!gcheap.AreGCStructuresValid()) + { + DisplayInvalidStructuresMessage(); + } + + // Print out header for the main line of each async state machine object. + ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s %s\n", "Address", "MT", "Size", "Name"); + + // Walk each heap object looking for async state machine objects. + int numStateMachines = 0; + for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr) + { + // Skip objects we know to be too small to possibly be a state machine. + // This helps filter out some caching data structures generated by the compiler. + if (itr->GetSize() <= 24) + { + continue; + } + + // Match only MTs the user requested. + if (mt != NULL && mt != itr->GetMT()) + { + continue; + } + + // Match only type name substrings the user requested. + if (type != NULL && _wcsstr(itr->GetTypeName(), type) == NULL) + { + continue; + } + + // Match only the two known state machine class name prefixes. + if (_wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1"), 79) != 0 && // Normal box. + _wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1"), 95) != 0) // Used when certain ETW events enabled. + { + continue; + } + + // Get the async state machine object's StateMachine field. If we can't, it's not + // an async state machine we can handle. + DacpFieldDescData stateMachineField; + int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField); + if (stateMachineFieldOffset <= 0) + { + continue; + } + + // Get the address and method table of the state machine. While it'll generally be a struct, + // it is valid for it to be a class, so we accomodate both. + BOOL bStateMachineIsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE; + CLRDATA_ADDRESS stateMachineAddr; + CLRDATA_ADDRESS stateMachineMT; + if (bStateMachineIsValueType) + { + stateMachineAddr = itr->GetAddress() + stateMachineFieldOffset; + stateMachineMT = stateMachineField.MTOfType; + } + else + { + MOVE(stateMachineAddr, itr->GetAddress() + stateMachineFieldOffset); + DacpObjectData objData; + if (objData.Request(g_sos, stateMachineAddr) != S_OK) + { + // Couldn't get the class-based object; just skip this state machine. + continue; + } + stateMachineMT = objData.MethodTable; // update from Canon to actual type + } + + // We now have a state machine that's passed all of our criteria. Print out its details. + + // Print out top level description of the state machine object. + ExtOut("#%d\n", numStateMachines); + numStateMachines++; + DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize()); + ExtOut(" %S\n", itr->GetTypeName()); + + // Output the state machine's name and fields. + DacpMethodTableData mtabledata; + DacpMethodTableFieldData vMethodTableFields; + if (mtabledata.Request(g_sos, stateMachineMT) == S_OK && + vMethodTableFields.Request(g_sos, stateMachineMT) == S_OK && + vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) + { + sos::MethodTable mt = (TADDR)stateMachineMT; + ExtOut("StateMachine: %S (%s)\n", mt.GetName(), bStateMachineIsValueType ? "struct" : "class"); + DisplayFields(stateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)stateMachineAddr, TRUE, bStateMachineIsValueType); + } + + // If the object already has a registered continuation, output it. + int iContOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("m_continuationObject")); + if (iContOffset > 0) + { + DWORD_PTR ContObjPtr; + MOVE(ContObjPtr, itr->GetAddress() + iContOffset); + DMLOut("Continuation: %s", DMLObject(ContObjPtr)); + if (sos::IsObject(ContObjPtr, false)) + { + sos::Object contObj = ContObjPtr; + ExtOut(" (%S)", contObj.GetTypeName()); + } + ExtOut("\n"); + } + + // Finally, output gcroots, as they can serve as call stacks, and also help to highlight + // state machines that aren't being kept alive. + ExtOut("GC roots:\n"); + IncrementIndent(); + GCRootImpl gcroot; + gcroot.PrintRootsForObject(*itr, FALSE, FALSE); + DecrementIndent(); + + ExtOut("\n"); + } + + ExtOut("\nFound %d state machines.\n", numStateMachines); + return S_OK; + } + catch (const sos::Exception &e) + { + ExtOut("%s\n", e.what()); + return E_FAIL; + } +} + /**********************************************************************\ * Routine Description: * * * diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp index 937d9a34f063..c7d78d26b482 100644 --- a/src/ToolBox/SOS/Strike/util.cpp +++ b/src/ToolBox/SOS/Strike/util.cpp @@ -1772,7 +1772,7 @@ int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL // 0 = field not found, // > 0 = offset to field from objAddr int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, - BOOL bFirst/*=TRUE*/) + BOOL bFirst/*=TRUE*/, DacpFieldDescData* pDacpFieldDescData/*=NULL*/) { #define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0) @@ -1795,7 +1795,7 @@ int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCW if (dmtd.ParentMethodTable) { DWORD retVal = GetObjFieldOffset (cdaObj, dmtd.ParentMethodTable, - wszFieldName, FALSE); + wszFieldName, FALSE, pDacpFieldDescData); if (retVal != 0) { // return in case of error or success. @@ -1820,6 +1820,10 @@ int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCW NameForToken_s (TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false); if (_wcscmp (wszFieldName, g_mdName) == 0) { + if (pDacpFieldDescData != NULL) + { + *pDacpFieldDescData = vFieldDesc; + } return offset; } numInstanceFields ++; diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h index 0b0b34480f22..fa26c3faa2d4 100644 --- a/src/ToolBox/SOS/Strike/util.h +++ b/src/ToolBox/SOS/Strike/util.h @@ -1387,7 +1387,7 @@ const char *ElementTypeName (unsigned type); void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE); int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE); -int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE); +int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE, DacpFieldDescData* pDacpFieldDescData=NULL); BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb); void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, diff --git a/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/ToolBox/SOS/lldbplugin/soscommand.cpp index 64d198cb5f63..bf2912249f7f 100644 --- a/src/ToolBox/SOS/lldbplugin/soscommand.cpp +++ b/src/ToolBox/SOS/lldbplugin/soscommand.cpp @@ -127,6 +127,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running."); interpreter.AddCommand("createdump", new sosCommand("CreateDump"), "Create a xplat minidump."); interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); + interpreter.AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap."); interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address."); interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects."); interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.");