diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 4be75dc9158969..506a371e040a62 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -715,6 +715,10 @@ void EEStartupHelper() } #endif +#ifdef FEATURE_PERFMAP + PerfMap::Initialize(); +#endif + #ifdef FEATURE_PERFTRACING DiagnosticServerAdapter::Initialize(); DiagnosticServerAdapter::PauseForDiagnosticsMonitor(); @@ -740,7 +744,6 @@ void EEStartupHelper() #endif #ifdef FEATURE_PERFMAP - PerfMap::Initialize(); InitThreadManagerPerfMapData(); #endif @@ -829,6 +832,10 @@ void EEStartupHelper() ExecutionManager::Init(); +#ifdef FEATURE_PERFMAP + PerfMap::SignalDependenciesReady(); +#endif + JitHost::Init(); #ifndef TARGET_UNIX diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index df46fe9c1ce965..5b1932c80f060f 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -27,6 +27,7 @@ #endif Volatile PerfMap::s_enabled = false; +Volatile PerfMap::s_dependenciesReady = false; PerfMap * PerfMap::s_Current = nullptr; bool PerfMap::s_ShowOptimizationTiers = false; bool PerfMap::s_GroupStubsOfSameType = false; @@ -120,6 +121,17 @@ void PerfMap::Enable(PerfMapType type, bool sendExisting) if (sendExisting) { + // When Enable is called very early in startup (e.g., via DiagnosticServer IPC before + // SystemDomain::Attach and ExecutionManager::Init), the AppDomain and EEJitManager + // may not exist yet. We use s_dependenciesReady (a Volatile) to guard against + // this, rather than null-checking individual pointers which would have race conditions + // due to non-Volatile statics like m_pEEJitManager. + // Safe to skip: no assemblies are loaded and no code is JIT'd at that point. + if (!s_dependenciesReady) + { + return; + } + AppDomain::AssemblyIterator assemblyIterator = GetAppDomain()->IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution)); CollectibleAssemblyHolder pAssembly; @@ -207,6 +219,15 @@ void PerfMap::Disable() } } +// Signal that all dependencies (AppDomain, ExecutionManager) are ready. +// This method must be called before any code is JITed or restored from R2R image. +void PerfMap::SignalDependenciesReady() +{ + LIMITED_METHOD_CONTRACT; + + s_dependenciesReady = true; +} + // Construct a new map for the process. PerfMap::PerfMap() { diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 6079d1fbc2e15d..04ef2598b692f5 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -24,6 +24,9 @@ class PerfMap private: static Volatile s_enabled; + // Set to true after all dependencies (AppDomain, ExecutionManager) are initialized. + static Volatile s_dependenciesReady; + // The one and only PerfMap for the process. static PerfMap * s_Current; @@ -104,6 +107,9 @@ class PerfMap // Close the map and flush any remaining data. static void Disable(); + // Signal that all dependencies (AppDomain, ExecutionManager) are ready. + static void SignalDependenciesReady(); + static bool LowGranularityStubs() { return !s_IndividualAllocationStubReporting; } }; #endif // PERFPID_H