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
Next Next commit
Initial perf jitdump implementation
  • Loading branch information
sdmaclea committed Oct 3, 2019
commit 3b0148c2641d71b7af8d7b3d2738e8c92dc6d109
29 changes: 29 additions & 0 deletions src/inc/perfjitdump.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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.
// ===========================================================================

#ifndef PERF_JITDUMP_H
#define PERF_JITDUMP_H

struct PerfJitDumpState;
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved

// 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();
};

#endif // PERF_JITDUMP_H

1 change: 1 addition & 0 deletions src/pal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ set(SOURCES
misc/jitsupport.cpp
misc/miscpalapi.cpp
misc/msgbox.cpp
misc/perfjitdump.cpp
misc/strutil.cpp
misc/sysinfo.cpp
misc/time.cpp
Expand Down
271 changes: 271 additions & 0 deletions src/pal/src/misc/perfjitdump.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// 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.
// ===========================================================================

#if defined(__linux__)
#define JITDUMP_SUPPORTED
#endif

#include "pal/palinternal.h"
#include "pal/dbgmsg.h"

#include <cstddef>
#include "perfjitdump.h"

#ifdef JITDUMP_SUPPORTED

#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>

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

SET_DEFAULT_DEBUG_CHANNEL(MISC);

namespace
{
enum
{
#ifdef BIGENDIAN
JIT_DUMP_MAGIC = 0x4454694A,
#else
JIT_DUMP_MAGIC = 0x4A695444,
#endif
JIT_DUMP_VERSION = 1,

#if defined(_X86_)
ELF_MACHINE = EM_386,
#elif defined(_ARM_)
ELF_MACHINE = EM_ARM,
#elif defined(_AMD64_)
ELF_MACHINE = EM_X86_64,
#elif defined(_ARM64_)
ELF_MACHINE = EM_AARCH64,
#else
#error ELF_MACHINE unsupported for target
#endif

JIT_CODE_LOAD = 0,
};

uint64_t GetTimeStampNS()
{
#if HAVE_CLOCK_MONOTONIC
struct timespec ts;
int result = clock_gettime(CLOCK_MONOTONIC, &ts);

if (result != 0)
{
ASSERT("clock_gettime(CLOCK_MONOTONIC) failed: %d\n", result);
return 0;
}
else
{
return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
}
#else
#error "The PAL jitdump requires clock_gettime(CLOCK_MONOTONIC) to be supported."
#endif
}

struct FileHeader
{
FileHeader() :
magic(JIT_DUMP_MAGIC),
version(JIT_DUMP_VERSION),
total_size(sizeof(FileHeader)),
elf_mach(ELF_MACHINE),
pad1(0),
pid(getpid()),
timestamp(GetTimeStampNS()),
flags(0)
{}

uint32_t magic;
uint32_t version;
uint32_t total_size;
uint32_t elf_mach;
uint32_t pad1;
uint32_t pid;
uint64_t timestamp;
uint64_t flags;
};

struct RecordHeader
{
uint32_t id;
uint32_t total_size;
uint64_t timestamp;
};

struct JitCoreLoadRecord
{
JitCoreLoadRecord() :
pid(getpid()),
tid(syscall(SYS_gettid))
{
header.id = JIT_CODE_LOAD;
header.timestamp = GetTimeStampNS();
}

RecordHeader header;
uint32_t pid;
uint32_t tid;
uint64_t vma;
uint64_t code_addr;
uint64_t code_size;
uint64_t code_index;
// Null terminated name
// Optional native code
};
};

struct PerfJitDumpState
{
PerfJitDumpState() :
enabled(false),
fd(-1),
mmapAddr(nullptr),
mutex(PTHREAD_MUTEX_INITIALIZER),
codeIndex(0)
{}

bool enabled;
int fd;
void *mmapAddr;
pthread_mutex_t mutex;
uint64_t codeIndex;

void Start()
{
pthread_mutex_lock(&mutex);

char jitdumpPath[1024];

snprintf(jitdumpPath, sizeof(jitdumpPath), "/tmp/jit-%i.dump", getpid());

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

// Write file header
FileHeader header;

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

fsync(fd);

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

enabled = true;

pthread_mutex_unlock(&mutex);
}

void LogMethod(void* pCode, size_t codeSize, const char* symbol)
{
if (enabled)
{
pthread_mutex_lock(&mutex);

size_t symbolLen = strlen(symbol);

JitCoreLoadRecord record;

record.vma = (uint64_t) pCode;
record.code_addr = (uint64_t) pCode;
record.code_size = codeSize;
record.code_index = ++codeIndex;
record.header.total_size = sizeof(JitCoreLoadRecord) + symbolLen + 1 + codeSize;
record.header.timestamp = GetTimeStampNS();

write(fd, &record, sizeof(JitCoreLoadRecord));

write(fd, symbol, symbolLen + 1);

write(fd, pCode, codeSize);

fsync(fd);

pthread_mutex_unlock(&mutex);
}
}

void Finish()
{
if (enabled)
{
// Lock the mutex
pthread_mutex_lock(&mutex);

enabled = false;

munmap(mmapAddr, sizeof(FileHeader));

fsync(fd);

close(fd);

pthread_mutex_unlock(&mutex);
}
}
};


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

return s;
}

void PerfJitDump::Start()
{
GetState().Start();
}

void PerfJitDump::LogMethod(void* pCode, size_t codeSize, const char* symbol)
{
GetState().LogMethod(pCode, codeSize, symbol);
}

void PerfJitDump::Finish()
{
GetState().Finish();
}

#else // JITDUMP_SUPPORTED

struct PerfJitDumpState
{
};

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

return s;
}

void PerfJitDump::Start()
{
}

void PerfJitDump::LogMethod(void* pCode, size_t codeSize, const char* symbol)
{
}

void PerfJitDump::Finish()
{
}

#endif // JITDUMP_SUPPORTED
34 changes: 23 additions & 11 deletions src/vm/perfmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "perfmap.h"
#include "perfinfo.h"
#include "pal.h"
#include "perfjitdump.h"

// The code addresses are actually native image offsets during crossgen. Print
// them as 32-bit numbers for consistent output when cross-targeting and to
Expand Down Expand Up @@ -50,6 +51,9 @@ void PerfMap::Initialize()
{
s_ShowOptimizationTiers = true;
}
#ifndef CROSSGEN_COMPILE
sdmaclea marked this conversation as resolved.
Show resolved Hide resolved
PerfJitDump::Start();
#endif
}
}

Expand All @@ -60,6 +64,9 @@ void PerfMap::Destroy()

if (s_Current != nullptr)
{
#ifndef CROSSGEN_COMPILE
PerfJitDump::Finish();
#endif
delete s_Current;
s_Current = nullptr;
}
Expand Down Expand Up @@ -183,24 +190,23 @@ void PerfMap::LogMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, cons
EX_TRY
{
// Get the full method signature.
SString fullMethodSignature;
pMethod->GetFullMethodInfo(fullMethodSignature);
SString name;
pMethod->GetFullMethodInfo(name);

// Build the map file line.
StackScratchBuffer scratch;
SString line;
line.Printf(FMT_CODE_ADDR " %x %s", pCode, codeSize, fullMethodSignature.GetANSI(scratch));
if (optimizationTier != nullptr && s_ShowOptimizationTiers)
{
line.AppendPrintf("[%s]\n", optimizationTier);
}
else
{
line.Append(W('\n'));
name.AppendPrintf("[%s]", optimizationTier);
}
SString line;
line.Printf(FMT_CODE_ADDR " %x %s\n", pCode, codeSize, name.GetANSI(scratch));

// Write the line.
WriteLine(line);
#ifndef CROSSGEN_COMPILE
PerfJitDump::LogMethod((void*)pCode, codeSize, name.GetANSI(scratch));
#endif
}
EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
}
Expand Down Expand Up @@ -282,15 +288,21 @@ void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode,
}
if(!stubType)
{
stubOwner = "?";
stubType = "?";
}

// Build the map file line.
StackScratchBuffer scratch;
SString name;
name.Printf("stub<%d> %s<%s>", ++(s_Current->m_StubsMapped), stubType, stubOwner);
SString line;
line.Printf(FMT_CODE_ADDR " %x stub<%d> %s<%s>\n", pCode, codeSize, ++(s_Current->m_StubsMapped), stubType, stubOwner);
line.Printf(FMT_CODE_ADDR " %x %s\n", pCode, codeSize, name.GetANSI(scratch));

// Write the line.
s_Current->WriteLine(line);
#ifndef CROSSGEN_COMPILE
PerfJitDump::LogMethod((void*)pCode, codeSize, name.GetANSI(scratch));
#endif
}
EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
}
Expand Down