diff options
Diffstat (limited to 'src/vm/perfmap.cpp')
-rw-r--r-- | src/vm/perfmap.cpp | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/vm/perfmap.cpp b/src/vm/perfmap.cpp new file mode 100644 index 0000000000..b664b72d7a --- /dev/null +++ b/src/vm/perfmap.cpp @@ -0,0 +1,324 @@ +// 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: perfmap.cpp +// + +#include "common.h" + +#if defined(FEATURE_PERFMAP) && !defined(DACCESS_COMPILE) +#include "perfmap.h" +#include "perfinfo.h" +#include "pal.h" + +PerfMap * PerfMap::s_Current = nullptr; + +// Initialize the map for the process - called from EEStartupHelper. +void PerfMap::Initialize() +{ + LIMITED_METHOD_CONTRACT; + + // Only enable the map if requested. + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled)) + { + // Get the current process id. + int currentPid = GetCurrentProcessId(); + + // Create the map. + s_Current = new PerfMap(currentPid); + } +} + +// Destroy the map for the process - called from EEShutdownHelper. +void PerfMap::Destroy() +{ + LIMITED_METHOD_CONTRACT; + + if (s_Current != nullptr) + { + delete s_Current; + s_Current = nullptr; + } +} + +// Construct a new map for the process. +PerfMap::PerfMap(int pid) +{ + LIMITED_METHOD_CONTRACT; + + // Initialize with no failures. + m_ErrorEncountered = false; + + // Build the path to the map file on disk. + WCHAR tempPath[MAX_LONGPATH+1]; + if(!GetTempPathW(MAX_LONGPATH, tempPath)) + { + return; + } + + SString path; + path.Printf("%Sperf-%d.map", &tempPath, pid); + + // Open the map file for writing. + OpenFile(path); + + m_PerfInfo = new PerfInfo(pid); +} + +// Construct a new map without a specified file name. +// Used for offline creation of NGEN map files. +PerfMap::PerfMap() + : m_FileStream(nullptr) + , m_PerfInfo(nullptr) +{ + LIMITED_METHOD_CONTRACT; + + // Initialize with no failures. + m_ErrorEncountered = false; +} + +// Clean-up resources. +PerfMap::~PerfMap() +{ + LIMITED_METHOD_CONTRACT; + + delete m_FileStream; + m_FileStream = nullptr; + + delete m_PerfInfo; + m_PerfInfo = nullptr; +} + +// Open the specified destination map file. +void PerfMap::OpenFile(SString& path) +{ + STANDARD_VM_CONTRACT; + + // Open the file stream. + m_FileStream = new (nothrow) CFileStream(); + if(m_FileStream != nullptr) + { + HRESULT hr = m_FileStream->OpenForWrite(path.GetUnicode()); + if(FAILED(hr)) + { + delete m_FileStream; + m_FileStream = nullptr; + } + } +} + +// Write a line to the map file. +void PerfMap::WriteLine(SString& line) +{ + STANDARD_VM_CONTRACT; + + EX_TRY + { + // Write the line. + // The PAL already takes a lock when writing, so we don't need to do so here. + StackScratchBuffer scratch; + const char * strLine = line.GetANSI(scratch); + ULONG inCount = line.GetCount(); + ULONG outCount; + m_FileStream->Write(strLine, inCount, &outCount); + + if (inCount != outCount) + { + // This will cause us to stop writing to the file. + // The file will still remain open until shutdown so that we don't have to take a lock at this level when we touch the file stream. + m_ErrorEncountered = true; + } + + } + EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + +// Log a method to the map. +void PerfMap::LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize) +{ + CONTRACTL{ + THROWS; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + PRECONDITION(pMethod != nullptr); + PRECONDITION(pCode != nullptr); + PRECONDITION(codeSize > 0); + } CONTRACTL_END; + + if (m_FileStream == nullptr || m_ErrorEncountered) + { + // A failure occurred, do not log. + return; + } + + // Logging failures should not cause any exceptions to flow upstream. + EX_TRY + { + // Get the full method signature. + SString fullMethodSignature; + pMethod->GetFullMethodInfo(fullMethodSignature); + + // Build the map file line. + StackScratchBuffer scratch; + SString line; + line.Printf("%p %x %s\n", pCode, codeSize, fullMethodSignature.GetANSI(scratch)); + + // Write the line. + WriteLine(line); + } + EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + + +void PerfMap::LogImageLoad(PEFile * pFile) +{ + if (s_Current != nullptr) + { + s_Current->LogImage(pFile); + } +} + +// Log an image load to the map. +void PerfMap::LogImage(PEFile * pFile) +{ + CONTRACTL{ + THROWS; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + PRECONDITION(pFile != nullptr); + } CONTRACTL_END; + + + if (m_FileStream == nullptr || m_ErrorEncountered) + { + // A failure occurred, do not log. + return; + } + + EX_TRY + { + WCHAR wszSignature[39]; + GetNativeImageSignature(pFile, wszSignature, lengthof(wszSignature)); + + m_PerfInfo->LogImage(pFile, wszSignature); + } + EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); +} + + +// Log a method to the map. +void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize) +{ + LIMITED_METHOD_CONTRACT; + + if (s_Current != nullptr) + { + s_Current->LogMethod(pMethod, pCode, codeSize); + } +} + +void PerfMap::GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned int nSigSize) +{ + CONTRACTL{ + PRECONDITION(pFile != nullptr); + PRECONDITION(pwszSig != nullptr); + PRECONDITION(nSigSize >= 39); + } CONTRACTL_END; + + // We use the MVID as the signature, since ready to run images + // don't have a native image signature. + GUID mvid; + pFile->GetMVID(&mvid); + if(!StringFromGUID2(mvid, pwszSig, nSigSize)) + { + pwszSig[0] = '\0'; + } +} + +// Create a new native image perf map. +NativeImagePerfMap::NativeImagePerfMap(Assembly * pAssembly, BSTR pDestPath) + : PerfMap() +{ + STANDARD_VM_CONTRACT; + + // Generate perfmap path. + + // Get the assembly simple name. + LPCUTF8 lpcSimpleName = pAssembly->GetSimpleName(); + + // Get the native image signature (GUID). + // Used to ensure that we match symbols to the correct NGEN image. + WCHAR wszSignature[39]; + GetNativeImageSignature(pAssembly->GetManifestFile(), wszSignature, lengthof(wszSignature)); + + // Build the path to the perfmap file, which consists of <inputpath><imagesimplename>.ni.<signature>.map. + // Example: /tmp/mscorlib.ni.{GUID}.map + SString sDestPerfMapPath; + sDestPerfMapPath.Printf("%S%s.ni.%S.map", pDestPath, lpcSimpleName, wszSignature); + + // Open the perf map file. + OpenFile(sDestPerfMapPath); +} + +// Log data to the perfmap for the specified module. +void NativeImagePerfMap::LogDataForModule(Module * pModule) +{ + STANDARD_VM_CONTRACT; + + PEImageLayout * pLoadedLayout = pModule->GetFile()->GetLoaded(); + _ASSERTE(pLoadedLayout != nullptr); + + SIZE_T baseAddr = (SIZE_T)pLoadedLayout->GetBase(); + +#ifdef FEATURE_READYTORUN_COMPILER + if (pLoadedLayout->HasReadyToRunHeader()) + { + ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo()); + while (mi.Next()) + { + MethodDesc *hotDesc = mi.GetMethodDesc(); + + LogPreCompiledMethod(hotDesc, mi.GetMethodStartAddress(), baseAddr); + } + } + else +#endif // FEATURE_READYTORUN_COMPILER + { + MethodIterator mi((PTR_Module)pModule); + while (mi.Next()) + { + MethodDesc *hotDesc = mi.GetMethodDesc(); + hotDesc->CheckRestore(); + + LogPreCompiledMethod(hotDesc, mi.GetMethodStartAddress(), baseAddr); + } + } +} + +// Log a pre-compiled method to the perfmap. +void NativeImagePerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode, SIZE_T baseAddr) +{ + STANDARD_VM_CONTRACT; + + // Get information about the NGEN'd method code. + EECodeInfo codeInfo(pCode); + _ASSERTE(codeInfo.IsValid()); + + IJitManager::MethodRegionInfo methodRegionInfo; + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + + // NGEN can split code between hot and cold sections which are separate in memory. + // Emit an entry for each section if it is used. + if (methodRegionInfo.hotSize > 0) + { + LogMethod(pMethod, (PCODE)methodRegionInfo.hotStartAddress - baseAddr, methodRegionInfo.hotSize); + } + + if (methodRegionInfo.coldSize > 0) + { + LogMethod(pMethod, (PCODE)methodRegionInfo.coldStartAddress - baseAddr, methodRegionInfo.coldSize); + } +} + +#endif // FEATURE_PERFMAP && !DACCESS_COMPILE |