From 3db5d4f605ef91eefc117fa8db47fdf74bf1a903 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 4 Jun 2026 15:50:19 -0700 Subject: [PATCH 1/8] Add logic to handle some simple cases where moving to preemptive is more ideal for perfmap scenarios --- src/coreclr/vm/perfmap.cpp | 14 +++-- src/coreclr/vm/perfmap.h | 47 +++++++++++++++++ src/coreclr/vm/virtualcallstub.cpp | 85 ++++++++++++++++++++---------- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 5b1932c80f060f..5bbb01c592b62f 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -46,7 +46,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 -- the I/O was already + // performed in cooperative mode with the previous default Crst. + s_csPerfMap.Init(CrstPerfMap, CrstFlags(CRST_UNSAFE_ANYMODE)); PerfMapType perfMapType = (PerfMapType)CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled); PerfMap::Enable(perfMapType, false); @@ -399,14 +407,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); } } diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 04ef2598b692f5..361252b9a6ab7d 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_PREEMPTIVE; + } + CONTRACTL_END; + } +}; + +#else // FEATURE_PERFMAP + class PerfMap { private: @@ -112,4 +158,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; From d22cd332f319b31356c7eb9497a12fc891e6f881 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 13:59:42 -0700 Subject: [PATCH 2/8] Code review feedback --- .../vm/eventing/eventpipe/ds-rt-coreclr.h | 6 +++++- src/coreclr/vm/perfmap.cpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) 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 5bbb01c592b62f..45221fcd98e05b 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -52,8 +52,8 @@ void PerfMap::Initialize() // 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 -- the I/O was already - // performed in cooperative mode with the previous default Crst. + // 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); @@ -88,7 +88,11 @@ void PerfMap::InitializeConfiguration() void PerfMap::Enable(PerfMapType type, bool sendExisting) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + MODE_PREEMPTIVE; + } + CONTRACTL_END; if (type == PerfMapType::DISABLED) { @@ -371,7 +375,11 @@ 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 + { + MODE_PREEMPTIVE; + } + CONTRACTL_END; if (!s_enabled) { From c18a4d3e9cd8656984376d7b48c62806a186fcc1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 15:17:31 -0700 Subject: [PATCH 3/8] Add feature to disable reporting of stubs via PerfMap Correct LogStubs contract for use on Windows --- src/coreclr/vm/perfmap.cpp | 4 +++- src/coreclr/vm/perfmap.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 45221fcd98e05b..b40b67ab220a2d 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; @@ -84,6 +85,7 @@ 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) @@ -434,7 +436,7 @@ void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, { LIMITED_METHOD_CONTRACT; - if (!s_enabled) + if (!s_enabled || !s_LogStubs) { return; } diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 361252b9a6ab7d..85dd86dc81e63e 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -57,7 +57,7 @@ class PerfMap CONTRACTL { GC_NOTRIGGER; - MODE_PREEMPTIVE; + MODE_ANY; } CONTRACTL_END; } @@ -82,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; From 013f5e09728b71996bcb8f89280bbbf889fdda83 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 15:29:14 -0700 Subject: [PATCH 4/8] Reverse polarity of new flag --- src/coreclr/vm/perfmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index b40b67ab220a2d..ebcae2a2ebae48 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -85,7 +85,7 @@ 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; + s_LogStubs = (granularity & 4) == 0; } void PerfMap::Enable(PerfMapType type, bool sendExisting) From 78a75a977ebb95b66480ffc18b2d9fedeaf0f324 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 15:30:50 -0700 Subject: [PATCH 5/8] And update env flag doc in clrconfigvalues.h --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6646bd585cf29a..01055c08ba7c90 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 disable stub logging).") #endif RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "") From 50474466c023ca6ddebb3e6038c951a1e034d77c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 8 Jun 2026 10:23:07 -0700 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Jan Kotas --- src/coreclr/vm/perfmap.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index ebcae2a2ebae48..7c34f53655e049 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -323,7 +323,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; @@ -379,6 +378,7 @@ void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) { CONTRACTL { + THROWS; MODE_PREEMPTIVE; } CONTRACTL_END; @@ -434,7 +434,12 @@ 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 || !s_LogStubs) { From 3da60a771cdf5ba8c45360b6ae2e9d232aad96a2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 8 Jun 2026 10:53:59 -0700 Subject: [PATCH 7/8] Apply suggestion from @jkotas --- src/coreclr/vm/perfmap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 7c34f53655e049..edf11d2d169f33 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -322,7 +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; From 2b885641ca2c303090351fe6b264227680553ec3 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 8 Jun 2026 10:57:10 -0700 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Jan Kotas --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 01055c08ba7c90..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) (third lowest bit disable stub logging).") +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"), "")