diff options
Diffstat (limited to 'src/ToolBox/superpmi/superpmi/jitinstance.cpp')
-rw-r--r-- | src/ToolBox/superpmi/superpmi/jitinstance.cpp | 436 |
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, ¶m) + { + 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(¶m.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); +} |