Two changes to !dumpasync (#286)
authorStephen Toub <stoub@microsoft.com>
Sun, 26 May 2019 02:15:01 +0000 (22:15 -0400)
committerGitHub <noreply@github.com>
Sun, 26 May 2019 02:15:01 +0000 (22:15 -0400)
* Display state machine fields in DumpAsync for -stacks

Currently the DumpAsync -fields option just impacts whether the state machine fields are output for the top-level state machines.  We received feedback that it would be more expected for -fields to also cause the fields to be output for all state machines listed as part of continuation chains.  This does that.

* Improve performance of !dumpasync SOS command

A significant majority of the time spent in dumpasync was in getting the type name of each object in order to compare it to "AsyncStateMachineBox".  This switches to comparing by module and mdTypeDef.  On an ~1GB dump, the time to execute !dumpasync dropped from 43 to 11 seconds on my machine.

src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h

index c9e72d3e3369d75b1615c542ddb16fcf5442551a..6c070d618404737cd9ab883d824ccb9839bffdcf 100644 (file)
@@ -4416,6 +4416,36 @@ void ExtOutTaskStateFlagsDescription(int stateFlags)
     ExtOut("\n");
 }
 
+void ExtOutStateMachineFields(AsyncRecord& ar)
+{
+       DacpMethodTableData mtabledata;
+       DacpMethodTableFieldData vMethodTableFields;
+       if (mtabledata.Request(g_sos, ar.StateMachineMT) == S_OK &&
+               vMethodTableFields.Request(g_sos, ar.StateMachineMT) == S_OK &&
+               vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+       {
+               DisplayFields(ar.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)ar.StateMachineAddr, TRUE, ar.IsValueType);
+       }
+}
+
+void FindStateMachineTypes(DWORD_PTR* corelibModule, mdTypeDef* stateMachineBox, mdTypeDef* debugStateMachineBox)
+{
+    int numModule;
+    ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(const_cast<LPSTR>("System.Private.CoreLib.dll"), &numModule);
+    if (moduleList != NULL && numModule == 1)
+    {
+        *corelibModule = moduleList[0];
+        GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1", stateMachineBox);
+        GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1", debugStateMachineBox);
+    }
+    else
+    {
+        *corelibModule = 0;
+        *stateMachineBox = 0;
+        *debugStateMachineBox = 0;
+    }
+}
+
 DECLARE_API(DumpAsync)
 {
     INIT_API();
@@ -4479,6 +4509,11 @@ DECLARE_API(DumpAsync)
             DisplayInvalidStructuresMessage();
         }
 
+        // Find the state machine types
+        DWORD_PTR corelibModule;
+        mdTypeDef stateMachineBoxMd, debugStateMachineBoxMd;
+        FindStateMachineTypes(&corelibModule, &stateMachineBoxMd, &debugStateMachineBoxMd);
+
         // Walk each heap object looking for async state machine objects.  As we're targeting .NET Core 2.1+, all such objects
         // will be Task or Task-derived types.
         std::map<CLRDATA_ADDRESS, AsyncRecord> asyncRecords;
@@ -4504,8 +4539,10 @@ DECLARE_API(DumpAsync)
             {
                 // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1
                 // that's used when certain ETW events are set.
-                if (_wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1"), 79) != 0 &&
-                    _wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1"), 95) != 0)
+                DacpMethodTableData mtdata;
+                if (mtdata.Request(g_sos, TO_TADDR(itr->GetMT())) != S_OK ||
+                    mtdata.Module != corelibModule ||
+                    (mtdata.cl != stateMachineBoxMd && mtdata.cl != debugStateMachineBoxMd))
                 {
                     continue;
                 }
@@ -4684,19 +4721,14 @@ DECLARE_API(DumpAsync)
 
             // Output the state machine's details as a single line.
             sos::Object obj = TO_TADDR(arIt->second.Address);
-            DacpMethodTableData mtabledata;
-            DacpMethodTableFieldData vMethodTableFields;
-            if (arIt->second.IsStateMachine &&
-                mtabledata.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
-                vMethodTableFields.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
-                vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+            if (arIt->second.IsStateMachine)
             {
                 // This has a StateMachine.  Output its details.
                 sos::MethodTable mt = TO_TADDR(arIt->second.StateMachineMT);
                 DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize());
                 if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second));
                 ExtOut("%10d %S\n", arIt->second.StateValue, mt.GetName());
-                if (dumpFields) DisplayFields(arIt->second.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)arIt->second.StateMachineAddr, TRUE, arIt->second.IsValueType);
+                if (dumpFields) ExtOutStateMachineFields(arIt->second);
             }
             else
             {
@@ -4759,7 +4791,8 @@ DECLARE_API(DumpAsync)
                             sos::MethodTable contMT = TO_TADDR(contAsyncRecord->second.StateMachineMT);
                             if (contAsyncRecord->second.IsStateMachine) ExtOut("(%d) ", contAsyncRecord->second.StateValue);
                             ExtOut("%S\n", contMT.GetName());
-                        }
+                                                       if (contAsyncRecord->second.IsStateMachine && dumpFields) ExtOutStateMachineFields(contAsyncRecord->second);
+                                               }
                         else
                         {
                             ExtOut("%S\n", cont.GetTypeName());
index 9d9d753a7dcb5fa71269c50e7b7eb15aa772e220..03f618d78dd750d3648c1827a8b706a934e5e0e4 100644 (file)
@@ -2618,8 +2618,12 @@ HRESULT GetModuleFromAddress(___in CLRDATA_ADDRESS peAddress, ___out IXCLRDataMo
 *    Find the EE data given a name.                                    *  
 *                                                                      *
 \**********************************************************************/
-void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
+void GetInfoFromName(DWORD_PTR ModulePtr, const char* name, mdTypeDef* retMdTypeDef)
 {
+    DWORD_PTR ignoredModuleInfoRet = NULL;
+    if (retMdTypeDef)
+        *retMdTypeDef = 0;
+
     ToRelease<IMetaDataImport> pImport = MDImportForModule (ModulePtr);    
     if (pImport == 0)
         return;
@@ -2644,13 +2648,13 @@ void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
             BOOL fStatus = FALSE;
             while (ModuleDefinition->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
             {
-                if (fStatus)
+                if (fStatus && !retMdTypeDef)
                     ExtOut("-----------------------\n");
 
                 mdTypeDef token;
                 if (pMeth->GetTokenAndScope(&token, NULL) == S_OK)
                 {
-                    GetInfoFromModule(ModulePtr, token);
+                    GetInfoFromModule(ModulePtr, token, retMdTypeDef ? &ignoredModuleInfoRet : NULL);
                     fStatus = TRUE;
                 }
                 pMeth->Release();
@@ -2679,7 +2683,10 @@ void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
     // @todo:  Handle Nested classes correctly.
     if (SUCCEEDED (pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
     {
-        GetInfoFromModule(ModulePtr, cl);
+        if (retMdTypeDef)
+            *retMdTypeDef = cl;
+        
+        GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL);
         return;
     }
     
@@ -2696,6 +2703,9 @@ void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
     // @todo:  Handle Nested classes correctly.
     if (SUCCEEDED(pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
     {
+        if (retMdTypeDef)
+            *retMdTypeDef = cl;
+
         mdMethodDef token;
         ULONG cTokens;
         HCORENUM henum = NULL;
@@ -2706,8 +2716,8 @@ void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
                                                      &token, 1, &cTokens))
             && cTokens == 1)
         {
-            ExtOut("Member (mdToken token) of\n");
-            GetInfoFromModule(ModulePtr, cl);
+            if (!retMdTypeDef) ExtOut("Member (mdToken token) of\n");
+            GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL);
             return;
         }
 
@@ -2717,8 +2727,8 @@ void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
                                                      &token, 1, &cTokens))
             && cTokens == 1)
         {
-            ExtOut("Field (mdToken token) of\n");
-            GetInfoFromModule(ModulePtr, cl);
+            if (!retMdTypeDef) ExtOut("Field (mdToken token) of\n");
+            GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL);
             return;
         }
     }
index 031082de4f962fd8a41e3fcfa759e70fabfb3e02..f32e4cefc204f0e3c710264d5614c10106fd1846 100644 (file)
@@ -1871,7 +1871,7 @@ bool IsMetadataMemory(CLRDATA_ADDRESS address, ULONG32 size);
  */
 DWORD_PTR *ModuleFromName(__in_opt LPSTR name, int *numModules);
 HRESULT GetModuleFromAddress(___in CLRDATA_ADDRESS peAddress, ___out IXCLRDataModule** ppModule);
-void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name);
+void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name, mdTypeDef* retMdTypeDef=NULL);
 void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL);