summaryrefslogtreecommitdiff
path: root/src/ToolBox/superpmi/superpmi/jitinstance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/superpmi/superpmi/jitinstance.cpp')
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.cpp436
1 files changed, 436 insertions, 0 deletions
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.cpp b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
new file mode 100644
index 0000000000..5003e91f96
--- /dev/null
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
@@ -0,0 +1,436 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "standardpch.h"
+#include "superpmi.h"
+#include "jitinstance.h"
+#include "coreclrcallbacks.h"
+#include "icorjitinfo.h"
+#include "jithost.h"
+#include "errorhandling.h"
+#include "spmiutil.h"
+
+JitInstance *JitInstance::InitJit(char *nameOfJit, bool breakOnAssert, SimpleTimer *st1, MethodContext* firstContext)
+{
+ JitInstance *jit = new JitInstance();
+ if (jit == nullptr)
+ {
+ LogError("Failed to allocate a JitInstance");
+ return nullptr;
+ }
+
+ if (st1 != nullptr)
+ st1->Start();
+ HRESULT hr = jit->StartUp(nameOfJit, false, breakOnAssert, firstContext);
+ if (st1 != nullptr)
+ st1->Stop();
+ if (hr != S_OK)
+ {
+ LogError("Startup of JIT(%s) failed %d", nameOfJit, hr);
+ return nullptr;
+ }
+ if (st1 != nullptr)
+ LogVerbose("Jit startup took %fms", st1->GetMilliseconds());
+ return jit;
+}
+
+HRESULT JitInstance::StartUp(char * PathToJit, bool copyJit, bool parambreakOnDebugBreakorAV, MethodContext* firstContext)
+{
+ //startup jit
+ DWORD dwRetVal = 0;
+ UINT uRetVal = 0;
+ BOOL bRetVal = FALSE;
+
+ breakOnDebugBreakorAV = parambreakOnDebugBreakorAV;
+
+ char pFullPathName[MAX_PATH];
+ char lpTempPathBuffer[MAX_PATH];
+ char szTempFileName[MAX_PATH];
+
+//Get an allocator instance
+//Note: we do this to keep cleanup somewhat simple...
+ ourHeap = ::HeapCreate(0,0,0);
+ if(ourHeap == nullptr)
+ {
+ LogError("Failed to get a new heap (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//find the full jit path
+ dwRetVal = ::GetFullPathNameA(PathToJit, MAX_PATH, pFullPathName, nullptr);
+ if (dwRetVal == 0)
+ {
+ LogError("GetFullPathName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//Store the full path to the jit
+ PathToOriginalJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToOriginalJit == nullptr)
+ {
+ LogError("1st HeapAlloc failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToOriginalJit, MAX_PATH, pFullPathName);
+
+ if(copyJit)
+ {
+ //Get a temp file location
+ dwRetVal = ::GetTempPathA(MAX_PATH, lpTempPathBuffer);
+ if (dwRetVal == 0)
+ {
+ LogError("GetTempPath failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ if (dwRetVal > MAX_PATH)
+ {
+ LogError("GetTempPath returned a path that was larger than MAX_PATH");
+ return E_FAIL;
+ }
+ //Get a temp filename
+ uRetVal = ::GetTempFileNameA(lpTempPathBuffer, "Jit", 0, szTempFileName);
+ if (uRetVal == 0)
+ {
+ LogError("GetTempFileName failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ dwRetVal = (DWORD)::strlen(szTempFileName);
+
+ //Store the full path to the temp jit
+ PathToTempJit = (char *)::HeapAlloc(ourHeap, 0, MAX_PATH);
+ if(PathToTempJit == nullptr)
+ {
+ LogError("2nd HeapAlloc failed 0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ ::strcpy_s(PathToTempJit, MAX_PATH, szTempFileName);
+
+ //Copy Temp File
+ bRetVal = ::CopyFileA(PathToOriginalJit, PathToTempJit, FALSE);
+ if (bRetVal == FALSE)
+ {
+ LogError("CopyFile failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+ }
+ else
+ PathToTempJit = PathToOriginalJit;
+
+#ifndef FEATURE_PAL // No file version APIs in the PAL
+ //Do a quick version check
+ DWORD dwHandle = 0;
+ DWORD fviSize = GetFileVersionInfoSizeA(PathToTempJit, &dwHandle);
+
+ if ((fviSize != 0)&&(dwHandle==0))
+ {
+ unsigned char *fviData = new unsigned char[fviSize];
+ if (GetFileVersionInfoA(PathToTempJit, dwHandle, fviSize, fviData))
+ {
+ UINT size = 0;
+ VS_FIXEDFILEINFO *verInfo = nullptr;
+ if (VerQueryValueA(fviData, "\\", (LPVOID*)&verInfo, &size))
+ {
+ if (size)
+ {
+ if (verInfo->dwSignature == 0xfeef04bd)
+ LogDebug("'%s' is version %u.%u.%u.%u", PathToTempJit,
+ (verInfo->dwFileVersionMS)>>16, (verInfo->dwFileVersionMS)&0xFFFF,
+ (verInfo->dwFileVersionLS)>>16, (verInfo->dwFileVersionLS)&0xFFFF);
+ }
+ }
+ }
+ delete[] fviData;
+ }
+#endif // !FEATURE_PAL
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return E_FAIL;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return -1;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return -1;
+ }
+
+ // Check the JIT version identifier.
+
+ GUID versionId;
+ memset(&versionId, 0, sizeof(GUID));
+ pJitInstance->getVersionIdentifier(&versionId);
+
+ if (memcmp(&versionId, &JITEEVersionIdentifier, sizeof(GUID)) != 0)
+ {
+ // Mismatched version ID. Fail the load.
+ pJitInstance = NULL;
+
+ LogError("Jit Compiler has wrong version identifier");
+ return -1;
+ }
+
+
+ icji = InitICorJitInfo(this);
+
+ return S_OK;
+}
+
+bool JitInstance::reLoad(MethodContext* firstContext)
+{
+ FreeLibrary(hLib);
+
+//Load Library
+ hLib = ::LoadLibraryA(PathToTempJit);
+ if(hLib == 0)
+ {
+ LogError("LoadLibrary failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+
+//get entry points
+ pngetJit = (PgetJit)::GetProcAddress(hLib, "getJit");
+ if(pngetJit == 0)
+ {
+ LogError("GetProcAddress 'getJit' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnsxsJitStartup = (PsxsJitStartup)::GetProcAddress(hLib, "sxsJitStartup");
+ if(pnsxsJitStartup == 0)
+ {
+ LogError("GetProcAddress 'sxsJitStartup' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ pnjitStartup = (PjitStartup)::GetProcAddress(hLib, "jitStartup");
+
+ //Setup CoreClrCallbacks and call sxsJitStartup
+ CoreClrCallbacks *cccallbacks = InitCoreClrCallbacks();
+ pnsxsJitStartup(*cccallbacks);
+
+ // Setup ICorJitHost and call jitStartup if necessary
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ jitHost = new JitHost(*this);
+ pnjitStartup(jitHost);
+ }
+
+ pJitInstance = pngetJit();
+ if(pJitInstance == nullptr)
+ {
+ LogError("pngetJit gave us null");
+ return false;
+ }
+
+ icji = InitICorJitInfo(this);
+
+ return true;
+}
+
+JitInstance::Result JitInstance::CompileMethod(MethodContext *MethodToCompile, int mcIndex, bool collectThroughput)
+{
+ struct Param : FilterSuperPMIExceptionsParam_CaptureException
+ {
+ JitInstance* pThis;
+ JitInstance::Result result;
+ CORINFO_METHOD_INFO info;
+ unsigned flags;
+ int mcIndex;
+ bool collectThroughput;
+ } param;
+ param.pThis = this;
+ param.result = RESULT_SUCCESS; // assume success
+ param.flags = 0;
+ param.mcIndex = mcIndex;
+ param.collectThroughput = collectThroughput;
+
+ //store to instance field our raw values, so we can figure things out a bit later...
+ mc = MethodToCompile;
+
+ times[0] = 0;
+ times[1] = 0;
+
+ mc->repEnvironmentSet(); //Sets envvars
+ stj.Start();
+
+ PAL_TRY(Param*, pParam, &param)
+ {
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ pParam->pThis->mc->repCompileMethod(&pParam->info, &pParam->flags);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Start();
+ }
+ CorJitResult temp = pParam->pThis->pJitInstance->compileMethod(pParam->pThis->icji, &pParam->info, pParam->flags, &NEntryBlock, &NCodeSizeBlock);
+ if (pParam->collectThroughput)
+ {
+ pParam->pThis->lt.Stop();
+ pParam->pThis->times[0] = pParam->pThis->lt.GetCycles();
+ }
+ if ((SpmiTargetArchitecture == SPMI_TARGET_ARCHITECTURE_ARM64) && (temp == CORJIT_SKIPPED))
+ {
+ // For altjit, treat SKIPPED as OK
+ temp = CORJIT_OK;
+ }
+ if (temp == CORJIT_OK)
+ {
+ //capture the results of compilation
+ pParam->pThis->mc->cr->recCompileMethod(&NEntryBlock, &NCodeSizeBlock, temp);
+ pParam->pThis->mc->cr->recAllocMemCapture();
+ pParam->pThis->mc->cr->recAllocGCInfoCapture();
+
+ pParam->pThis->mc->cr->recMessageLog("Successful Compile");
+ }
+ else
+ {
+ LogDebug("compileMethod failed with result %d", temp);
+ pParam->result = RESULT_ERROR;
+ }
+ }
+ PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndStop)
+ {
+ SpmiException e(&param.exceptionPointers);
+
+ if (e.GetCode() == EXCEPTIONCODE_MC)
+ {
+ char *message = e.GetExceptionMessage();
+ LogMissing("Method context %d failed to replay: %s", mcIndex, message);
+ e.DeleteMessage();
+ param.result = RESULT_MISSING;
+ }
+ else
+ {
+ e.ShowAndDeleteMessage();
+ param.result = RESULT_ERROR;
+ }
+ }
+ PAL_ENDTRY
+
+ stj.Stop();
+ if (collectThroughput)
+ {
+ // If we get here, we know it compiles
+ timeResult(param.info, param.flags);
+ }
+ mc->repEnvironmentUnset(); //Unsets envvars
+
+ mc->cr->secondsToCompile = stj.GetSeconds();
+
+ return param.result;
+}
+
+void JitInstance::timeResult(CORINFO_METHOD_INFO info, unsigned flags)
+{
+ BYTE *NEntryBlock = nullptr;
+ ULONG NCodeSizeBlock = 0;
+
+ int sampleSize = 10;
+ // Save 2 smallest times. To help reduce noise, we will look at the closest pair of these.
+ unsigned __int64 time;
+
+ for (int i = 0; i < sampleSize; i++)
+ {
+ delete mc->cr;
+ mc->cr = new CompileResult();
+ lt.Start();
+ pJitInstance->compileMethod(icji, &info, flags, &NEntryBlock, &NCodeSizeBlock);
+ lt.Stop();
+ time = lt.GetCycles();
+ if (times[1] == 0)
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ else if (time < times[1])
+ {
+ if (time < times[0])
+ {
+ times[1] = times[0];
+ times[0] = time;
+ }
+ else
+ times[1] = time;
+ }
+ }
+}
+
+/*-------------------------- Misc ---------------------------------------*/
+
+// Used to allocate memory that needs to handed to the EE.
+// For eg, use this to allocated memory for reporting debug info,
+// which will be handed to the EE by setVars() and setBoundaries()
+void * JitInstance::allocateArray(
+ ULONG cBytes
+ )
+{
+ mc->cr->AddCall("allocateArray");
+ return HeapAlloc(mc->cr->getCodeHeap(),0,cBytes);
+}
+
+// Used to allocate memory that needs to live as long as the jit
+// instance does.
+void * JitInstance::allocateLongLivedArray(
+ ULONG cBytes
+ )
+{
+ return HeapAlloc(ourHeap,0,cBytes);
+}
+
+// JitCompiler will free arrays passed by the EE using this
+// For eg, The EE returns memory in getVars() and getBoundaries()
+// to the JitCompiler, which the JitCompiler should release using
+// freeArray()
+void JitInstance::freeArray(
+ void *array
+ )
+{
+ mc->cr->AddCall("freeArray");
+ HeapFree(mc->cr->getCodeHeap(),0,array);
+}
+
+// Used to free memory allocated by JitInstance::allocateLongLivedArray.
+void JitInstance::freeLongLivedArray(
+ void *array
+ )
+{
+ HeapFree(ourHeap,0,array);
+}