Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Initial perf jitdump implementation #26897

Merged
merged 7 commits into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Feedback
Revise PAL API
Add error handling
Clean up #ifdef CROSSGEN
Add COMPlus_PerfMapJitDumpPath
Add placeholder debuginfo and unwindinfo arguments
  • Loading branch information
sdmaclea committed Oct 4, 2019
commit 4c69ef853b6ff888f2a89349ea21b08dd2725974
1 change: 1 addition & 0 deletions Documentation/project-docs/clr-configuration-knobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ Name | Description | Type | Class | Default Value | Flags
`ETWEnabled` | This flag is used on OSes < Vista to enable/disable ETW. It is disabled by default | `DWORD` | `EXTERNAL` | `0` | REGUTIL_default
`MsBetweenAttachCheck` | | `DWORD` | `EXTERNAL` | `500` |
`PerfMapEnabled` | This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default | `DWORD` | `EXTERNAL` | `0` | REGUTIL_default
`PerfMapJitDumpPath` | Specifies a path to write the perf jitdump file. Defaults to GetTempPathA() | `STRING` | `EXTERNAL` | | REGUTIL_default
`PerfMapIgnoreSignal` | When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default | `DWORD` | `EXTERNAL` | `0` | REGUTIL_default
`ProfAPI_AttachProfilerMinTimeoutInMs` | Timeout in ms for the minimum time out value of AttachProfiler | `DWORD` | `EXTERNAL` | `10*1000` |
`ProfAPI_DetachMaxSleepMs` | The maximum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded. | `DWORD` | `EXTERNAL` | `0` |
Expand Down
1 change: 1 addition & 0 deletions src/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("Pro

#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)
CONFIG_STRING_INFO_EX(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to GetTempPathA()", CLRConfig::REGUTIL_default)
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default", CLRConfig::REGUTIL_default)
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.")
RETAIL_CONFIG_STRING_INFO(EXTERNAL_NativeImagePerfMapFormat, W("NativeImagePerfMapFormat"), "Specifies the format of native image perfmap files generated by crossgen. Valid options are RVA or OFFSET.")
Expand Down
32 changes: 14 additions & 18 deletions src/inc/perfjitdump.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,20 @@
#ifndef PERF_JITDUMP_H
#define PERF_JITDUMP_H

struct PerfJitDumpState;

// Generates a perf jitdump file.
class PerfJitDump
{
private:
static PerfJitDumpState& GetState();

public:
// Start the jitdump file
static void Start();

// Log a method to the jitdump file.
static void LogMethod(void* pCode, size_t codeSize, const char* symbol);

// Finish the jitdump file
static void Finish();
};
int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should go to pal.h where all other PAL function declarations are located.

PALAPI
// Start the jitdump file
PAL_PerfJitDump_Start(const char* path);

int
PALAPI
// Log a method to the jitdump file.
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo);

int
PALAPI
// Finish the jitdump file
PAL_PerfJitDump_Finish();

#endif // PERF_JITDUMP_H

171 changes: 124 additions & 47 deletions src/pal/src/misc/perfjitdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
// ===========================================================================

#if defined(__linux__)
#if defined(__linux__) && !defined(CROSSGEN_COMPILE)
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved
#define JITDUMP_SUPPORTED
#endif

Expand All @@ -26,6 +26,7 @@
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
#include <linux/limits.h>

#include "../inc/llvm/ELF.h"

Expand Down Expand Up @@ -141,38 +142,66 @@ struct PerfJitDumpState
pthread_mutex_t mutex;
uint64_t codeIndex;

void Start()
int Start(const char* path)
{
pthread_mutex_lock(&mutex);
int result = 0;

char jitdumpPath[1024];
// Write file header
FileHeader header;

snprintf(jitdumpPath, sizeof(jitdumpPath), "/tmp/jit-%i.dump", getpid());
result = pthread_mutex_lock(&mutex);

fd = open(jitdumpPath, O_CREAT|O_TRUNC|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR );
if (enabled)
goto exit;
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved

// Write file header
FileHeader header;
if (result != 0)
goto exit;

char jitdumpPath[PATH_MAX];

result = snprintf(jitdumpPath, sizeof(jitdumpPath), "%s/jit-%i.dump", path, getpid());

if (result >= PATH_MAX)
goto exit;

result = open(jitdumpPath, O_CREAT|O_TRUNC|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR );

if (result == -1)
goto exit;

fd = result;

write(fd, &header, sizeof(FileHeader));
result = write(fd, &header, sizeof(FileHeader));

fsync(fd);
if (result == -1)
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved
goto exit;

result = fsync(fd);

if (result == -1)
goto exit;

// mmap jitdump file
// this is a marker for perf inject to find the jitdumpfile
mmapAddr = mmap(nullptr, sizeof(FileHeader), PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);

if (mmapAddr == MAP_FAILED)
goto exit;

enabled = true;

pthread_mutex_unlock(&mutex);
exit:
result = pthread_mutex_unlock(&mutex);
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved

return result;
}

void LogMethod(void* pCode, size_t codeSize, const char* symbol)
int LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo)
{
int result = 0;

if (enabled)
{
pthread_mutex_lock(&mutex);

size_t symbolLen = strlen(symbol);

JitCodeLoadRecord record;
Expand All @@ -182,86 +211,134 @@ struct PerfJitDumpState
record.code_size = codeSize;
record.code_index = ++codeIndex;
record.header.total_size = sizeof(JitCodeLoadRecord) + symbolLen + 1 + codeSize;

result = pthread_mutex_lock(&mutex);

if (result != 0)
goto exit;

if (!enabled)
goto exit;

// ToDo write debugInfo and unwindInfo immediately before the JitCodeLoadRecord (while lock is held).

record.header.timestamp = GetTimeStampNS();

write(fd, &record, sizeof(JitCodeLoadRecord));
result = write(fd, &record, sizeof(JitCodeLoadRecord));

write(fd, symbol, symbolLen + 1);
if (result == -1)
goto exit;

write(fd, pCode, codeSize);
result = write(fd, symbol, symbolLen + 1);

fsync(fd);
if (result == -1)
goto exit;

pthread_mutex_unlock(&mutex);
result = write(fd, pCode, codeSize);

if (result == -1)
goto exit;

result = fsync(fd);

if (result == -1)
goto exit;

exit:
if (result != 0)
enabled = false;

result = pthread_mutex_unlock(&mutex);
}
return result;
}

void Finish()
int Finish()
{
int result = 0;

if (enabled)
{
enabled = false;

// Lock the mutex
pthread_mutex_lock(&mutex);
result = pthread_mutex_lock(&mutex);

enabled = false;
if (result != 0)
goto exit;

result = munmap(mmapAddr, sizeof(FileHeader));

if (result == -1)
goto exit;

munmap(mmapAddr, sizeof(FileHeader));
result = fsync(fd);

fsync(fd);
if (result == -1)
goto exit;

close(fd);
result = close(fd);

pthread_mutex_unlock(&mutex);
if (result == -1)
goto exit;

exit:
result = pthread_mutex_unlock(&mutex);
}
return result;
}
};


PerfJitDumpState& PerfJitDump::GetState()
PerfJitDumpState& GetState()
{
static PerfJitDumpState s;

return s;
}

void PerfJitDump::Start()
int
PALAPI
PAL_PerfJitDump_Start(const char* path)
{
GetState().Start();
return GetState().Start(path);
}

void PerfJitDump::LogMethod(void* pCode, size_t codeSize, const char* symbol)
int
PALAPI
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo)
{
GetState().LogMethod(pCode, codeSize, symbol);
return GetState().LogMethod(pCode, codeSize, symbol, debugInfo, unwindInfo);
}

void PerfJitDump::Finish()
int
PALAPI
PAL_PerfJitDump_Finish()
{
GetState().Finish();
return GetState().Finish();
}

#else // JITDUMP_SUPPORTED

struct PerfJitDumpState
{
};

PerfJitDumpState& PerfJitDump::GetState()
{
static PerfJitDumpState s;

return s;
}

void PerfJitDump::Start()
int
PALAPI
PAL_PerfJitDump_Start(const char* path)
{
return 0;
}

void PerfJitDump::LogMethod(void* pCode, size_t codeSize, const char* symbol)
int
PALAPI
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo)
{
return 0;
}

void PerfJitDump::Finish()
int
PALAPI
PAL_PerfJitDump_Finish()
{
return 0;
}

#endif // JITDUMP_SUPPORTED
Loading