From: Stephen Toub Date: Sun, 26 May 2019 02:15:01 +0000 (-0400) Subject: Two changes to !dumpasync (#286) X-Git-Tag: submit/tizen/20190813.035844~6^2^2~20 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c42750e3df352cf038c08c31dfd8ee026b0668c8;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Two changes to !dumpasync (#286) * 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. --- diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index c9e72d3e3..6c070d618 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -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 moduleList = ModuleFromName(const_cast("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 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()); diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index 9d9d753a7..03f618d78 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -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 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; } } diff --git a/src/SOS/Strike/util.h b/src/SOS/Strike/util.h index 031082de4..f32e4cefc 100644 --- a/src/SOS/Strike/util.h +++ b/src/SOS/Strike/util.h @@ -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);