summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Robbins <brianrob@microsoft.com>2015-04-13 16:18:16 -0700
committerBrian Robbins <brianrob@microsoft.com>2015-04-22 08:47:12 -0700
commite406060ba14e532d964dbe64be23afe1b510aa6f (patch)
treea3b5c1719a6270da2ce9ee49548a164b190c3f40
parent1426853c339e0c101e9301bf442c94e2afb7555f (diff)
downloadcoreclr-e406060ba14e532d964dbe64be23afe1b510aa6f.tar.gz
coreclr-e406060ba14e532d964dbe64be23afe1b510aa6f.tar.bz2
coreclr-e406060ba14e532d964dbe64be23afe1b510aa6f.zip
Adds support for resolving JIT compiled managed call frames in perf_events.
-rw-r--r--CMakeLists.txt3
-rw-r--r--clr.defines.targets1
-rw-r--r--src/inc/clrconfigvalues.h4
-rw-r--r--src/vm/CMakeLists.txt1
-rw-r--r--src/vm/ceemain.cpp13
-rw-r--r--src/vm/jitinterface.cpp10
-rw-r--r--src/vm/jitinterface.h2
-rw-r--r--src/vm/perfmap.cpp141
-rw-r--r--src/vm/perfmap.h49
-rw-r--r--src/vm/prestub.cpp17
10 files changed, 238 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9b2786fab0..ca7bc3a1b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -455,6 +455,9 @@ if(WIN32)
add_definitions(-DFEATURE_STRONGNAME_TESTKEY_ALLOWED)
endif(WIN32)
add_definitions(-DFEATURE_SVR_GC)
+if(CLR_CMAKE_PLATFORM_LINUX)
+ add_definitions(-DFEATURE_PERFMAP)
+endif(CLR_CMAKE_PLATFORM_LINUX)
add_definitions(-DFEATURE_SYNTHETIC_CULTURES)
add_definitions(-DFEATURE_VERSIONING)
if(WIN32)
diff --git a/clr.defines.targets b/clr.defines.targets
index 0a64d551be..87ec8ace0a 100644
--- a/clr.defines.targets
+++ b/clr.defines.targets
@@ -102,6 +102,7 @@
<CDefines Condition="'$(FeatureStrongnameMigration)' == 'true'">$(CDefines);FEATURE_STRONGNAME_MIGRATION</CDefines>
<CDefines Condition="'$(FeatureStrongnameTestkeyAllowed)' == 'true'">$(CDefines);FEATURE_STRONGNAME_TESTKEY_ALLOWED</CDefines>
<CDefines Condition="'$(FeatureSvrGc)' == 'true'">$(CDefines);FEATURE_SVR_GC</CDefines>
+ <CDefines Condition="'$(FeaturePerfMap)' == 'true'">$(CDefines);FEATURE_PERFMAP</CDefines>
<CDefines Condition="'$(FeatureSynchronizationcontextWait)' == 'true'">$(CDefines);FEATURE_SYNCHRONIZATIONCONTEXT_WAIT</CDefines>
<CDefines Condition="'$(FeatureSyntheticCultures)' == 'true'">$(CDefines);FEATURE_SYNTHETIC_CULTURES</CDefines>
<CDefines Condition="'$(FeatureTypeEquivalence)' == 'true'">$(CDefines);FEATURE_TYPEEQUIVALENCE</CDefines>
diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h
index 75273cb1f6..75836e0c07 100644
--- a/src/inc/clrconfigvalues.h
+++ b/src/inc/clrconfigvalues.h
@@ -843,6 +843,10 @@ RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_VistaAndAboveETWEnabled, W("ETWEnabled"), 1
RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W("ETW_ObjectAllocationEventsPerTypePerSec"), "Desired number of GCSampledObjectAllocation ETW events to be logged per type per second. If 0, then the default built in to the implementation for the enabled event (e.g., High, Low), will be used.", CLRConfig::REGUTIL_default)
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("ProfAPI_ValidateNGENInstrumentation"), 0, "This flag enables additional validations when using the IMetaDataEmit APIs for NGEN'ed images to ensure only supported edits are made.")
+#ifdef FEATURE_PERFMAP
+RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default", CLRConfig::REGUTIL_default)
+#endif
+
//
// Shim
//
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 62ec27fcf0..c29ae78184 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -94,6 +94,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
pefile.cpp
peimage.cpp
peimagelayout.cpp
+ perfmap.cpp
precode.cpp
prestub.cpp
rejit.cpp
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index 8721b54714..55909eefba 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -252,6 +252,10 @@
#include <mscoruefwrapper.h>
#endif // FEATURE_UEF_CHAINMANAGER
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
#ifdef FEATURE_IPCMAN
static HRESULT InitializeIPCManager(void);
static void PublishIPCManager(void);
@@ -926,6 +930,10 @@ void EEStartupHelper(COINITIEE fFlags)
PerfLog::PerfLogInitialize();
#endif //ENABLE_PERF_LOG
+#ifdef FEATURE_PERFMAP
+ PerfMap::Initialize();
+#endif
+
STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "===================EEStartup Starting===================");
#ifndef CROSSGEN_COMPILE
@@ -1907,6 +1915,11 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading)
ETW::TypeSystemLog::FlushObjectAllocationEvents();
#endif // FEATURE_EVENT_TRACE
+#ifdef FEATURE_PERFMAP
+ // Flush and close the perf map file.
+ PerfMap::Destroy();
+#endif
+
#ifdef FEATURE_PREJIT
// If we're doing basic block profiling, we need to write the log files to disk.
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index a50590aac9..8af3fd9057 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -12533,7 +12533,7 @@ BOOL g_fAllowRel32 = TRUE;
// are OK since they discard the return value of this method.
PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader,
- DWORD flags, DWORD flags2)
+ DWORD flags, DWORD flags2, ULONG * pSizeOfCode)
{
STANDARD_VM_CONTRACT;
@@ -12785,6 +12785,14 @@ PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader,
(MethodDesc*)ftn);
LOG((LF_CORDB, LL_EVERYTHING, "Got through CallCompile MethodWithSEHWrapper\n"));
+#if FEATURE_PERFMAP
+ // Save the code size so that it can be reported to the perfmap.
+ if (pSizeOfCode != NULL)
+ {
+ *pSizeOfCode = sizeOfCode;
+ }
+#endif
+
#if defined(ENABLE_PERF_COUNTERS)
LARGE_INTEGER CycleStop;
QueryPerformanceCounter(&CycleStop);
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index 8143d4830d..5977b176d9 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -46,7 +46,7 @@ void InitJITHelpers1();
void InitJITHelpers2();
PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* header,
- DWORD flags, DWORD flags2);
+ DWORD flags, DWORD flags2, ULONG* sizeOfCode = NULL);
void getMethodInfoHelper(MethodDesc * ftn,
CORINFO_METHOD_HANDLE ftnHnd,
diff --git a/src/vm/perfmap.cpp b/src/vm/perfmap.cpp
new file mode 100644
index 0000000000..ccb511fc86
--- /dev/null
+++ b/src/vm/perfmap.cpp
@@ -0,0 +1,141 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ===========================================================================
+// File: perfmap.cpp
+//
+
+#include "common.h"
+
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#include "pal.h"
+
+PerfMap * PerfMap::s_Current = NULL;
+
+// 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);
+ }
+}
+
+// Log a method to the map.
+void PerfMap::LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (s_Current != NULL)
+ {
+ s_Current->Log(pMethod, pCode, codeSize);
+ }
+}
+
+// Destroy the map for the process - called from EEShutdownHelper.
+void PerfMap::Destroy()
+{
+ if (s_Current != NULL)
+ {
+ delete s_Current;
+ s_Current = NULL;
+ }
+}
+
+// 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_PATH+1];
+ if(!GetTempPathW(MAX_PATH, tempPath))
+ {
+ return;
+ }
+
+ SString path;
+ path.Printf("%Sperf-%d.map", &tempPath, pid);
+
+ // Open the file stream.
+ m_FileStream = new (nothrow) CFileStream();
+ if(m_FileStream != NULL)
+ {
+ HRESULT hr = m_FileStream->OpenForWrite(path.GetUnicode());
+ if(FAILED(hr))
+ {
+ delete m_FileStream;
+ m_FileStream = NULL;
+ }
+ }
+}
+
+// Clean-up resources.
+PerfMap::~PerfMap()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ delete m_FileStream;
+ m_FileStream = NULL;
+}
+
+// Log a method to the map.
+void PerfMap::Log(MethodDesc * pMethod, PCODE pCode, size_t codeSize)
+{
+ CONTRACTL{
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pMethod != NULL);
+ PRECONDITION(pCode != NULL);
+ PRECONDITION(codeSize > 0);
+ } CONTRACTL_END;
+
+ if (m_FileStream == NULL || 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.
+ // The PAL already takes a lock when writing, so we don't need to do so here.
+ 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 levelwhen we touch the file stream.
+ m_ErrorEncountered = true;
+ }
+ }
+ EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
+}
+
+#endif // FEATURE_PERFMAP
diff --git a/src/vm/perfmap.h b/src/vm/perfmap.h
new file mode 100644
index 0000000000..e0539150fc
--- /dev/null
+++ b/src/vm/perfmap.h
@@ -0,0 +1,49 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ===========================================================================
+// File: perfmap.h
+//
+#ifndef PERFPID_H
+#define PERFPID_H
+
+#include "sstring.h"
+#include "fstream.h"
+
+class PerfMap
+{
+private:
+ // The one and only PerfMap for the process.
+ static PerfMap * s_Current;
+
+ // The file stream to write the map to.
+ CFileStream * m_FileStream;
+
+ // Set to true if an error is encountered when writing to the file.
+ bool m_ErrorEncountered;
+
+ // Construct a new map.
+ PerfMap(int pid);
+
+ // Clean-up resources.
+ ~PerfMap();
+
+ // Does the actual work to log to the map.
+ void Log(MethodDesc * pMethod, PCODE pCode, size_t codeSize);
+
+ // Does the actual work to close and flush the map.
+ void Close();
+
+public:
+ // Initialize the map for the current process.
+ static void Initialize();
+
+ // Log a method to the map.
+ static void LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize);
+
+ // Close the map and flush any remaining data.
+ static void Destroy();
+};
+
+#endif // PERFPID_H
diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
index 79c34d27da..e740c9a69d 100644
--- a/src/vm/prestub.cpp
+++ b/src/vm/prestub.cpp
@@ -49,6 +49,10 @@
#include "stacksampler.h"
#endif
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
#ifndef DACCESS_COMPILE
EXTERN_C void STDCALL ThePreStub();
@@ -262,6 +266,7 @@ PCODE MethodDesc::MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, DWORD flags, DWO
m_pszDebugMethodName));
PCODE pCode = NULL;
+ ULONG sizeOfCode = 0;
#ifdef FEATURE_INTERPRETER
PCODE pPreviousInterpStub = NULL;
BOOL fInterpreted = FALSE;
@@ -454,7 +459,7 @@ PCODE MethodDesc::MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, DWORD flags, DWO
EX_TRY
{
- pCode = UnsafeJitFunction(this, ILHeader, flags, flags2);
+ pCode = UnsafeJitFunction(this, ILHeader, flags, flags2, &sizeOfCode);
}
EX_CATCH
{
@@ -514,6 +519,11 @@ PCODE MethodDesc::MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, DWORD flags, DWO
{
// Fire an ETW event to mark the end of JIT'ing
ETW::MethodLog::MethodJitted(this, &namespaceOrClassName, &methodName, &methodSignature, pCode, 0 /* ReJITID */);
+
+#ifdef FEATURE_PERFMAP
+ // Save the JIT'd method information so that perf can resolve JIT'd call frames.
+ PerfMap::LogMethod(this, pCode, sizeOfCode);
+#endif
mcJitManager.GetMulticoreJitCodeStorage().StoreMethodCode(this, pCode);
@@ -593,6 +603,11 @@ GotNewCode:
{
// Fire an ETW event to mark the end of JIT'ing
ETW::MethodLog::MethodJitted(this, &namespaceOrClassName, &methodName, &methodSignature, pCode, 0 /* ReJITID */);
+
+#ifdef FEATURE_PERFMAP
+ // Save the JIT'd method information so that perf can resolve JIT'd call frames.
+ PerfMap::LogMethod(this, pCode, sizeOfCode);
+#endif
}