summaryrefslogtreecommitdiff
path: root/src/vm/multicorejit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/multicorejit.cpp')
-rw-r--r--src/vm/multicorejit.cpp1662
1 files changed, 1662 insertions, 0 deletions
diff --git a/src/vm/multicorejit.cpp b/src/vm/multicorejit.cpp
new file mode 100644
index 0000000000..24154a81c7
--- /dev/null
+++ b/src/vm/multicorejit.cpp
@@ -0,0 +1,1662 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// File: MultiCoreJIT.cpp
+//
+
+// ===========================================================================
+// This file contains the implementation for MultiCore JIT (player in a seperate file MultiCoreJITPlayer.cpp)
+// ===========================================================================
+//
+
+#include "common.h"
+#include "vars.hpp"
+#include "security.h"
+#include "eeconfig.h"
+#include "dllimport.h"
+#include "comdelegate.h"
+#include "dbginterface.h"
+#include "listlock.inl"
+#include "stubgen.h"
+#include "eventtrace.h"
+#include "array.h"
+#include "fstream.h"
+#include "hash.h"
+
+#include "appdomain.hpp"
+#include "qcall.h"
+
+#include "eventtracebase.h"
+#include "multicorejit.h"
+#include "multicorejitimpl.h"
+
+const wchar_t * AppxProfile = W("Application.Profile");
+
+#if defined(FEATURE_APPX_BINDER)
+
+//static
+bool MulticoreJitManager::IsLoadOkay(Module * pModule)
+{
+ CONTRACTL
+ {
+ THROWS;
+ SO_INTOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (pModule->GetAssembly()->GetManifestFile()->IsWindowsRuntime())
+ {
+ PEFile * pFile = pModule->GetFile();
+
+ ICLRPrivAssembly * pHostAsm = pFile->GetHostAssembly();
+
+ // Allow first party WinMD to load in multicore JIT background thread
+ if (pHostAsm != NULL)
+ {
+ BOOL shared = FALSE;
+
+ if (SUCCEEDED(pHostAsm->IsShareable(& shared)))
+ {
+ if (shared)
+ {
+ LPCUTF8 simpleName = pModule->GetSimpleName();
+
+ if (IsWindowsNamespace(simpleName))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+#endif
+
+
+void MulticoreJitFireEtw(const wchar_t * pAction, const wchar_t * pTarget, int p1, int p2, int p3)
+{
+ LIMITED_METHOD_CONTRACT
+
+ FireEtwMulticoreJit(GetClrInstanceId(), pAction, pTarget, p1, p2, p3);
+}
+
+
+void MulticoreJitFireEtwA(const wchar_t * pAction, const char * pTarget, int p1, int p2, int p3)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+#ifdef FEATURE_EVENT_TRACE
+ EX_TRY
+ {
+ if (EventEnabledMulticoreJit())
+ {
+ SString wTarget;
+
+ wTarget.SetUTF8(pTarget);
+
+ FireEtwMulticoreJit(GetClrInstanceId(), pAction, wTarget.GetUnicode(), p1, p2, p3);
+ }
+ }
+ EX_CATCH
+ { }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // FEATURE_EVENT_TRACE
+}
+
+void MulticoreJitFireEtwMethodCodeReturned(MethodDesc * pMethod)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ EX_TRY
+ {
+ if(pMethod)
+ {
+ // Get the module id.
+ Module * pModule = pMethod->GetModule_NoLogging();
+ ULONGLONG ullModuleID = (ULONGLONG)(TADDR) pModule;
+
+ // Get the method id.
+ ULONGLONG ullMethodID = (ULONGLONG)pMethod;
+
+ // Fire the event.
+ FireEtwMulticoreJitMethodCodeReturned(GetClrInstanceId(), ullModuleID, ullMethodID);
+ }
+ }
+ EX_CATCH
+ { }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+#ifdef MULTICOREJIT_LOGGING
+
+// %s ANSI
+// %S UNICODE
+void _MulticoreJitTrace(const char * format, ...)
+{
+ static unsigned s_startTick = 0;
+
+ WRAPPER_NO_CONTRACT;
+
+ if (s_startTick == 0)
+ {
+ s_startTick = GetTickCount();
+ }
+
+ va_list args;
+ va_start(args, format);
+
+#ifdef LOGGING
+ LogSpew2 (LF2_MULTICOREJIT, LL_INFO100, "Mcj ");
+ LogSpew2Valist(LF2_MULTICOREJIT, LL_INFO100, format, args);
+ LogSpew2 (LF2_MULTICOREJIT, LL_INFO100, ", (time=%d ms)\n", GetTickCount() - s_startTick);
+#else
+
+ // Following LogSpewValist(DWORD facility, DWORD level, const char *fmt, va_list args)
+ char buffer[512];
+
+ int len;
+
+ len = sprintf_s(buffer, _countof(buffer), "Mcj TID %04x: ", GetCurrentThreadId());
+ len += _vsnprintf(buffer + len, _countof(buffer) - len, format, args);
+ len += sprintf_s(buffer + len, _countof(buffer) - len, ", (time=%d ms)\r\n", GetTickCount() - s_startTick);
+
+ OutputDebugStringA(buffer);
+#endif
+
+ va_end(args);
+
+}
+
+#endif
+
+
+HRESULT MulticoreJitRecorder::WriteOutput()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY; // Called from AppDomain::Stop which is MODE_ANY
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+
+ // Go into preemptive mode for file operations
+ GCX_PREEMP();
+
+ {
+ CFileStream fileStream;
+
+ if (SUCCEEDED(hr = fileStream.OpenForWrite(m_fullFileName)))
+ {
+ hr = WriteOutput(& fileStream);
+ }
+ }
+
+ return hr;
+}
+
+
+HRESULT WriteData(IStream * pStream, const void * pData, unsigned len)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END
+
+ ULONG cbWritten;
+
+ HRESULT hr = pStream->Write(pData, len, & cbWritten);
+
+ if (SUCCEEDED(hr) && (cbWritten != len))
+ {
+ hr = E_FAIL;
+ }
+
+ return hr;
+}
+
+// Write string, round to to DWORD alignment
+HRESULT WriteString(const void * pString, unsigned len, IStream * pStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ ULONG cbWritten = 0;
+
+ HRESULT hr;
+
+ hr = pStream->Write(pString, len, & cbWritten);
+
+ if (SUCCEEDED(hr))
+ {
+ len = RoundUp(len) - len;
+
+ if (len != 0)
+ {
+ cbWritten = 0;
+
+ hr = pStream->Write(& cbWritten, len, & cbWritten);
+ }
+ }
+
+ return hr;
+}
+
+
+//static
+FileLoadLevel MulticoreJitManager::GetModuleFileLoadLevel(Module * pModule)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ FileLoadLevel level = FILE_LOAD_CREATE; // min level
+
+ if (pModule != NULL)
+ {
+ DomainFile * pDomainFile = pModule->FindDomainFile(GetAppDomain());
+
+ if (pDomainFile != NULL)
+ {
+ level = pDomainFile->GetLoadLevel();
+ }
+ }
+
+ return level;
+}
+
+
+bool ModuleVersion::GetModuleVersion(Module * pModule)
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = E_FAIL;
+
+ // GetMVID can throw exception
+ EX_TRY
+ {
+ PEFile * pFile = pModule->GetFile();
+
+ if (pFile != NULL)
+ {
+ PEAssembly * pAsm = pFile->GetAssembly();
+
+ if (pAsm != NULL)
+ {
+ // CorAssemblyFlags, only 16-bit used
+ versionFlags = pAsm->GetFlags();
+
+ _ASSERTE((versionFlags & 0x80000000) == 0);
+
+ if (pFile->HasNativeImage())
+ {
+ hasNativeImage = 1;
+ }
+
+ pAsm->GetVersion(& major, & minor, & build, & revision);
+
+ pAsm->GetMVID(& mvid);
+
+ hr = S_OK;
+ }
+ }
+
+ // If the load context is LOADFROM, store it in the flags.
+#ifdef FEATURE_FUSION
+ Assembly * pAssembly = pModule->GetAssembly();
+ LOADCTX_TYPE loadCtx = pAssembly->GetManifestFile()->GetLoadContext();
+ if(LOADCTX_TYPE_LOADFROM == loadCtx)
+ {
+ versionFlags |= VERSIONFLAG_LOADCTX_LOADFROM;
+ }
+#endif
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return SUCCEEDED(hr);
+}
+
+ModuleRecord::ModuleRecord(unsigned lenName, unsigned lenAsmName)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ memset(this, 0, sizeof(ModuleRecord));
+
+ recordID = Pack8_24(MULTICOREJIT_MODULE_RECORD_ID, sizeof(ModuleRecord));
+
+ wLoadLevel = 0;
+ // Extra data
+ lenModuleName = (unsigned short) lenName;
+#if defined(FEATURE_CORECLR)
+ lenAssemblyName = (unsigned short) lenAsmName;
+ recordID += RoundUp(lenModuleName) + RoundUp(lenAssemblyName);
+#else
+ recordID += RoundUp(lenModuleName);
+#endif
+}
+
+
+bool RecorderModuleInfo::SetModule(Module * pMod)
+{
+ STANDARD_VM_CONTRACT;
+
+ pModule = pMod;
+
+ LPCUTF8 pModuleName = pMod->GetSimpleName();
+ unsigned lenModuleName = (unsigned) strlen(pModuleName);
+ simpleName.Set((const BYTE *) pModuleName, lenModuleName); // SBuffer::Set copies over name
+
+#if defined(FEATURE_CORECLR)
+ SString sAssemblyName;
+ StackScratchBuffer scratch;
+ pMod->GetAssembly()->GetManifestFile()->GetDisplayName(sAssemblyName);
+
+ LPCUTF8 pAssemblyName = sAssemblyName.GetUTF8(scratch);
+ unsigned lenAssemblyName = sAssemblyName.GetCount();
+ assemblyName.Set((const BYTE *) pAssemblyName, lenAssemblyName);
+#endif
+
+#if defined(FEATURE_APPX_BINDER)
+
+ // Allow certain modules to load on background thread
+ if (AppX::IsAppXProcess() && MulticoreJitManager::IsLoadOkay(pMod))
+ {
+ flags |= FLAG_LOADOKAY;
+ }
+
+#endif
+
+ return moduleVersion.GetModuleVersion(pMod);
+}
+
+
+
+/////////////////////////////////////////////////////
+//
+// class MulticoreJitRecorder
+//
+/////////////////////////////////////////////////////
+
+HRESULT MulticoreJitRecorder::WriteModuleRecord(IStream * pStream, const RecorderModuleInfo & module)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+
+ const void * pModuleName = module.simpleName;
+ unsigned lenModuleName = module.simpleName.GetSize();
+
+#if defined(FEATURE_CORECLR)
+ const void * pAssemblyName = module.assemblyName;
+ unsigned lenAssemblyName = module.assemblyName.GetSize();
+#else
+ unsigned lenAssemblyName = 0;
+#endif
+
+ ModuleRecord mod(lenModuleName, lenAssemblyName);
+
+ mod.version = module.moduleVersion;
+ mod.jitMethodCount = module.methodCount;
+ mod.wLoadLevel = (unsigned short) module.loadLevel;
+ mod.flags = module.flags;
+
+ hr = WriteData(pStream, & mod, sizeof(mod));
+
+ if (SUCCEEDED(hr))
+ {
+ hr = WriteString(pModuleName, lenModuleName, pStream);
+
+#if defined(FEATURE_CORECLR)
+ if (SUCCEEDED(hr))
+ {
+ hr = WriteString(pAssemblyName, lenAssemblyName, pStream);
+ }
+#endif
+ }
+
+ return hr;
+}
+
+
+HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ {
+ HeaderRecord header;
+
+ memset(&header, 0, sizeof(header));
+
+ header.recordID = Pack8_24(MULTICOREJIT_HEADER_RECORD_ID, sizeof(HeaderRecord));
+ header.version = MULTICOREJIT_PROFILE_VERSION;
+ header.moduleCount = m_ModuleCount;
+ header.methodCount = m_JitInfoCount - m_ModuleDepCount;
+ header.moduleDepCount = m_ModuleDepCount;
+
+ MulticoreJitCodeStorage & curStorage = m_pDomain->GetMulticoreJitManager().GetMulticoreJitCodeStorage();
+
+ // Stats about played profile, 14 short, 3 long = 40 bytes
+ header.shortCounters[ 0] = m_stats.m_nTotalMethod;
+ header.shortCounters[ 1] = m_stats.m_nHasNativeCode;
+ header.shortCounters[ 2] = m_stats.m_nTryCompiling;
+ header.shortCounters[ 3] = (unsigned short) curStorage.GetStored();
+ header.shortCounters[ 4] = (unsigned short) curStorage.GetReturned();
+ header.shortCounters[ 5] = m_stats.m_nFilteredMethods;
+ header.shortCounters[ 6] = m_stats.m_nMissingModuleSkip;
+ 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);
+
+ header.longCounters[0] = m_stats.m_hr;
+
+ _ASSERTE(HEADER_D_COUNTER >= 3);
+
+ _ASSERTE((sizeof(header) % sizeof(unsigned)) == 0);
+
+ hr = WriteData(pStream, & header, sizeof(header));
+ }
+
+ DWORD dwData = 0;
+
+ for (unsigned i = 0; SUCCEEDED(hr) && (i < m_ModuleCount); i ++)
+ {
+ hr = WriteModuleRecord(pStream, m_ModuleList[i]);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ unsigned remain = m_JitInfoCount;
+
+ const unsigned * pInfo = m_JitInfoArray;
+
+ while (SUCCEEDED(hr) && (remain > 0))
+ {
+ unsigned count = remain;
+
+ if (count > MAX_JIT_COUNT)
+ {
+ count = MAX_JIT_COUNT;
+ }
+
+ dwData = Pack8_24(MULTICOREJIT_JITINF_RECORD_ID, count * sizeof(DWORD) + sizeof(DWORD));
+
+ hr = WriteData(pStream, & dwData, sizeof(dwData));
+
+ if (SUCCEEDED(hr))
+ {
+ hr = WriteData(pStream, pInfo, sizeof(unsigned) * count);
+ }
+
+ pInfo += count;
+ remain -= count;
+ }
+ }
+
+ MulticoreJitTrace(("New profile: %d modules, %d methods", m_ModuleCount, m_JitInfoCount));
+
+ _FireEtwMulticoreJit(W("WRITEPROFILE"), m_fullFileName.GetUnicode(), m_ModuleCount, m_JitInfoCount, 0);
+
+ return hr;
+}
+
+
+unsigned MulticoreJitRecorder::FindModule(Module * pModule)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ for (unsigned i = 0 ; i < m_ModuleCount; i ++)
+ {
+ if (m_ModuleList[i].pModule == pModule)
+ {
+ return i;
+ }
+ }
+
+ return UINT_MAX;
+}
+
+
+// Find known module index, or add to module table
+// Return UINT_MAX when table is full, or SetModule fails
+unsigned MulticoreJitRecorder::GetModuleIndex(Module * pModule)
+{
+ STANDARD_VM_CONTRACT;
+
+ unsigned slot = FindModule(pModule);
+
+ if ((slot == UINT_MAX) && (m_ModuleCount < MAX_MODULES))
+ {
+ slot = m_ModuleCount ++;
+
+ if (! m_ModuleList[slot].SetModule(pModule))
+ {
+ return UINT_MAX;
+ }
+ }
+
+ return slot;
+}
+
+
+void MulticoreJitRecorder::RecordJitInfo(unsigned module, unsigned method)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_JitInfoCount < (LONG) MAX_METHOD_ARRAY)
+ {
+ unsigned info1 = Pack8_24(module, method & 0xFFFFFF);
+
+ // Due to incremental loading, there are quite a few RecordModuleLoad coming with increasing load level, merge
+
+ // Previous record and current record are both MODULE_DEPENDENCY
+ if ((m_JitInfoCount > 0) && (info1 & MODULE_DEPENDENCY))
+ {
+ unsigned info0 = m_JitInfoArray[m_JitInfoCount - 1];
+
+ if ((info0 & 0xFFFF00FF) == (info1 & 0xFFFF00FF)) // to/from modules are the same
+ {
+ if (info1 > info0) // higher level
+ {
+ m_JitInfoArray[m_JitInfoCount - 1] = info1; // replace
+ }
+
+ return; // no new record
+ }
+ }
+
+ if (method & MODULE_DEPENDENCY)
+ {
+ m_ModuleDepCount ++;
+ }
+ else
+ {
+ m_ModuleList[module].methodCount ++;
+ }
+
+ m_JitInfoArray[m_JitInfoCount] = info1;
+ m_JitInfoCount ++;
+ }
+}
+
+class MulticoreJitRecorderModuleEnumerator : public MulticoreJitModuleEnumerator
+{
+ MulticoreJitRecorder * m_pRecorder;
+ bool m_fAppxMode;
+
+ HRESULT OnModule(Module * pModule)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ if (MulticoreJitManager::IsSupportedModule(pModule, false, m_fAppxMode))
+ {
+ m_pRecorder->AddModuleDependency(pModule, MulticoreJitManager::GetModuleFileLoadLevel(pModule));
+ }
+
+ return S_OK;
+ }
+
+public:
+ MulticoreJitRecorderModuleEnumerator(MulticoreJitRecorder * pRecorder, bool fAppxMode)
+ {
+ m_pRecorder = pRecorder;
+ m_fAppxMode = fAppxMode;
+ }
+};
+
+
+// The whole AppDomain is depending on pModule
+void MulticoreJitRecorder::AddModuleDependency(Module * pModule, FileLoadLevel loadLevel)
+{
+ STANDARD_VM_CONTRACT;
+
+ MulticoreJitTrace(("AddModuleDependency(%s, %d)", pModule->GetSimpleName(), loadLevel));
+
+ _FireEtwMulticoreJitA(W("ADDMODULEDEPENDENCY"), pModule->GetSimpleName(), loadLevel, 0, 0);
+
+ unsigned moduleTo = GetModuleIndex(pModule);
+
+ if (moduleTo != UINT_MAX)
+ {
+ if (m_ModuleList[moduleTo].loadLevel < loadLevel)
+ {
+ m_ModuleList[moduleTo].loadLevel = loadLevel;
+
+ // Update load level
+ RecordJitInfo(0, ((unsigned) loadLevel << 8) | moduleTo | MODULE_DEPENDENCY);
+ }
+ }
+}
+
+
+// Enumerate all modules within an assembly, call OnModule virtual method
+HRESULT MulticoreJitModuleEnumerator::HandleAssembly(DomainAssembly * pAssembly)
+{
+ STANDARD_VM_CONTRACT;
+
+ DomainAssembly::ModuleIterator modIt = pAssembly->IterateModules(kModIterIncludeLoaded);
+
+ HRESULT hr = S_OK;
+
+ while (modIt.Next() && SUCCEEDED(hr))
+ {
+ Module * pModule = modIt.GetModule();
+
+ if (pModule != NULL)
+ {
+ hr = OnModule(pModule);
+ }
+ }
+
+ return hr;
+}
+
+
+// Enum all loaded modules within pDomain, call OnModule virtual method
+HRESULT MulticoreJitModuleEnumerator::EnumerateLoadedModules(AppDomain * pDomain)
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ AppDomain::AssemblyIterator appIt = pDomain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
+
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (appIt.Next(pDomainAssembly.This()) && SUCCEEDED(hr))
+ {
+#if !defined(FEATURE_CORECLR)
+ if (! pDomainAssembly->IsSystem())
+#endif
+ {
+ hr = HandleAssembly(pDomainAssembly);
+ }
+ }
+
+ return hr;
+}
+
+
+#if defined(FEATURE_APPX_BINDER)
+// ProfileName = ProcessName_CoreAppId.Profile ; for server process, always use it for output
+// ProcessName.Profile
+
+void AppendAppxProfileName(SString & name)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ } CONTRACTL_END;
+
+ {
+ WCHAR wszProcessName[_MAX_PATH];
+
+ if (WszGetModuleFileName(NULL, wszProcessName, _MAX_PATH) != 0)
+ {
+ WCHAR * pNameOnly = wcsrchr(wszProcessName, W('\\'));
+
+ if (pNameOnly == NULL)
+ {
+ pNameOnly = wszProcessName;
+ }
+ else
+ {
+ pNameOnly ++;
+ }
+
+ WCHAR * pExt = wcsrchr(pNameOnly, W('.')); // last .
+
+ if (pExt != NULL)
+ {
+ * pExt = 0;
+ }
+
+ // Use process name only
+ name.Append(pNameOnly);
+ name.Append(W("_"));
+ }
+ }
+
+ LPCWSTR pAppId = NULL;
+ if (SUCCEEDED(AppX::GetApplicationId(pAppId)))
+ {
+ name.Append(pAppId);
+ name.Append(W(".Profile"));
+
+ return;
+ }
+
+ // default name
+ name.Append(AppxProfile);
+}
+#endif
+
+// static: single instace within a process
+
+#ifndef FEATURE_PAL
+TP_TIMER * MulticoreJitRecorder::s_delayedWriteTimer; // = NULL;
+
+// static
+void CALLBACK
+MulticoreJitRecorder::WriteMulticoreJitProfiler(PTP_CALLBACK_INSTANCE pInstance, PVOID pvContext, PTP_TIMER pTimer)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ } CONTRACTL_END;
+
+ // Avoid saving after MulticoreJitRecorder is deleted, and saving twice
+ if (! CloseTimer())
+ {
+ return;
+ }
+
+ MulticoreJitRecorder * pRecorder = (MulticoreJitRecorder *) pvContext;
+
+ if (pRecorder != NULL)
+ {
+#if defined(FEATURE_APPX_BINDER)
+ if (pRecorder->m_fAppxMode)
+ {
+ const wchar_t * pOutputDir = NULL;
+
+ HRESULT hr = Clr::Util::GetLocalAppDataDirectory(&pOutputDir);
+
+ if (SUCCEEDED(hr))
+ {
+ pRecorder->m_fullFileName = pOutputDir;
+ pRecorder->m_fullFileName.Append(W("\\"));
+
+ AppendAppxProfileName(pRecorder->m_fullFileName);
+
+ pRecorder->StopProfile(false);
+ }
+ }
+ else
+#endif
+ {
+ pRecorder->StopProfile(false);
+ }
+ }
+}
+
+#endif // !FEATURE_PAL
+
+void MulticoreJitRecorder::PreRecordFirstMethod()
+{
+ STANDARD_VM_CONTRACT;
+
+ // When first method is added to an AppDomain, add all currently loaded modules as dependent modules
+
+ m_fFirstMethod = false;
+
+ {
+ MulticoreJitRecorderModuleEnumerator enumerator(this, m_fAppxMode);
+
+ 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),
+ // So we only allow writing out after profileWriteTimeout seconds
+#if !defined(FEATURE_CORECLR)
+ if (m_fAppxMode)
+#endif
+ {
+ // Get the timeout in seconds.
+ int profileWriteTimeout = (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfileWriteDelay);
+
+#ifndef FEATURE_PAL
+ // Using the same threadpool timer used by UsageLog to write out profile when running under Appx or CoreCLR.
+ s_delayedWriteTimer = CreateThreadpoolTimer(WriteMulticoreJitProfiler, this, NULL);
+
+ if (s_delayedWriteTimer != NULL)
+ {
+ ULARGE_INTEGER msDelay;
+
+ // SetThreadpoolTimer needs delay to be given in 100 ns unit, negative
+ msDelay.QuadPart = (ULONGLONG) -(profileWriteTimeout * 10 * 1000 * 1000);
+ FILETIME ftDueTime;
+ ftDueTime.dwLowDateTime = msDelay.u.LowPart;
+ ftDueTime.dwHighDateTime = msDelay.u.HighPart;
+
+ // This will either set the timer to happen in profileWriteTimeout seconds, or reset the timer so the same will happen.
+ // This function is safe to call
+ SetThreadpoolTimer(s_delayedWriteTimer, &ftDueTime, 0, 2000 /* large 2000 ms window for executing this timer is acceptable as the timing here is very much not critical */);
+ }
+#endif // !FEATURE_PAL
+ }
+}
+
+
+void MulticoreJitRecorder::RecordMethodJit(MethodDesc * pMethod, bool application)
+{
+ STANDARD_VM_CONTRACT;
+
+ Module * pModule = pMethod->GetModule_NoLogging();
+
+ // Skip methods from non-supported modules
+ if (! MulticoreJitManager::IsSupportedModule(pModule, true, m_fAppxMode))
+ {
+ return;
+ }
+
+ // pModule could be unknown at this point (modules not enumerated, no event received yet)
+ unsigned moduleIndex = GetModuleIndex(pModule);
+
+ if (moduleIndex < UINT_MAX)
+ {
+ if (m_fFirstMethod)
+ {
+ PreRecordFirstMethod();
+ }
+
+ // Make sure level for current module is recorded properly
+ if (m_ModuleList[moduleIndex].loadLevel != FILE_ACTIVE)
+ {
+ FileLoadLevel needLevel = MulticoreJitManager::GetModuleFileLoadLevel(pModule);
+
+ if (m_ModuleList[moduleIndex].loadLevel < needLevel)
+ {
+ m_ModuleList[moduleIndex].loadLevel = needLevel;
+
+ // Update load level
+ RecordJitInfo(0, ((unsigned) needLevel << 8) | moduleIndex | MODULE_DEPENDENCY);
+ }
+ }
+
+ unsigned methodIndex = pMethod->GetMemberDef_NoLogging() & 0xFFFFFF;
+
+ if (methodIndex <= METHODINDEX_MASK)
+ {
+ if (application) // Jitted by application threads, not background thread
+ {
+ methodIndex |= JIT_BY_APP_THREAD;
+ }
+
+ RecordJitInfo(moduleIndex, methodIndex);
+ }
+ }
+}
+
+
+// Called from AppDomain::RaiseAssemblyResolveEvent, make it simple
+
+void MulticoreJitRecorder::AbortProfile()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Increment session ID tells background thread to stop
+ m_pDomain->GetMulticoreJitManager().GetProfileSession().Increment();
+
+ m_fAborted = true; // Do not save output when StopProfile is called
+}
+
+
+HRESULT MulticoreJitRecorder::StopProfile(bool appDomainShutdown)
+{
+ CONTRACTL
+ {
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ // Increment session ID tells background thread to stop
+ MulticoreJitManager & manager = m_pDomain->GetMulticoreJitManager();
+
+ manager.GetProfileSession().Increment();
+
+ if (! m_fAborted && ! m_fullFileName.IsEmpty())
+ {
+ hr = WriteOutput();
+ }
+
+ MulticoreJitTrace(("StopProfile: Save new profile to %S, hr=0x%x", m_fullFileName.GetUnicode(), hr));
+
+ return hr;
+}
+
+
+// suffix (>= 0) is used for AutoStartProfile, to support multiple AppDomains. It's set to -1 for normal API call path
+HRESULT MulticoreJitRecorder::StartProfile(const wchar_t * pRoot, const wchar_t * pFile, int suffix, LONG nSession)
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_FALSE;
+
+ if ((pRoot == NULL) || (pFile == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ MulticoreJitTrace(("StartProfile('%S', '%S', %d)", pRoot, pFile, suffix));
+
+ size_t lenFile = wcslen(pFile);
+
+ // Options (only AutoStartProfile using environment variable, for testing)
+ // ([d|D]main-thread-delay)
+ if ((suffix >= 0) && (lenFile >= 3) && (pFile[0]=='('))// AutoStartProfile, using environment variable
+ {
+ pFile ++;
+ lenFile --;
+
+ while ((lenFile > 0) && isalpha(pFile[0]))
+ {
+ switch (pFile[0])
+ {
+ case 'd':
+ case 'D':
+ g_MulticoreJitEnabled = false;
+
+ default:
+ break;
+ }
+
+ pFile ++;
+ lenFile --;
+ }
+
+ if ((lenFile > 0) && isdigit(* pFile))
+ {
+ g_MulticoreJitDelay = 0;
+
+ while ((lenFile > 0) && isdigit(* pFile))
+ {
+ g_MulticoreJitDelay = g_MulticoreJitDelay * 10 + (int) (* pFile - '0');
+
+ pFile ++;
+ lenFile --;
+ }
+ }
+
+ // End of options
+ if ((lenFile > 0) && (* pFile == ')'))
+ {
+ pFile ++;
+ lenFile --;
+ }
+ }
+
+ MulticoreJitTrace(("g_MulticoreJitEnabled = %d, disable/enable Mcj feature", g_MulticoreJitEnabled));
+
+ if (g_MulticoreJitEnabled && (lenFile > 0))
+ {
+ m_fullFileName = pRoot;
+
+ // Append seperator if root does not end with one
+ unsigned len = m_fullFileName.GetCount();
+
+ if ((len != 0) && (m_fullFileName[len - 1] != '\\'))
+ {
+ m_fullFileName.Append('\\');
+ }
+
+ m_fullFileName.Append(pFile);
+
+ // Suffix for AutoStartProfile, used for multiple appdomain
+ if (suffix >= 0)
+ {
+ m_fullFileName.AppendPrintf(W("_%s_%s_%d.prof"),
+ SystemDomain::System()->DefaultDomain()->GetFriendlyName(),
+ m_pDomain->GetFriendlyName(),
+ suffix);
+ }
+
+ NewHolder<MulticoreJitProfilePlayer> player(new (nothrow) MulticoreJitProfilePlayer(
+ m_pDomain,
+#if defined(FEATURE_CORECLR)
+ m_pBinderContext,
+#else
+ NULL,
+#endif
+ nSession,
+ m_fAppxMode));
+
+ if (player == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ HRESULT hr1 = S_OK;
+
+ EX_TRY
+ {
+ hr1 = player->ProcessProfile(m_fullFileName);
+ }
+ EX_CATCH_HRESULT(hr1);
+
+ // If ProcessProfile succeeds, the background thread is responsible for deleting it when it finishes; otherwise, delete now
+ if (SUCCEEDED(hr1))
+ {
+ if (g_MulticoreJitDelay > 0)
+ {
+ MulticoreJitTrace(("Delay main thread %d ms", g_MulticoreJitDelay));
+
+ ClrSleepEx(g_MulticoreJitDelay, FALSE);
+ }
+
+ player.SuppressRelease();
+ }
+
+ MulticoreJitTrace(("ProcessProfile('%S') returns %x", m_fullFileName.GetUnicode(), hr1));
+
+ // Ignore error, even when we can't play back the file, we can still record new one
+
+ // If file exists, but profile header can't be read, pass error to caller (ignored by caller for non Appx)
+ if (hr1 == COR_E_BADIMAGEFORMAT)
+ {
+ hr = hr1;
+ }
+ }
+ }
+
+ MulticoreJitTrace(("StartProfile('%S', '%S', %d) returns %x", pRoot, pFile, suffix, hr));
+
+ _FireEtwMulticoreJit(W("STARTPROFILE"), m_fullFileName.GetUnicode(), hr, 0, 0);
+
+ return hr;
+}
+
+
+// Module load call back, record new module information, update play-back module list
+void MulticoreJitRecorder::RecordModuleLoad(Module * pModule, FileLoadLevel loadLevel)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (pModule != NULL)
+ {
+ if (! m_fFirstMethod) // If m_fFirstMethod flag is still on, defer calling AddModuleDependency until first method JIT
+ {
+ AddModuleDependency(pModule, loadLevel);
+ }
+ }
+}
+
+
+// Call back from MethodDesc::MakeJitWorker for
+PCODE MulticoreJitRecorder::RequestMethodCode(MethodDesc * pMethod, MulticoreJitManager * pManager)
+{
+ STANDARD_VM_CONTRACT;
+
+ // Disable it when profiler is running
+
+#ifdef PROFILING_SUPPORTED
+
+ _ASSERTE(! CORProfilerTrackJITInfo());
+
+#endif
+
+ _ASSERTE(! pMethod->IsDynamicMethod());
+
+ PCODE pCode = NULL;
+
+ pCode = pManager->GetMulticoreJitCodeStorage().QueryMethodCode(pMethod);
+
+ if ((pCode != NULL) && pManager->IsRecorderActive()) // recorder may be off when player is on (e.g. for Appx)
+ {
+ RecordMethodJit(pMethod, false); // JITTed by background thread, returned to application
+ }
+
+ return pCode;
+}
+
+
+//////////////////////////////////////////////////////////
+//
+// class MulticoreJitManager: attachment to AppDomain
+//
+//
+//////////////////////////////////////////////////////////
+
+
+// API Function: SettProfileRoot, store information with MulticoreJitManager class
+// Threading: protected by InterlockedExchange(m_fMulticoreJITEnabled)
+
+void MulticoreJitManager::SetProfileRoot(AppDomain * pDomain, const wchar_t * pProfilePath)
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef PROFILING_SUPPORTED
+
+ if (CORProfilerTrackJITInfo())
+ {
+ return;
+ }
+
+#endif
+
+ if (g_SystemInfo.dwNumberOfProcessors >= 2)
+ {
+ if (InterlockedCompareExchange(& m_fSetProfileRootCalled, SETPROFILEROOTCALLED, 0) == 0) // Only allow the first call per appdomain
+ {
+ m_profileRoot = pProfilePath;
+ }
+ }
+}
+
+
+// API Function: StartProfile
+// Threading: protected by m_playerLock
+void MulticoreJitManager::StartProfile(AppDomain * pDomain, ICLRPrivBinder *pBinderContext, const wchar_t * pProfile, int suffix)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
+ {
+ MulticoreJitTrace(("StartProfile fail: SetProfileRoot not called/failed"));
+ _FireEtwMulticoreJit(W("STARTPROFILE"), W("No SetProfileRoot"), 0, 0, 0);
+ return;
+ }
+
+ // Need extra processor for multicore JIT feature
+ _ASSERTE(g_SystemInfo.dwNumberOfProcessors >= 2);
+
+#ifdef PROFILING_SUPPORTED
+
+ if (CORProfilerTrackJITInfo())
+ {
+ MulticoreJitTrace(("StartProfile fail: CORProfilerTrackJITInfo on"));
+ _FireEtwMulticoreJit(W("STARTPROFILE"), W("Profiling On"), 0, 0, 0);
+ return;
+ }
+
+#endif
+ CrstHolder hold(& m_playerLock);
+
+ // Stop current profiling first, delete current m_pMulticoreJitRecorder if any
+ StopProfile(false);
+
+ if ((pProfile != NULL) && (pProfile[0] != 0)) // Ignore empty file name, just same as StopProfile
+ {
+ MulticoreJitRecorder * pRecorder = new (nothrow) MulticoreJitRecorder(
+ pDomain,
+#if defined(FEATURE_CORECLR)
+ pBinderContext,
+#else
+ NULL,
+#endif
+ m_fAppxMode);
+
+ if (pRecorder != NULL)
+ {
+ m_pMulticoreJitRecorder = pRecorder;
+
+ LONG sessionID = m_ProfileSession.Increment();
+
+ HRESULT hr = m_pMulticoreJitRecorder->StartProfile(m_profileRoot, pProfile, suffix, sessionID);
+
+ 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)
+ {
+ m_fRecorderActive = true;
+ }
+ }
+ else if ((hr == COR_E_BADIMAGEFORMAT) || SUCCEEDED(hr)) // Otherwise, ignore COR_E_BADIMAGEFORMAT, alway record new profile
+ {
+ m_fRecorderActive = true;
+ }
+
+ _FireEtwMulticoreJit(W("STARTPROFILE"), W("Recorder"), m_fRecorderActive, hr, 0);
+ }
+ }
+}
+
+
+// Threading: protected by m_playerLock
+void MulticoreJitManager::AbortProfile()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
+ {
+ return;
+ }
+
+ CrstHolder hold(& m_playerLock);
+
+ if (m_pMulticoreJitRecorder != NULL)
+ {
+ MulticoreJitTrace(("AbortProfile"));
+
+ _FireEtwMulticoreJit(W("ABORTPROFILE"), W(""), 0, 0, 0);
+
+ m_fRecorderActive = false;
+
+ m_pMulticoreJitRecorder->AbortProfile();
+ }
+
+ // Disable the feature within the AppDomain
+ m_fSetProfileRootCalled = -1;
+}
+
+
+// Stop current profiling, could be called automatically from AppDomain shut down
+// Threading: protected by m_playerLock
+void MulticoreJitManager::StopProfile(bool appDomainShutdown)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ if (m_fSetProfileRootCalled != SETPROFILEROOTCALLED)
+ {
+ return;
+ }
+
+ MulticoreJitRecorder * pRecorder;
+
+ if (appDomainShutdown)
+ {
+ // In the app domain shut down code path, need to hold m_playerLock critical section to wait for other thread to finish using recorder
+ CrstHolder hold(& m_playerLock);
+
+ pRecorder = InterlockedExchangeT(& m_pMulticoreJitRecorder, NULL);
+ }
+ else
+ {
+ // When called from StartProfile, should not take critical section because it's already entered
+
+ pRecorder = InterlockedExchangeT(& m_pMulticoreJitRecorder, NULL);
+ }
+
+ if (pRecorder != NULL)
+ {
+ m_fRecorderActive = false;
+
+ EX_TRY
+ {
+ pRecorder->StopProfile(appDomainShutdown);
+ }
+ EX_CATCH
+ {
+ MulticoreJitTrace(("StopProfile(%d) throws exception", appDomainShutdown));
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ delete pRecorder;
+ }
+
+ MulticoreJitTrace(("StopProfile(%d) returns", appDomainShutdown));
+}
+
+
+LONG g_nMulticoreAutoStart = 0;
+
+// Threading: calls into StartProfile
+void MulticoreJitManager::AutoStartProfile(AppDomain * pDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CLRConfigStringHolder wszProfile(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MultiCoreJitProfile));
+
+ if ((wszProfile != NULL) && (wszProfile[0] != 0))
+ {
+ int suffix = (int) InterlockedIncrement(& g_nMulticoreAutoStart);
+
+ SetProfileRoot(pDomain, W("")); // Fake a SetProfileRoot call
+
+ StartProfile(
+ pDomain,
+ NULL,
+ wszProfile,
+ suffix);
+ }
+}
+
+#if defined(FEATURE_APPX_BINDER)
+
+// Called from CorHost2::ExecuteMain
+void MulticoreJitManager::AutoStartProfileAppx(AppDomain * pDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (InterlockedCompareExchange(& m_fAutoStartCalled, SETPROFILEROOTCALLED, 0) == 0) // Only allow the first call
+ {
+ WCHAR wzFilePath[_MAX_PATH];
+
+ UINT32 cchFilePath = NumItems(wzFilePath);
+
+ SString profileName;
+
+ // Try to find ProcessName_AppId.Profile
+ AppendAppxProfileName(profileName);
+
+ // Search for Application.Profile within the package
+ HRESULT hr = AppX::FindFileInCurrentPackage(profileName, &cchFilePath, wzFilePath);
+
+ if (SUCCEEDED(hr))
+ {
+ m_fAppxMode = true;
+ SetProfileRoot(pDomain, W("")); // Fake a SetProfileRoot call
+ StartProfile(pDomain, NULL, wzFilePath);
+ }
+ else
+ {
+ _FireEtwMulticoreJit(W("AUTOSTARTPROFILEAPPX"), profileName, hr, 0, 0);
+ }
+ }
+}
+
+#endif
+
+// Constructor
+
+MulticoreJitManager::MulticoreJitManager()
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ m_pMulticoreJitRecorder = NULL;
+ m_fSetProfileRootCalled = 0;
+ m_fAutoStartCalled = 0;
+ m_fRecorderActive = false;
+ m_fAppxMode = false;
+
+ m_playerLock.Init(CrstMulticoreJitManager, (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN));
+ m_MulticoreJitCodeStorage.Init();
+}
+
+
+// Threading: uses Release to free object
+MulticoreJitManager::~MulticoreJitManager()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pMulticoreJitRecorder != NULL)
+ {
+ delete m_pMulticoreJitRecorder;
+
+ m_pMulticoreJitRecorder = NULL;
+ }
+
+ m_playerLock.Destroy();
+}
+
+
+// Threading: proected by m_playerLock
+
+void MulticoreJitManager::RecordModuleLoad(Module * pModule, FileLoadLevel loadLevel)
+{
+ STANDARD_VM_CONTRACT;
+
+
+#if defined(FEATURE_APPX_BINDER) && !defined(FEATURE_CORECLR)
+ // When running under Appx, allow framework assembly / first party winmd to load
+ // load-level change not allowed in the background thread, unless for resource DLL (loaded for exception throwing), but this could still happen.
+ _ASSERTE(! GetThread()->HasThreadStateNC(Thread::TSNC_CallingManagedCodeDisabled) || ModuleHasNoCode(pModule)
+ || m_fAppxMode && IsLoadOkay(pModule));
+
+#elif !defined(FEATURE_CORECLR)
+
+ _ASSERTE(! GetThread()->HasThreadStateNC(Thread::TSNC_CallingManagedCodeDisabled) || ModuleHasNoCode(pModule));
+
+#endif
+
+ if (m_fRecorderActive)
+ {
+ if(IsSupportedModule(pModule, false, m_fAppxMode)) // Filter out unsupported module
+ {
+ CrstHolder hold(& m_playerLock);
+
+ if (m_pMulticoreJitRecorder != NULL)
+ {
+ m_pMulticoreJitRecorder->RecordModuleLoad(pModule, loadLevel);
+ }
+ }
+ else
+ {
+ _FireEtwMulticoreJitA(W("UNSUPPORTEDMODULE"), pModule->GetSimpleName(), 0, 0, 0);
+ }
+ }
+}
+
+
+// Call back from MethodDesc::MakeJitWorker for
+// Threading: proected by m_playerLock
+
+PCODE MulticoreJitManager::RequestMethodCode(MethodDesc * pMethod)
+{
+ STANDARD_VM_CONTRACT;
+
+ CrstHolder hold(& m_playerLock);
+
+ if (m_pMulticoreJitRecorder != NULL)
+ {
+ PCODE requestedCode = m_pMulticoreJitRecorder->RequestMethodCode(pMethod, this);
+ if(requestedCode)
+ {
+ _FireEtwMulticoreJitMethodCodeReturned(pMethod);
+ }
+
+ return requestedCode;
+ }
+
+ return NULL;
+}
+
+
+// Call back from MethodDesc::MakeJitWorker for
+// Threading: proected by m_playerLock
+
+void MulticoreJitManager::RecordMethodJit(MethodDesc * pMethod)
+{
+ STANDARD_VM_CONTRACT;
+
+ CrstHolder hold(& m_playerLock);
+
+ if (m_pMulticoreJitRecorder != NULL)
+ {
+ m_pMulticoreJitRecorder->RecordMethodJit(pMethod, true);
+
+ if (m_pMulticoreJitRecorder->IsAtFullCapacity())
+ {
+ m_fRecorderActive = false;
+ }
+ }
+}
+
+
+// static
+bool MulticoreJitManager::IsMethodSupported(MethodDesc * pMethod)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ return pMethod->HasILHeader() &&
+ pMethod->IsTypicalSharedInstantiation() &&
+ ! pMethod->IsDynamicMethod();
+}
+
+
+// static
+// Stop all multicore Jitting profile, called from EEShutDown
+void MulticoreJitManager::StopProfileAll()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(!AppX::IsAppXProcess());
+
+ AppDomainIterator domain(TRUE);
+
+ while (domain.Next())
+ {
+ AppDomain * pDomain = domain.GetDomain();
+
+ if (pDomain != NULL)
+ {
+ pDomain->GetMulticoreJitManager().StopProfile(true);
+ }
+ }
+}
+
+// static
+// Stop all multicore Jitting in the current process, called from ProfilingAPIUtility::LoadProfiler
+void MulticoreJitManager::DisableMulticoreJit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+#ifdef PROFILING_SUPPORTED
+
+ AppDomainIterator domain(TRUE);
+
+ while (domain.Next())
+ {
+ AppDomain * pDomain = domain.GetDomain();
+
+ if (pDomain != NULL)
+ {
+ pDomain->GetMulticoreJitManager().AbortProfile();
+ }
+ }
+
+#endif
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// MultiCore JIT
+//
+// Arguments:
+// wszProfile - profile name
+// ptrNativeAssemblyLoadContext - the binding context
+//
+void QCALLTYPE MultiCoreJITNative::InternalStartProfile(__in_z LPCWSTR wszProfile, INT_PTR ptrNativeAssemblyLoadContext)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ AppDomain * pDomain = GetAppDomain();
+
+ ICLRPrivBinder *pBinderContext = reinterpret_cast<ICLRPrivBinder *>(ptrNativeAssemblyLoadContext);
+
+ pDomain->GetMulticoreJitManager().StartProfile(
+ pDomain,
+ pBinderContext,
+ wszProfile);
+
+ END_QCALL;
+}
+
+
+void QCALLTYPE MultiCoreJITNative::InternalSetProfileRoot(__in_z LPCWSTR wszProfilePath)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ AppDomain * pDomain = GetAppDomain();
+
+ pDomain->GetMulticoreJitManager().SetProfileRoot(pDomain, wszProfilePath);
+
+ END_QCALL;
+}