summaryrefslogtreecommitdiff
path: root/src/vm/perfmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/perfmap.cpp')
-rw-r--r--src/vm/perfmap.cpp324
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