diff options
author | Andy Ayers <andya@microsoft.com> | 2021-05-26 18:56:09 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-26 18:56:09 +0300 |
commit | 7f090c346afbedcfbff93c4002b577bab1988ecb (patch) | |
tree | 89a036378ac4d5212f02e6c69253ad30fb3b5ec2 /src/coreclr/ToolBox | |
parent | dc5a1c8f221ff3c38d61021e517719ac1aa24356 (diff) |
JIT: pgo/devirt diagnostic improvements (#53247)
Several changes to help better diagnose PGO and devirtualization issues:
* Report the source of the PGO data to the jit
* Report the reason for a devirtualization failure to the jit
* Add checking mode that compares result of devirtualization to class profile
* Add reporting mode to assess overall rates of devirtualization failure
when the jit has exact type information.
Also fix a loophole where in some case we'd still devirtualize if not
optimizing.
Note crossgen2 does not yet set devirtualization failure reasons.
Diffstat (limited to 'src/coreclr/ToolBox')
9 files changed, 63 insertions, 23 deletions
diff --git a/src/coreclr/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/coreclr/ToolBox/superpmi/mcs/verbdumpmap.cpp index 4880d1b6362..a86b85be14f 100644 --- a/src/coreclr/ToolBox/superpmi/mcs/verbdumpmap.cpp +++ b/src/coreclr/ToolBox/superpmi/mcs/verbdumpmap.cpp @@ -90,7 +90,8 @@ void DumpMap(int index, MethodContext* mc) bool hasEdgeProfile = false; bool hasClassProfile = false; bool hasLikelyClass = false; - if (mc->hasPgoData(hasEdgeProfile, hasClassProfile, hasLikelyClass)) + ICorJitInfo::PgoSource pgoSource = ICorJitInfo::PgoSource::Unknown; + if (mc->hasPgoData(hasEdgeProfile, hasClassProfile, hasLikelyClass, pgoSource)) { rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_PGO); @@ -108,6 +109,16 @@ void DumpMap(int index, MethodContext* mc) { rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_LIKELY_CLASS); } + + if (pgoSource == ICorJitInfo::PgoSource::Static) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_STATIC_PROFILE); + } + + if (pgoSource == ICorJitInfo::PgoSource::Dynamic) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_DYNAMIC_PROFILE); + } } printf(", %s\n", SpmiDumpHelper::DumpJitFlags(rawFlags).c_str()); diff --git a/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp b/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp index 8c0f855d5c5..eaf208508b7 100644 --- a/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp +++ b/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp @@ -30,7 +30,8 @@ int verbJitFlags::DoWork(const char* nameOfInput) bool hasEdgeProfile = false; bool hasClassProfile = false; bool hasLikelyClass = false; - if (mc->hasPgoData(hasEdgeProfile, hasClassProfile, hasLikelyClass)) + ICorJitInfo::PgoSource pgoSource = ICorJitInfo::PgoSource::Unknown; + if (mc->hasPgoData(hasEdgeProfile, hasClassProfile, hasLikelyClass, pgoSource)) { rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_PGO); @@ -48,6 +49,16 @@ int verbJitFlags::DoWork(const char* nameOfInput) { rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_LIKELY_CLASS); } + + if (pgoSource == ICorJitInfo::PgoSource::Static) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_STATIC_PROFILE); + } + + if (pgoSource == ICorJitInfo::PgoSource::Dynamic) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_DYNAMIC_PROFILE); + } } int index = flagMap.GetIndex(rawFlags); diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/agnostic.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/agnostic.h index 59cc7857f4c..4b47339b352 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/agnostic.h @@ -491,6 +491,7 @@ struct Agnostic_GetPgoInstrumentationResults DWORD data_index; DWORD dataByteCount; DWORD result; + DWORD pgoSource; }; struct Agnostic_GetProfilingHandle @@ -567,6 +568,7 @@ struct Agnostic_ResolveVirtualMethodResult DWORDLONG devirtualizedMethod; bool requiresInstMethodTableArg; DWORDLONG exactContext; + DWORD detail; }; struct ResolveTokenValue diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index dbfacdaa588..238a50494d6 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -3175,14 +3175,15 @@ void MethodContext::recResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info result.devirtualizedMethod = CastHandle(info->devirtualizedMethod); result.requiresInstMethodTableArg = info->requiresInstMethodTableArg; result.exactContext = CastHandle(info->exactContext); + result.detail = (DWORD) info->detail; ResolveVirtualMethod->Add(key, result); DEBUG_REC(dmpResolveVirtualMethod(key, result)); } void MethodContext::dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethodKey& key, const Agnostic_ResolveVirtualMethodResult& result) { - printf("ResolveVirtualMethod virtMethod-%016llX, objClass-%016llX, context-%016llX :: returnValue-%d, devirtMethod-%016llX, requiresInstArg-%d, exactContext-%016llX", - key.virtualMethod, key.objClass, key.context, result.returnValue, result.devirtualizedMethod, result.requiresInstMethodTableArg, result.exactContext); + printf("ResolveVirtualMethod virtMethod-%016llX, objClass-%016llX, context-%016llX :: returnValue-%d, devirtMethod-%016llX, requiresInstArg-%d, exactContext-%016llX, detail-%d", + key.virtualMethod, key.objClass, key.context, result.returnValue, result.devirtualizedMethod, result.requiresInstMethodTableArg, result.exactContext, result.detail); } bool MethodContext::repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info) @@ -3201,6 +3202,7 @@ bool MethodContext::repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info info->devirtualizedMethod = (CORINFO_METHOD_HANDLE) result.devirtualizedMethod; info->requiresInstMethodTableArg = result.requiresInstMethodTableArg; info->exactContext = (CORINFO_CONTEXT_HANDLE) result.exactContext; + info->detail = (CORINFO_DEVIRTUALIZATION_DETAIL) result.detail; return result.returnValue; } @@ -5543,6 +5545,7 @@ void MethodContext::recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, + ICorJitInfo::PgoSource* pPgoSource, HRESULT result) { if (GetPgoInstrumentationResults == nullptr) @@ -5571,6 +5574,7 @@ void MethodContext::recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd value.data_index = GetPgoInstrumentationResults->AddBuffer((unsigned char*)*pInstrumentationData, (unsigned)maxOffset); value.dataByteCount = (unsigned)maxOffset; value.result = (DWORD)result; + value.pgoSource = (DWORD)*pPgoSource; DWORDLONG key = CastHandle(ftnHnd); GetPgoInstrumentationResults->Add(key, value); @@ -5578,8 +5582,8 @@ void MethodContext::recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd } void MethodContext::dmpGetPgoInstrumentationResults(DWORDLONG key, const Agnostic_GetPgoInstrumentationResults& value) { - printf("GetPgoInstrumentationResults key ftn-%016llX, value res-%08X schemaCnt-%u profileBufSize-%u schema{", - key, value.result, value.countSchemaItems, value.dataByteCount); + printf("GetPgoInstrumentationResults key ftn-%016llX, value res-%08X schemaCnt-%u profileBufSize-%u source-%u schema{", + key, value.result, value.countSchemaItems, value.dataByteCount, value.pgoSource); if (value.countSchemaItems > 0) { @@ -5639,7 +5643,8 @@ void MethodContext::dmpGetPgoInstrumentationResults(DWORDLONG key, const Agnosti HRESULT MethodContext::repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, - BYTE** pInstrumentationData) + BYTE** pInstrumentationData, + ICorJitInfo::PgoSource* pPgoSource) { DWORDLONG key = CastHandle(ftnHnd); AssertMapAndKeyExist(GetPgoInstrumentationResults, key, ": key %016llX", key); @@ -5649,6 +5654,7 @@ HRESULT MethodContext::repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftn *pCountSchemaItems = (UINT32)tempValue.countSchemaItems; *pInstrumentationData = (BYTE*)GetPgoInstrumentationResults->GetBuffer(tempValue.data_index); + *pPgoSource = (ICorJitInfo::PgoSource)tempValue.pgoSource; ICorJitInfo::PgoInstrumentationSchema* pOutSchema = (ICorJitInfo::PgoInstrumentationSchema*)AllocJitTempBuffer(tempValue.countSchemaItems * sizeof(ICorJitInfo::PgoInstrumentationSchema)); @@ -6817,7 +6823,8 @@ int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len, bool igno ICorJitInfo::PgoInstrumentationSchema* schema = nullptr; UINT32 schemaCount = 0; BYTE* schemaData = nullptr; - HRESULT pgoHR = repGetPgoInstrumentationResults(pInfo->ftn, &schema, &schemaCount, &schemaData); + ICorJitInfo::PgoSource pgoSource = ICorJitInfo::PgoSource::Unknown; + HRESULT pgoHR = repGetPgoInstrumentationResults(pInfo->ftn, &schema, &schemaCount, &schemaData, &pgoSource); size_t minOffset = (size_t) ~0; size_t maxOffset = 0; @@ -6905,7 +6912,7 @@ int MethodContext::dumpMD5HashToBuffer(BYTE* pBuffer, int bufLen, char* hash, in return m_hash.HashBuffer(pBuffer, bufLen, hash, hashLen); } -bool MethodContext::hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool& hasLikelyClass) +bool MethodContext::hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool& hasLikelyClass, ICorJitInfo::PgoSource& pgoSource) { hasEdgeProfile = false; hasClassProfile = false; @@ -6922,7 +6929,7 @@ bool MethodContext::hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool ICorJitInfo::PgoInstrumentationSchema* schema = nullptr; UINT32 schemaCount = 0; BYTE* schemaData = nullptr; - HRESULT pgoHR = repGetPgoInstrumentationResults(info.ftn, &schema, &schemaCount, &schemaData); + HRESULT pgoHR = repGetPgoInstrumentationResults(info.ftn, &schema, &schemaCount, &schemaData, &pgoSource); if (SUCCEEDED(pgoHR)) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index c9cfddd1167..6970b163cde 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -50,7 +50,9 @@ enum EXTRA_JIT_FLAGS HAS_PGO = 63, HAS_EDGE_PROFILE = 62, HAS_CLASS_PROFILE = 61, - HAS_LIKELY_CLASS = 60 + HAS_LIKELY_CLASS = 60, + HAS_STATIC_PROFILE = 59, + HAS_DYNAMIC_PROFILE = 58 }; // Asserts to catch changes in corjit flags definitions. @@ -59,6 +61,8 @@ static_assert((int)EXTRA_JIT_FLAGS::HAS_PGO == (int)CORJIT_FLAGS::CorJitFlag::CO static_assert((int)EXTRA_JIT_FLAGS::HAS_EDGE_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED35, "Jit Flags Mismatch"); static_assert((int)EXTRA_JIT_FLAGS::HAS_CLASS_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED34, "Jit Flags Mismatch"); static_assert((int)EXTRA_JIT_FLAGS::HAS_LIKELY_CLASS == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED33, "Jit Flags Mismatch"); +static_assert((int)EXTRA_JIT_FLAGS::HAS_STATIC_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED32, "Jit Flags Mismatch"); +static_assert((int)EXTRA_JIT_FLAGS::HAS_DYNAMIC_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED31, "Jit Flags Mismatch"); class MethodContext { @@ -99,7 +103,7 @@ public: int dumpMethodIdentityInfoToBuffer(char* buff, int len, bool ignoreMethodName = false, CORINFO_METHOD_INFO* optInfo = nullptr, unsigned optFlags = 0); int dumpMethodMD5HashToBuffer(char* buff, int len, bool ignoreMethodName = false, CORINFO_METHOD_INFO* optInfo = nullptr, unsigned optFlags = 0); - bool hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool& hasLikelyClass); + bool hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile, bool& hasLikelyClass, ICorJitInfo::PgoSource& pgoSource); void recGlobalContext(const MethodContext& other); @@ -699,9 +703,9 @@ public: void dmpAllocPgoInstrumentationBySchema(DWORDLONG key, const Agnostic_AllocPgoInstrumentationBySchema& value); HRESULT repAllocPgoInstrumentationBySchema(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema* pSchema, UINT32 countSchemaItems, BYTE** pInstrumentationData); - void recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, HRESULT result); + void recGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, ICorJitInfo::PgoSource* pPgoSource, HRESULT result); void dmpGetPgoInstrumentationResults(DWORDLONG key, const Agnostic_GetPgoInstrumentationResults& value); - HRESULT repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData); + HRESULT repGetPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, UINT32* pCountSchemaItems, BYTE** pInstrumentationData, ICorJitInfo::PgoSource* pPgoSource); void recMergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2, CORINFO_CLASS_HANDLE result); void dmpMergeClasses(DLDL key, DWORDLONG value); diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index d7a3b48fdac..cd601a26213 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -2067,11 +2067,12 @@ HRESULT interceptor_ICJI::allocPgoInstrumentationBySchema(CORINFO_METHOD_HANDLE HRESULT interceptor_ICJI::getPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, PgoInstrumentationSchema **pSchema, // pointer to the schema table which describes the instrumentation results (pointer will not remain valid after jit completes) uint32_t * pCountSchemaItems, // pointer to the count schema items - uint8_t ** pInstrumentationData) // pointer to the actual instrumentation data (pointer will not remain valid after jit completes) + uint8_t ** pInstrumentationData, // pointer to the actual instrumentation data (pointer will not remain valid after jit completes) + PgoSource* pPgoSource) { mc->cr->AddCall("getPgoInstrumentationResults"); - HRESULT temp = original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData); - mc->recGetPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, temp); + HRESULT temp = original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, pPgoSource); + mc->recGetPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, pPgoSource, temp); return temp; } diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 46ab7cb8489..0c9a8391148 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -1329,10 +1329,11 @@ JITINTERFACE_HRESULT interceptor_ICJI::getPgoInstrumentationResults( CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, uint32_t* pCountSchemaItems, - uint8_t** pInstrumentationData) + uint8_t** pInstrumentationData, + ICorJitInfo::PgoSource* pgoSource) { mcs->AddCall("getPgoInstrumentationResults"); - return original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData); + return original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, pgoSource); } JITINTERFACE_HRESULT interceptor_ICJI::allocPgoInstrumentationBySchema( diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 1ab397077f0..1fd27c307ac 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -1164,9 +1164,10 @@ JITINTERFACE_HRESULT interceptor_ICJI::getPgoInstrumentationResults( CORINFO_METHOD_HANDLE ftnHnd, ICorJitInfo::PgoInstrumentationSchema** pSchema, uint32_t* pCountSchemaItems, - uint8_t** pInstrumentationData) + uint8_t** pInstrumentationData, + ICorJitInfo::PgoSource* pgoSource) { - return original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData); + return original_ICorJitInfo->getPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, pgoSource); } JITINTERFACE_HRESULT interceptor_ICJI::allocPgoInstrumentationBySchema( diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index ce20edd00be..f1eb73b8915 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -1815,10 +1815,12 @@ HRESULT MyICJI::allocPgoInstrumentationBySchema(CORINFO_METHOD_HANDLE ftnHnd, HRESULT MyICJI::getPgoInstrumentationResults(CORINFO_METHOD_HANDLE ftnHnd, PgoInstrumentationSchema **pSchema, // pointer to the schema table which describes the instrumentation results (pointer will not remain valid after jit completes) uint32_t * pCountSchemaItems, // pointer to the count schema items - uint8_t ** pInstrumentationData) // pointer to the actual instrumentation data (pointer will not remain valid after jit completes) + uint8_t ** pInstrumentationData, // pointer to the actual instrumentation data (pointer will not remain valid after jit completes) + PgoSource* pPgoSource) + { jitInstance->mc->cr->AddCall("getPgoInstrumentationResults"); - return jitInstance->mc->repGetPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData); + return jitInstance->mc->repGetPgoInstrumentationResults(ftnHnd, pSchema, pCountSchemaItems, pInstrumentationData, pPgoSource); } // Associates a native call site, identified by its offset in the native code stream, with |