From cbfa7cf5dfeb0fb31d6b4d401463352d45a6cc23 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Tue, 20 Jul 2021 20:40:19 +0300 Subject: [PATCH] Add profile-use-only mode for MultiCoreJit (#55005) * Add profile-use-only mode for MultiCoreJit - memory consumption is reduced if profile is not gathered - disk/flash life is increased if profile is not saved each time * Remove non-set m_fAppxMode --- src/coreclr/inc/clrconfigvalues.h | 1 + src/coreclr/vm/multicorejit.cpp | 63 +++++++++++++++++++++-------------- src/coreclr/vm/multicorejit.h | 4 +-- src/coreclr/vm/multicorejitimpl.h | 39 +++++++++++++++++----- src/coreclr/vm/multicorejitplayer.cpp | 24 ++++--------- 5 files changed, 77 insertions(+), 54 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index e2f1a63a..60cdfe9 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -371,6 +371,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynami RETAIL_CONFIG_STRING_INFO(INTERNAL_MultiCoreJitProfile, W("MultiCoreJitProfile"), "If set, use the file to store/control multi-core JIT.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitProfileWriteDelay, W("MultiCoreJitProfileWriteDelay"), 12, "Set the delay after which the multi-core JIT profile will be written to disk.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitMinNumCpus, W("MultiCoreJitMinNumCpus"), 2, "Minimum number of cpus that must be present to allow MultiCoreJit usage.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitNoProfileGather, W("MultiCoreJitNoProfileGather"), 0, "Set to 1 to disable profile gathering (but leave possibly enabled profile usage).") #endif diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp index ae8582f..380a007 100644 --- a/src/coreclr/vm/multicorejit.cpp +++ b/src/coreclr/vm/multicorejit.cpp @@ -144,6 +144,11 @@ HRESULT MulticoreJitRecorder::WriteOutput() HRESULT hr = E_FAIL; + if (m_JitInfoArray == nullptr || m_ModuleList == nullptr) + { + return S_OK; + } + // Go into preemptive mode for file operations GCX_PREEMP(); @@ -386,6 +391,9 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream) HRESULT hr = S_OK; + _ASSERTE(m_JitInfoArray != nullptr); + _ASSERTE(m_ModuleList != nullptr); + // Preprocessing Methods LONG skipped = 0; @@ -470,7 +478,6 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream) header.shortCounters[ 7] = m_stats.m_nTotalDelay; header.shortCounters[ 8] = m_stats.m_nDelayCount; header.shortCounters[ 9] = m_stats.m_nWalkBack; - header.shortCounters[10] = m_fAppxMode; _ASSERTE(HEADER_W_COUNTER >= 14); @@ -567,6 +574,8 @@ unsigned MulticoreJitRecorder::FindModule(Module * pModule) { LIMITED_METHOD_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + for (unsigned i = 0 ; i < m_ModuleCount; i ++) { if (m_ModuleList[i].pModule == pModule) @@ -585,6 +594,8 @@ unsigned MulticoreJitRecorder::GetOrAddModuleIndex(Module * pModule) { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + unsigned slot = FindModule(pModule); if ((slot == UINT_MAX) && (m_ModuleCount < MAX_MODULES)) @@ -604,7 +615,10 @@ void MulticoreJitRecorder::RecordMethodInfo(unsigned moduleIndex, MethodDesc * p { LIMITED_METHOD_CONTRACT; - if (m_JitInfoArray != nullptr && m_JitInfoCount < (LONG) MAX_METHODS) + _ASSERTE(m_JitInfoArray != nullptr); + _ASSERTE(m_ModuleList != nullptr); + + if (m_JitInfoCount < (LONG) MAX_METHODS) { m_ModuleList[moduleIndex].methodCount++; m_JitInfoArray[m_JitInfoCount++].PackMethod(moduleIndex, pMethod, application); @@ -615,6 +629,8 @@ unsigned MulticoreJitRecorder::RecordModuleInfo(Module * pModule) { LIMITED_METHOD_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + // pModule could be unknown at this point (modules not enumerated, no event received yet) unsigned moduleIndex = GetOrAddModuleIndex(pModule); @@ -675,7 +691,6 @@ void MulticoreJitRecorder::RecordOrUpdateModuleInfo(FileLoadLevel needLevel, uns class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator { MulticoreJitRecorder * m_pRecorder; - bool m_fAppxMode; HRESULT OnModule(Module * pModule) { @@ -688,7 +703,7 @@ class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator } CONTRACTL_END; - if (MulticoreJitManager::IsSupportedModule(pModule, false, m_fAppxMode)) + if (MulticoreJitManager::IsSupportedModule(pModule, false)) { m_pRecorder->AddModuleDependency(pModule, MulticoreJitManager::GetModuleFileLoadLevel(pModule)); } @@ -697,10 +712,9 @@ class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator } public: - MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder, bool fAppxMode) + MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder) { m_pRecorder = pRecorder; - m_fAppxMode = fAppxMode; } }; @@ -710,6 +724,8 @@ void MulticoreJitRecorder::AddModuleDependency(Module * pModule, FileLoadLevel l { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + MulticoreJitTrace(("AddModuleDependency(%s, %d)", pModule->GetSimpleName(), loadLevel)); _FireEtwMulticoreJitA(W("ADDMODULEDEPENDENCY"), pModule->GetSimpleName(), loadLevel, 0, 0); @@ -734,6 +750,8 @@ DWORD MulticoreJitRecorder::EncodeModule(Module * pReferencedModule) { STANDARD_VM_CONTRACT; + _ASSERTE(m_ModuleList != nullptr); + unsigned slot = GetOrAddModuleIndex(pReferencedModule); FileLoadLevel loadLevel = MulticoreJitManager::GetModuleFileLoadLevel(pReferencedModule); @@ -834,19 +852,19 @@ void MulticoreJitRecorder::PreRecordFirstMethod() m_fFirstMethod = false; { - MulticoreJitRecorderModuleEnumerator enumerator(this, m_fAppxMode); + MulticoreJitRecorderModuleEnumerator enumerator(this); enumerator.EnumerateLoadedModules(m_pDomain); } - // When running under Appx or CoreCLR for K, AppDomain is normally not shut down properly (CLR in hybrid case, or Alt-F4 shutdown), + // When running under CoreCLR for K, AppDomain is normally not shut down properly (CLR in hybrid case, or Alt-F4 shutdown), // So we only allow writing out after profileWriteTimeout seconds { // Get the timeout in seconds. int profileWriteTimeout = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfileWriteDelay); #ifndef TARGET_UNIX - // Using the same threadpool timer used by UsageLog to write out profile when running under Appx or CoreCLR. + // Using the same threadpool timer used by UsageLog to write out profile when running under CoreCLR. MulticoreJitManager & manager = m_pDomain->GetMulticoreJitManager(); s_delayedWriteTimer = CreateThreadpoolTimer(MulticoreJitRecorder::WriteMulticoreJitProfiler, &manager, NULL); @@ -876,7 +894,7 @@ void MulticoreJitRecorder::RecordMethodJitOrLoad(MethodDesc * pMethod, bool appl Module * pModule = pMethod->GetModule_NoLogging(); // Skip methods from non-supported modules - if (! MulticoreJitManager::IsSupportedModule(pModule, true, m_fAppxMode)) + if (! MulticoreJitManager::IsSupportedModule(pModule, true)) { return; } @@ -1019,8 +1037,7 @@ HRESULT MulticoreJitRecorder::StartProfile(const WCHAR * pRoot, const WCHAR * pF NewHolder player(new (nothrow) MulticoreJitProfilePlayer( m_pBinderContext, - nSession, - m_fAppxMode)); + nSession)); if (player == NULL) { @@ -1185,13 +1202,17 @@ void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBin if ((pProfile != NULL) && (pProfile[0] != 0)) // Ignore empty file name, just same as StopProfile { + bool gatherProfile = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitNoProfileGather) == 0; + MulticoreJitRecorder * pRecorder = new (nothrow) MulticoreJitRecorder( pDomain, pBinderContext, - m_fAppxMode); + gatherProfile); if (pRecorder != NULL) { + gatherProfile = pRecorder->CanGatherProfile(); + m_pMulticoreJitRecorder = pRecorder; LONG sessionID = m_ProfileSession.Increment(); @@ -1200,16 +1221,9 @@ void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBin MulticoreJitTrace(("MulticoreJitRecorder session %d created: %x", sessionID, hr)); - if (m_fAppxMode) // In Appx mode, recorder is only enabled when file exists, but header is bad (e.g. zero-length) + if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Ignore COR_E_BADIMAGEFORMAT, always record new profile { - if (hr == COR_E_BADIMAGEFORMAT) - { - m_fRecorderActive = true; - } - } - else if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Otherwise, ignore COR_E_BADIMAGEFORMAT, alway record new profile - { - m_fRecorderActive = true; + m_fRecorderActive = gatherProfile; } _FireEtwMulticoreJit(W("STARTPROFILE"), W("Recorder"), m_fRecorderActive, hr, 0); @@ -1349,7 +1363,6 @@ MulticoreJitManager::MulticoreJitManager() m_fSetProfileRootCalled = 0; m_fAutoStartCalled = 0; m_fRecorderActive = false; - m_fAppxMode = false; m_playerLock.Init(CrstMulticoreJitManager, (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN)); m_MulticoreJitCodeStorage.Init(); @@ -1382,7 +1395,7 @@ void MulticoreJitManager::RecordModuleLoad(Module * pModule, FileLoadLevel loadL if (m_fRecorderActive) { - if(IsSupportedModule(pModule, false, m_fAppxMode)) // Filter out unsupported module + if(IsSupportedModule(pModule, false)) // Filter out unsupported module { CrstHolder hold(& m_playerLock); @@ -1531,7 +1544,7 @@ void MulticoreJitManager::DisableMulticoreJit() // static DWORD MulticoreJitManager::EncodeModuleHelper(void * pModuleContext, Module * pReferencedModule) { - STANDARD_VM_CONTRACT + STANDARD_VM_CONTRACT; if (pModuleContext == NULL || pReferencedModule == NULL) { diff --git a/src/coreclr/vm/multicorejit.h b/src/coreclr/vm/multicorejit.h index 3b7bae0..58656ec 100644 --- a/src/coreclr/vm/multicorejit.h +++ b/src/coreclr/vm/multicorejit.h @@ -207,7 +207,6 @@ private: LONG m_fSetProfileRootCalled; // SetProfileRoot has been called LONG m_fAutoStartCalled; bool m_fRecorderActive; // Manager open for recording/event, turned on when initialized properly, turned off when at full capacity - bool m_fAppxMode; CrstExplicitInit m_playerLock; // Thread protection (accessing m_pMulticoreJitRecorder) MulticoreJitPlayerStat m_stats; // Statistics: normally gathered by player, written to profile @@ -229,7 +228,6 @@ public: m_fSetProfileRootCalled = 0; m_fAutoStartCalled = 0; m_fRecorderActive = false; - m_fAppxMode = false; } ~MulticoreJitManager() @@ -299,7 +297,7 @@ public: static void DisableMulticoreJit(); - static bool IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx); + static bool IsSupportedModule(Module * pModule, bool fMethodJit); static FileLoadLevel GetModuleFileLoadLevel(Module * pModule); diff --git a/src/coreclr/vm/multicorejitimpl.h b/src/coreclr/vm/multicorejitimpl.h index 234493f..d1f0581 100644 --- a/src/coreclr/vm/multicorejitimpl.h +++ b/src/coreclr/vm/multicorejitimpl.h @@ -212,7 +212,7 @@ public: ModuleRecord(unsigned lenName = 0, unsigned lenAssemblyName = 0); - bool MatchWithModule(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const; + bool MatchWithModule(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shouldAbort) const; unsigned ModuleNameLen() const { @@ -282,7 +282,6 @@ private: MulticoreJitPlayerStat & m_stats; MulticoreJitCounter & m_appdomainSession; bool m_shouldAbort; - bool m_fAppxMode; Thread * m_pThread; @@ -320,7 +319,7 @@ private: public: - MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode); + MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession); ~MulticoreJitProfilePlayer(); @@ -619,16 +618,15 @@ private: SString m_fullFileName; MulticoreJitPlayerStat & m_stats; - RecorderModuleInfo m_ModuleList[MAX_MODULES]; + RecorderModuleInfo * m_ModuleList; unsigned m_ModuleCount; unsigned m_ModuleDepCount; - RecorderInfo m_JitInfoArray[MAX_METHODS]; + RecorderInfo * m_JitInfoArray; LONG m_JitInfoCount; bool m_fFirstMethod; bool m_fAborted; - bool m_fAppxMode; #ifndef TARGET_UNIX static TP_TIMER * s_delayedWriteTimer; @@ -657,21 +655,32 @@ private: public: - MulticoreJitRecorder(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, bool fAppxMode) + MulticoreJitRecorder(AppDomain * pDomain, ICLRPrivBinder * pBinderContext, bool fRecorderActive) : m_stats(pDomain->GetMulticoreJitManager().GetStats()) + , m_ModuleList(nullptr) + , m_JitInfoArray(nullptr) { LIMITED_METHOD_CONTRACT; m_pDomain = pDomain; m_pBinderContext = pBinderContext; + + if (fRecorderActive) + { + m_ModuleList = new (nothrow) RecorderModuleInfo[MAX_MODULES]; + } m_ModuleCount = 0; + m_ModuleDepCount = 0; + if (fRecorderActive) + { + m_JitInfoArray = new (nothrow) RecorderInfo[MAX_METHODS]; + } m_JitInfoCount = 0; m_fFirstMethod = true; m_fAborted = false; - m_fAppxMode = fAppxMode; m_stats.Clear(); @@ -688,14 +697,26 @@ public: CloseThreadpoolTimer(pTimer); } } +#endif // !TARGET_UNIX ~MulticoreJitRecorder() { LIMITED_METHOD_CONTRACT; + delete[] m_ModuleList; + delete[] m_JitInfoArray; + +#ifndef TARGET_UNIX CloseTimer(); - } #endif // !TARGET_UNIX + } + + bool CanGatherProfile() + { + LIMITED_METHOD_CONTRACT; + + return m_ModuleList != NULL && m_JitInfoArray != NULL; + } bool IsAtFullCapacity() const { diff --git a/src/coreclr/vm/multicorejitplayer.cpp b/src/coreclr/vm/multicorejitplayer.cpp index da75fe4..a26e981 100644 --- a/src/coreclr/vm/multicorejitplayer.cpp +++ b/src/coreclr/vm/multicorejitplayer.cpp @@ -233,7 +233,7 @@ public: return false; } - bool MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx); + bool MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort); #ifdef MULTICOREJIT_LOGGING void Dump(const WCHAR * prefix, int index); @@ -242,11 +242,11 @@ public: }; -bool PlayerModuleInfo::MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort, bool fAppx) +bool PlayerModuleInfo::MatchWith(ModuleVersion & version, bool & gotVersion, Module * pModule, bool & shortAbort) { STANDARD_VM_CONTRACT; - if ((m_pModule == NULL) && m_pRecord->MatchWithModule(version, gotVersion, pModule, shortAbort, fAppx)) + if ((m_pModule == NULL) && m_pRecord->MatchWithModule(version, gotVersion, pModule, shortAbort)) { m_pModule = pModule; m_curLevel = (int) MulticoreJitManager::GetModuleFileLoadLevel(pModule); @@ -321,7 +321,7 @@ void PlayerModuleInfo::Dump(const WCHAR * prefix, int index) const unsigned EmptyToken = 0xFFFFFFFF; -bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion, Module * pModule, bool & shouldAbort, bool fAppx) const +bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion, Module * pModule, bool & shouldAbort) const { STANDARD_VM_CONTRACT; @@ -332,15 +332,6 @@ bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion if ((len == lenModuleName) && (memcmp(pModuleName, pName, lenModuleName) == 0)) { - // Ignore version check on play back when running under Appx (also GetModuleVersion is expensive) - - // For Appx, multicore JIT profile is pre-generated by application vendor, installed together with the package. - // So it may not have exact match with its own assemblies, and assemblies installed on the system. - if (fAppx) - { - return true; - } - if (! gotVersion) // Calling expensive GetModuleVersion only when simple name matches { gotVersion = true; @@ -369,7 +360,7 @@ bool ModuleRecord::MatchWithModule(ModuleVersion & modVersion, bool & gotVersion } -MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession, bool fAppxMode) +MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderContext, LONG nSession) : m_stats(::GetAppDomain()->GetMulticoreJitManager().GetStats()), m_appdomainSession(::GetAppDomain()->GetMulticoreJitManager().GetProfileSession()) { LIMITED_METHOD_CONTRACT; @@ -383,7 +374,6 @@ MulticoreJitProfilePlayer::MulticoreJitProfilePlayer(ICLRPrivBinder * pBinderCon m_nMissingModule = 0; m_nLoadedModuleCount = 0; m_shouldAbort = false; - m_fAppxMode = fAppxMode; m_pThread = NULL; m_pFileBuffer = NULL; @@ -438,7 +428,7 @@ bool MulticoreJitManager::ModuleHasNoCode(Module * pModule) // We only support default load context, non dynamic module, non domain neutral (needed for dependency) -bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit, bool fAppx) +bool MulticoreJitManager::IsSupportedModule(Module * pModule, bool fMethodJit) { CONTRACTL { @@ -651,7 +641,7 @@ HRESULT MulticoreJitProfilePlayer::OnModule(Module * pModule) // Match with simple name, and then version/flag/guid for (unsigned i = 0; i < m_moduleCount; i ++) { - if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort, m_fAppxMode)) + if (m_pModules[i].MatchWith(version, gotVersion, pModule, m_shouldAbort)) { m_nLoadedModuleCount ++; return hr; -- 2.7.4