diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6646bd585cf29a..0de388de0a6adb 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -428,7 +428,7 @@ RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux and macOS to enable writing /tmp/perf-$pid.map. It is disabled by default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapStubGranularity, W("PerfMapStubGranularity"), 0, "Report stubs with varying amounts of granularity (low bit being zero indicates attempt to group all stubs of a type together) (second lowest bit being non-zero records stubs at individual allocation sites, which is more expensive, but also more accurate).") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapStubGranularity, W("PerfMapStubGranularity"), 0, "Report stubs with varying amounts of granularity (low bit being zero indicates attempt to group all stubs of a type together) (second lowest bit being non-zero records stubs at individual allocation sites, which is more expensive, but also more accurate) (third lowest bit disables stub logging).") #endif RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "") diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index e94126817b44d1..928cc2ac84e729 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -305,7 +305,11 @@ static uint32_t ds_rt_enable_perfmap (uint32_t type) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + MODE_PREEMPTIVE; + } + CONTRACTL_END; #ifdef FEATURE_PERFMAP PerfMap::PerfMapType perfMapType = (PerfMap::PerfMapType)type; diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 5b1932c80f060f..edf11d2d169f33 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -32,6 +32,7 @@ PerfMap * PerfMap::s_Current = nullptr; bool PerfMap::s_ShowOptimizationTiers = false; bool PerfMap::s_GroupStubsOfSameType = false; bool PerfMap::s_IndividualAllocationStubReporting = false; +bool PerfMap::s_LogStubs = false; unsigned PerfMap::s_StubsMapped = 0; CrstStatic PerfMap::s_csPerfMap; @@ -46,7 +47,15 @@ void PerfMap::Initialize() { LIMITED_METHOD_CONTRACT; - s_csPerfMap.Init(CrstPerfMap); + // Use CRST_UNSAFE_ANYMODE to avoid a GC-mode toggle deadlock: callers such as + // CodeFragmentHeap::RealAllocAlignedMem hold CRST_UNSAFE_ANYMODE locks in cooperative + // mode. A default Crst here would toggle cooperative->preemptive->acquire->cooperative, + // and the post-acquire DisablePreemptiveGC can block on a pending GC suspension, + // forming a deadlock cycle with threads waiting on the outer UNSAFE_ANYMODE lock. + // All data accessed under this lock is native (FILE*, fd, SString) so holding it + // in cooperative mode does not introduce new GC-safety issues. Doing I/O + // in cooperative mode is still less than ideal. + s_csPerfMap.Init(CrstPerfMap, CrstFlags(CRST_UNSAFE_ANYMODE)); PerfMapType perfMapType = (PerfMapType)CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled); PerfMap::Enable(perfMapType, false); @@ -76,11 +85,16 @@ void PerfMap::InitializeConfiguration() DWORD granularity = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapStubGranularity); s_GroupStubsOfSameType = (granularity & 1) != 1; s_IndividualAllocationStubReporting = (granularity & 2) != 0; + s_LogStubs = (granularity & 4) == 0; } void PerfMap::Enable(PerfMapType type, bool sendExisting) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + MODE_PREEMPTIVE; + } + CONTRACTL_END; if (type == PerfMapType::DISABLED) { @@ -308,8 +322,6 @@ void PerfMap::WriteLine(SString& line) void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig) { - LIMITED_METHOD_CONTRACT; - CONTRACTL{ THROWS; GC_NOTRIGGER; @@ -363,7 +375,12 @@ void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t cod // Log a pre-compiled method to the perfmap. void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + THROWS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; if (!s_enabled) { @@ -399,14 +416,14 @@ void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) if (methodRegionInfo.coldSize > 0) { - CrstHolder ch(&(s_csPerfMap)); - if (s_ShowOptimizationTiers) { pMethod->GetFullMethodInfo(name); name.Append(W("[PreJit-cold]")); } + CrstHolder ch(&(s_csPerfMap)); + PAL_PerfJitDump_LogMethod((void*)methodRegionInfo.coldStartAddress, methodRegionInfo.coldSize, name.GetUTF8(), nullptr, nullptr); } } @@ -416,9 +433,14 @@ void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) // Log a set of stub to the map. void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize, PerfMapStubType stubAllocationType) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; - if (!s_enabled) + if (!s_enabled || !s_LogStubs) { return; } diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 04ef2598b692f5..85dd86dc81e63e 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -19,6 +19,52 @@ enum class PerfMapStubType Individual }; +#ifndef FEATURE_PERFMAP + +class PerfMap +{ +public: + static bool IsEnabled() + { +#ifdef DEBUG + return true; +#else + return false; +#endif + } + static void LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig) + { + CONTRACTL + { + THROWS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + } + + static void LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) + { + CONTRACTL + { + THROWS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + } + + static void LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize, PerfMapStubType stubAllocationType) + { + CONTRACTL + { + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + } +}; + +#else // FEATURE_PERFMAP + class PerfMap { private: @@ -36,6 +82,7 @@ class PerfMap // Indicate current stub granularity rules static bool s_GroupStubsOfSameType; static bool s_IndividualAllocationStubReporting; + static bool s_LogStubs; // If false, do not log stubs at all // Set to true if an error is encountered when writing to the file. static unsigned s_StubsMapped; @@ -112,4 +159,5 @@ class PerfMap static bool LowGranularityStubs() { return !s_IndividualAllocationStubReporting; } }; +#endif // FEATURE_PERFMAP #endif // PERFPID_H diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 41edef935b716e..66d01897985d8a 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -9,9 +9,7 @@ #include "CachedInterfaceDispatch.h" #include "comdelegate.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE @@ -1129,7 +1127,19 @@ PCODE VirtualCallStubManager::GetCallStub(DispatchToken token) { if ((stub = (PCODE)(lookups->Find(&probeL))) == CALL_STUB_EMPTY_ENTRY) { - LookupHolder *pLookupHolder = GenerateLookupStub(addrOfResolver, token.To_SIZE_T()); + LookupHolder *pLookupHolder; + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pLookupHolder = GenerateLookupStub(addrOfResolver, token.To_SIZE_T()); + } + + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = lookups->SetUpProber(token.To_SIZE_T(), 0, &probeL); + _ASSERTE(success); + } stub = (PCODE) (lookups->Add((size_t)(pLookupHolder->stub()->entryPoint()), &probeL)); } } @@ -1160,7 +1170,20 @@ PCODE VirtualCallStubManager::GetVTableCallStub(DWORD slot) { if ((stub = (PCODE)(vtableCallers->Find(&probe))) == CALL_STUB_EMPTY_ENTRY) { - VTableCallHolder *pHolder = GenerateVTableCallStub(slot); + VTableCallHolder *pHolder; + + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pHolder = GenerateVTableCallStub(slot); + } + + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = vtableCallers->SetUpProber(DispatchToken::CreateDispatchToken(slot).To_SIZE_T(), 0, &probe); + _ASSERTE(success); + } stub = (PCODE)(vtableCallers->Add((size_t)(pHolder->stub()->entryPoint()), &probe)); } } @@ -1193,9 +1216,7 @@ VTableCallHolder* VirtualCallStubManager::GenerateVTableCallStub(DWORD slot) LOG((LF_STUBS, LL_INFO10000, "GenerateVTableCallStub for slot " FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(slot), DBG_ADDR(pHolder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateVTableCallStub", (PCODE)pHolder->stub(), pHolder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN(pHolder); } @@ -2047,14 +2068,24 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, } #endif // TARGET_X86 && !UNIX_X86_ABI - pResolveHolder = GenerateResolveStub(pResolverFcn, - pBackPatchFcn, - token.To_SIZE_T() + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pResolveHolder = GenerateResolveStub(pResolverFcn, + pBackPatchFcn, + token.To_SIZE_T() #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) - , stackArgumentsSize + , stackArgumentsSize #endif ); + } + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = resolvers->SetUpProber(token.To_SIZE_T(), 0, &probeR); + _ASSERTE(success); + } // Add the resolve entrypoint into the cache. //@TODO: Can we store a pointer to the holder rather than the entrypoint? resolvers->Add((size_t)(pResolveHolder->stub()->resolveEntryPoint()), &probeR); @@ -2088,9 +2119,12 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, if (addrOfDispatch == CALL_STUB_EMPTY_ENTRY) { PCODE addrOfFail = pResolveHolder->stub()->failEntryPoint(); - bool reenteredCooperativeGCMode = false; - pDispatchHolder = GenerateDispatchStub( - target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pDispatchHolder = GenerateDispatchStub( + target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + } if (reenteredCooperativeGCMode) { // The prober may have been invalidated by reentering cooperative GC mode, reset it @@ -2203,9 +2237,12 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, // so we may have to create it now ResolveHolder* pResolveHolder = ResolveHolder::FromResolveEntry(pCallSite->GetSiteTarget()); PCODE addrOfFail = pResolveHolder->stub()->failEntryPoint(); - bool reenteredCooperativeGCMode = false; - pDispatchHolder = GenerateDispatchStub( - target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pDispatchHolder = GenerateDispatchStub( + target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + } if (reenteredCooperativeGCMode) { // The prober may have been invalidated by reentering cooperative GC mode, reset it @@ -2791,7 +2828,6 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad PRECONDITION(addrOfFail != NULL); PRECONDITION(CheckPointer(pMTExpected)); PRECONDITION(pMayHaveReenteredCooperativeGCMode != nullptr); - PRECONDITION(!*pMayHaveReenteredCooperativeGCMode); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; @@ -2854,9 +2890,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -2880,7 +2914,6 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE PRECONDITION(addrOfFail != NULL); PRECONDITION(CheckPointer(pMTExpected)); PRECONDITION(pMayHaveReenteredCooperativeGCMode != nullptr); - PRECONDITION(!*pMayHaveReenteredCooperativeGCMode); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; @@ -2915,9 +2948,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -3013,9 +3044,7 @@ ResolveHolder *VirtualCallStubManager::GenerateResolveStub(PCODE addr LOG((LF_STUBS, LL_INFO10000, "GenerateResolveStub for token" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateResolveStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -3046,9 +3075,7 @@ LookupHolder *VirtualCallStubManager::GenerateLookupStub(PCODE addrOfResolver, s LOG((LF_STUBS, LL_INFO10000, "GenerateLookupStub for token" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateLookupStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -3076,8 +3103,12 @@ ResolveCacheElem *VirtualCallStubManager::GenerateResolveCacheElem(void *addrOfC CONSISTENCY_CHECK(CheckPointer(pMTExpected)); //allocate from the requisite heap and set the appropriate fields - ResolveCacheElem *e = (ResolveCacheElem*) (void*) + ResolveCacheElem *e; + { + GCX_NOTRIGGER(); + e = (ResolveCacheElem*) (void*) cache_entry_heap->AllocAlignedMem(sizeof(ResolveCacheElem), CODE_SIZE_ALIGN); + } e->pMT = pMTExpected; e->token = token;