Adds support for resolving JIT compiled managed call frames in perf_events.

This commit is contained in:
Brian Robbins 2015-04-13 16:18:16 -07:00
Родитель 1426853c33
Коммит e406060ba1
10 изменённых файлов: 238 добавлений и 3 удалений

Просмотреть файл

@ -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)

Просмотреть файл

@ -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>

Просмотреть файл

@ -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
//

Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -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);

Просмотреть файл

@ -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,

141
src/vm/perfmap.cpp Normal file
Просмотреть файл

@ -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

49
src/vm/perfmap.h Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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
}