From d4ad23177254577dde59d483f001936c693d8dea Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 1 Feb 2017 21:18:54 -0800
Subject: [PATCH 1/2] [MCJIT] Call JIT notifiers only after code sections are
ready.
Previously JIT notifiers were called before relocations were
performed (leading to ominious function call of "0"), and before
memory marked executable (confusing some profilers).
Move notifications to finalizeLoadedModules().
---
lib/ExecutionEngine/MCJIT/MCJIT.cpp | 16 ++++++++++++++--
lib/ExecutionEngine/MCJIT/MCJIT.h | 2 ++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/lib/ExecutionEngine/MCJIT/MCJIT.cpp
index ff8749fbfed..ebe475d9f0b 100644
--- a/lib/ExecutionEngine/MCJIT/MCJIT.cpp
+++ b/lib/ExecutionEngine/MCJIT/MCJIT.cpp
@@ -222,8 +222,10 @@ void MCJIT::generateCodeForModule(Module *M) {
if (Dyld.hasError())
report_fatal_error(Dyld.getErrorString());
- NotifyObjectEmitted(*LoadedObject.get(), *L);
-
+ // Can't call notifiers yet as relocations have not yet been performed, and
+ // memory hasn't been marked executable.
+ PendingLoadedObjects.push_back(LoadedObject->get());
+ PendingLoadedObjectInfos.push_back(std::move(L));
Buffers.push_back(std::move(ObjectToLoad));
LoadedObjects.push_back(std::move(*LoadedObject));
@@ -243,6 +245,16 @@ void MCJIT::finalizeLoadedModules() {
// Set page permissions.
MemMgr->finalizeMemory();
+
+ // Notify listeners about loaded objects now that memory is marked executable
+ // and relocations have been performed.
+ for (size_t i = 0; i < PendingLoadedObjects.size(); i++) {
+ auto &Obj = PendingLoadedObjects[i];
+ auto &Info = PendingLoadedObjectInfos[i];
+ NotifyObjectEmitted(*Obj, *Info);
+ }
+ PendingLoadedObjects.clear();
+ PendingLoadedObjectInfos.clear();
}
// FIXME: Rename this.
diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.h b/lib/ExecutionEngine/MCJIT/MCJIT.h
index daf578f5daa..418578fc7a3 100644
--- a/lib/ExecutionEngine/MCJIT/MCJIT.h
+++ b/lib/ExecutionEngine/MCJIT/MCJIT.h
@@ -189,6 +189,8 @@ class MCJIT : public ExecutionEngine {
SmallVector<std::unique_ptr<MemoryBuffer>, 2> Buffers;
SmallVector<std::unique_ptr<object::ObjectFile>, 2> LoadedObjects;
+ SmallVector<object::ObjectFile*, 2> PendingLoadedObjects;
+ SmallVector<std::unique_ptr<RuntimeDyld::LoadedObjectInfo>, 2> PendingLoadedObjectInfos;
// An optional ObjectCache to be notified of compiled objects and used to
// perform lookup of pre-compiled code to avoid re-compilation.
--
2.11.0.22.g8d7a455.dirty
From debc641fbc5980461f8d36e534d5a6de70e0a293 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 1 Feb 2017 23:10:45 -0800
Subject: [PATCH 2/2] Add PerfJITEventListener for perf profiling support.
---
CMakeLists.txt | 13 +
include/llvm/Config/config.h.cmake | 3 +
include/llvm/Config/llvm-config.h.cmake | 3 +
include/llvm/ExecutionEngine/JITEventListener.h | 9 +
lib/ExecutionEngine/CMakeLists.txt | 4 +
lib/ExecutionEngine/LLVMBuild.txt | 2 +-
lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt | 5 +
.../{ => PerfJITEvents}/LLVMBuild.txt | 13 +-
.../PerfJITEvents/PerfJITEventListener.cpp | 530 +++++++++++++++++++++
tools/lli/CMakeLists.txt | 9 +
tools/lli/lli.cpp | 2 +
11 files changed, 584 insertions(+), 9 deletions(-)
create mode 100644 lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt
copy lib/ExecutionEngine/{ => PerfJITEvents}/LLVMBuild.txt (65%)
create mode 100644 lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5cb24ea7d78..6ce14799725 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -445,6 +445,16 @@ if( LLVM_USE_OPROFILE )
endif( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" )
endif( LLVM_USE_OPROFILE )
+option(LLVM_USE_PERF
+ "Use perf JIT interface to inform perf about JIT code" OFF)
+
+# If enabled, verify we are on a platform that supports perf.
+if( LLVM_USE_PERF )
+ if( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" )
+ message(FATAL_ERROR "perf support is available on Linux only.")
+ endif( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" )
+endif( LLVM_USE_PERF )
+
set(LLVM_USE_SANITIZER "" CACHE STRING
"Define the sanitizer used to build binaries and tests.")
@@ -638,6 +648,9 @@ endif (LLVM_USE_INTEL_JITEVENTS)
if (LLVM_USE_OPROFILE)
set(LLVMOPTIONALCOMPONENTS ${LLVMOPTIONALCOMPONENTS} OProfileJIT)
endif (LLVM_USE_OPROFILE)
+if (LLVM_USE_PERF)
+ set(LLVMOPTIONALCOMPONENTS PerfJITEvents)
+endif (LLVM_USE_PERF)
message(STATUS "Constructing LLVMBuild project information")
execute_process(
diff --git a/include/llvm/Config/config.h.cmake b/include/llvm/Config/config.h.cmake
index ff41d75373c..cdc14aebe0f 100644
--- a/include/llvm/Config/config.h.cmake
+++ b/include/llvm/Config/config.h.cmake
@@ -384,6 +384,9 @@
/* Define if we have the oprofile JIT-support library */
#cmakedefine01 LLVM_USE_OPROFILE
+/* Define if we have the perf JIT-support library */
+#cmakedefine01 LLVM_USE_PERF
+
/* LLVM version information */
#cmakedefine LLVM_VERSION_INFO "${LLVM_VERSION_INFO}"
diff --git a/include/llvm/Config/llvm-config.h.cmake b/include/llvm/Config/llvm-config.h.cmake
index 35999705761..09c807cba8c 100644
--- a/include/llvm/Config/llvm-config.h.cmake
+++ b/include/llvm/Config/llvm-config.h.cmake
@@ -65,6 +65,9 @@
/* Define if we have the oprofile JIT-support library */
#cmakedefine01 LLVM_USE_OPROFILE
+/* Define if we have the perf JIT-support library */
+#cmakedefine01 LLVM_USE_PERF
+
/* Major version of the LLVM API */
#define LLVM_VERSION_MAJOR ${LLVM_VERSION_MAJOR}
diff --git a/include/llvm/ExecutionEngine/JITEventListener.h b/include/llvm/ExecutionEngine/JITEventListener.h
index 94ec4e36a19..b6916bf55a1 100644
--- a/include/llvm/ExecutionEngine/JITEventListener.h
+++ b/include/llvm/ExecutionEngine/JITEventListener.h
@@ -113,6 +113,15 @@ public:
}
#endif // USE_OPROFILE
+#ifdef LLVM_USE_PERF
+ static JITEventListener *createPerfJITEventListener();
+#else
+ static JITEventListener *createPerfJITEventListener()
+ {
+ return nullptr;
+ }
+#endif // USE_PERF
+
private:
virtual void anchor();
};
diff --git a/lib/ExecutionEngine/CMakeLists.txt b/lib/ExecutionEngine/CMakeLists.txt
index 2d9337bbefd..6092b4d1100 100644
--- a/lib/ExecutionEngine/CMakeLists.txt
+++ b/lib/ExecutionEngine/CMakeLists.txt
@@ -26,3 +26,7 @@ endif( LLVM_USE_OPROFILE )
if( LLVM_USE_INTEL_JITEVENTS )
add_subdirectory(IntelJITEvents)
endif( LLVM_USE_INTEL_JITEVENTS )
+
+if( LLVM_USE_PERF )
+ add_subdirectory(PerfJITEvents)
+endif( LLVM_USE_PERF )
diff --git a/lib/ExecutionEngine/LLVMBuild.txt b/lib/ExecutionEngine/LLVMBuild.txt
index 9d29a41f504..b6e1bda6a51 100644
--- a/lib/ExecutionEngine/LLVMBuild.txt
+++ b/lib/ExecutionEngine/LLVMBuild.txt
@@ -16,7 +16,7 @@
;===------------------------------------------------------------------------===;
[common]
-subdirectories = Interpreter MCJIT RuntimeDyld IntelJITEvents OProfileJIT Orc
+subdirectories = Interpreter MCJIT RuntimeDyld IntelJITEvents OProfileJIT Orc PerfJITEvents
[component_0]
type = Library
diff --git a/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt b/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt
new file mode 100644
index 00000000000..136cc429d02
--- /dev/null
+++ b/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_llvm_library(LLVMPerfJITEvents
+ PerfJITEventListener.cpp
+ )
+
+add_dependencies(LLVMPerfJITEvents LLVMCodeGen)
diff --git a/lib/ExecutionEngine/LLVMBuild.txt b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt
similarity index 65%
copy from lib/ExecutionEngine/LLVMBuild.txt
+++ b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt
@@ -1,4 +1,4 @@
-;===- ./lib/ExecutionEngine/LLVMBuild.txt ----------------------*- Conf -*--===;
+;===- ./lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt ----------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
@@ -15,11 +15,8 @@
;
;===------------------------------------------------------------------------===;
-[common]
-subdirectories = Interpreter MCJIT RuntimeDyld IntelJITEvents OProfileJIT Orc
-
[component_0]
-type = Library
-name = ExecutionEngine
-parent = Libraries
-required_libraries = Core MC Object RuntimeDyld Support Target
+type = OptionalLibrary
+name = PerfJITEvents
+parent = ExecutionEngine
+required_libraries = CodeGen Core DebugInfoDWARF Support Object ExecutionEngine
diff --git a/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp b/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp
new file mode 100644
index 00000000000..301a8bc70e1
--- /dev/null
+++ b/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp
@@ -0,0 +1,530 @@
+//===-- PerfJITEventListener.cpp - Tell Linux's perf about JITted code ----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a JITEventListener object that tells perf JITted functions,
+// including source line information.
+//
+// Documentation for perf jit integration is available at:
+// jitdump-specification.txt « Documentation « perf « tools - kernel/git/torvalds/linux.git - Linux kernel source tree
+// jit-interface.txt « Documentation « perf « tools - kernel/git/torvalds/linux.git - Linux kernel source tree
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Config/config.h"
+
+#include <unistd.h> // for getpid(), sysconf()
+#include <syscall.h> // for gettid() */
+#include <time.h> // clock_gettime(), time(), localtime_r() */
+#include <sys/mman.h> // mmap() */
+#include <sys/types.h> // getpid(), open()
+#include <sys/stat.h> // open()
+#include <fcntl.h> // open()
+
+#include "llvm/ExecutionEngine/JITEventListener.h"
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolSize.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind;
+
+namespace {
+
+// language identifier (XXX: should we generate something better from debug info?)
+#define JIT_LANG "llvm-IR"
+#define LLVM_PERF_JIT_MAGIC ((uint32_t) 'J' << 24 | (uint32_t) 'i' << 16 | (uint32_t) 'T' << 8 | (uint32_t) 'D')
+#define LLVM_PERF_JIT_VERSION 2
+
+/* bit 0: set if the jitdump file is using an architecture-specific timestamp clock source */
+#define JITDUMP_FLAGS_ARCH_TIMESTAMP (1ULL << 0)
+
+struct LLVMPerfJitHeader;
+
+class PerfJITEventListener : public JITEventListener {
+public:
+ PerfJITEventListener();
+ ~PerfJITEventListener() {
+ if (MarkerAddr)
+ CloseMarker();
+ }
+
+ void NotifyObjectEmitted(const ObjectFile &Obj,
+ const RuntimeDyld::LoadedObjectInfo &L) override;
+
+ void NotifyFreeingObject(const ObjectFile &Obj) override;
+
+private:
+
+ bool InitDebuggingDir();
+ bool OpenMarker();
+ void CloseMarker();
+ bool FillMachine(LLVMPerfJitHeader &hdr);
+
+ void NotifyCode(Expected<llvm::StringRef> &Symbol, uint64_t CodeAddr, uint64_t CodeSize);
+ void NotifyDebug(uint64_t CodeAddr, DILineInfoTable Lines);
+
+ // output data stream
+ std::unique_ptr<raw_fd_ostream> Dumpstream;
+
+ // output data stream, lifeliness managed via Dumpstream
+ int Fd;
+
+ // prevent concurrent dumps from messing up the output file
+ sys::Mutex Mutex;
+
+ // cache lookups
+ pid_t Pid;
+
+ // base directory for output data
+ std::string JitPath;
+
+ // perf mmap marker
+ void *MarkerAddr = NULL;
+
+ // perf support ready
+ bool SuccessfullyInitialized = false;
+};
+
+// The following are POD struct definitions from the perf jit specification
+
+enum LLVMPerfJitRecordType {
+ JIT_CODE_LOAD = 0,
+ JIT_CODE_MOVE = 1,
+ JIT_CODE_DEBUG_INFO = 2,
+ JIT_CODE_CLOSE = 3,
+ JIT_CODE_UNWINDING_INFO = 4,
+
+ JIT_CODE_MAX,
+};
+
+struct LLVMPerfJitHeader {
+ uint32_t Magic; /* characters "JiTD" */
+ uint32_t Version; /* header version */
+ uint32_t TotalSize; /* total size of header */
+ uint32_t ElfMach; /* elf mach target */
+ uint32_t Pad1; /* reserved */
+ uint32_t Pid;
+ uint64_t Timestamp; /* timestamp */
+ uint64_t Flags; /* flags */
+};
+
+/* record prefix (mandatory in each record) */
+struct LLVMPerfJitRecordPrefix {
+ uint32_t Id; /* record type identifier */
+ uint32_t TotalSize;
+ uint64_t Timestamp;
+};
+
+struct LLVMPerfJitRecordCodeLoad {
+ LLVMPerfJitRecordPrefix Prefix;
+
+ uint32_t Pid;
+ uint32_t Tid;
+ uint64_t Vma;
+ uint64_t CodeAddr;
+ uint64_t CodeSize;
+ uint64_t CodeIndex;
+};
+
+struct LLVMPerfJitRecordClose {
+ LLVMPerfJitRecordPrefix Prefix;
+};
+
+struct LLVMPerfJitRecordMoveCode {
+ LLVMPerfJitRecordPrefix Prefix;
+
+ uint32_t Pid;
+ uint32_t Tid;
+ uint64_t Vma;
+ uint64_t OldCodeAddr;
+ uint64_t NewCodeAddr;
+ uint64_t CodeSize;
+ uint64_t CodeIndex;
+};
+
+struct LLVMPerfJitDebugEntry {
+ uint64_t Addr;
+ int Lineno; /* source line number starting at 1 */
+ int Discrim; /* column discriminator, 0 is default */
+ char Name; /* null terminated filename, \xff\0 if same as previous entry */
+};
+
+struct LLVMPerfJitRecordDebugInfo {
+ LLVMPerfJitRecordPrefix Prefix;
+
+ uint64_t CodeAddr;
+ uint64_t NrEntry;
+ LLVMPerfJitDebugEntry Entries;
+};
+
+struct LLVMPerfJitRecordUnwindInfo {
+ LLVMPerfJitRecordPrefix prefix;
+
+ uint64_t UnwindingSize;
+ uint64_t EhFrameHdrSize;
+ uint64_t MappedSize;
+ const char UnwindingData;
+};
+
+// not available otherwise
+static inline pid_t gettid(void) {
+ return (pid_t)syscall(__NR_gettid);
+}
+
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts) {
+ const uint64_t NanoSecPerSec = 1000000000;
+ return ((uint64_t) ts->tv_sec * NanoSecPerSec) + ts->tv_nsec;
+}
+
+static inline uint64_t
+perf_get_timestamp(void) {
+ struct timespec ts;
+ int ret;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret)
+ return 0;
+
+ return timespec_to_ns(&ts);
+}
+
+
+PerfJITEventListener::PerfJITEventListener()
+ : Pid(getpid()) {
+
+ LLVMPerfJitHeader Header = {0};
+ std::string Filename;
+ raw_string_ostream FilenameBuf(Filename);
+
+ // check if clock-source is supported
+ if (!perf_get_timestamp()) {
+ errs() << "kernel does not support CLOCK_MONOTONIC("<<CLOCK_MONOTONIC<<")\n";
+ return;
+ }
+
+ memset(&Header, 0, sizeof(Header));
+
+ if (!InitDebuggingDir()) {
+ errs() << "could not initialize debugging directory\n";
+ return;
+ }
+
+ FilenameBuf << JitPath << "/jit-"<<Pid<<".dump";
+
+ Fd = ::open(FilenameBuf.str().c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666);
+ if (Fd == -1) {
+ errs() << "could not open JIT dump file "<<FilenameBuf.str()<<"\n";
+ return;
+ }
+
+ std::error_code EC;
+ Dumpstream = make_unique<raw_fd_ostream>(Fd, true);
+ assert(!EC);
+
+ if (!OpenMarker()) {
+ return;
+ }
+
+ if (!FillMachine(Header)) {
+ return;
+ }
+
+ Header.Magic = LLVM_PERF_JIT_MAGIC;
+ Header.Version = LLVM_PERF_JIT_VERSION;
+ Header.TotalSize = sizeof(Header);
+ Header.Pid = Pid;
+ Header.Timestamp = perf_get_timestamp();
+
+ Dumpstream->write((char *) &Header, sizeof(Header));
+
+ // Everything initialized, can do profiling now.
+ if (!Dumpstream->has_error())
+ SuccessfullyInitialized = true;
+}
+
+void PerfJITEventListener::NotifyObjectEmitted(
+ const ObjectFile &Obj,
+ const RuntimeDyld::LoadedObjectInfo &L) {
+
+ if (!SuccessfullyInitialized)
+ return;
+
+ OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj);
+ const ObjectFile &DebugObj = *DebugObjOwner.getBinary();
+
+ // Get the address of the object image for use as a unique identifier
+ DWARFContextInMemory Context(DebugObj);
+
+ // Use symbol info to iterate functions in the object.
+ for (const std::pair<SymbolRef, uint64_t> &P : computeSymbolSizes(DebugObj)) {
+ SymbolRef Sym = P.first;
+ std::vector<LLVMPerfJitDebugEntry> LineInfo;
+ std::string SourceFileName;
+
+ Expected<SymbolRef::Type> SymTypeOrErr = Sym.getType();
+ if (!SymTypeOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SymTypeOrErr.takeError());
+ continue;
+ }
+ SymbolRef::Type SymType = *SymTypeOrErr;
+ if (SymType != SymbolRef::ST_Function)
+ continue;
+
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
+ continue;
+ }
+
+ Expected<uint64_t> AddrOrErr = Sym.getAddress();
+ if (!AddrOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(AddrOrErr.takeError());
+ continue;
+ }
+ uint64_t Addr = *AddrOrErr;
+ uint64_t Size = P.second;
+
+ // According to spec debugging info has to come before loading the
+ // corresonding code load.
+ DILineInfoTable Lines = Context.getLineInfoForAddressRange(
+ Addr, Size, FileLineInfoKind::AbsoluteFilePath);
+ NotifyDebug(Addr, Lines);
+
+ NotifyCode(Name, Addr, Size);
+ }
+
+ Dumpstream->flush();
+}
+
+void PerfJITEventListener::NotifyFreeingObject(const ObjectFile &Obj) {
+ /* perf currently doesn't have an interface for unloading */
+}
+
+bool PerfJITEventListener::InitDebuggingDir() {
+ const char *BaseDir;
+ llvm::SmallString<128> TestDir;
+ time_t Time;
+ struct tm LocalTime;
+ char TimeBuffer[sizeof("YYMMDD")];
+
+ time(&Time);
+ localtime_r(&Time, &LocalTime);
+
+ /* perf specific location */
+ BaseDir = getenv("JITDUMPDIR");
+ if (!BaseDir)
+ BaseDir = getenv("HOME");
+ if (!BaseDir)
+ BaseDir = ".";
+
+ strftime(TimeBuffer, sizeof(TimeBuffer), "%Y%m%d", &LocalTime);
+
+ std::string DebugDir(BaseDir);
+ DebugDir += "/.debug/jit/";
+
+ if (sys::fs::create_directories(DebugDir)) {
+ errs() << "could not create jit cache directory "<<DebugDir<<"\n";
+ return false;
+ }
+
+ SmallString<128> UniqueDebugDir;
+
+ if (sys::fs::createUniqueDirectory(Twine(DebugDir) + JIT_LANG"-jit-" + TimeBuffer,
+ UniqueDebugDir)) {
+ errs() << "could not create unique jit cache directory "<<DebugDir<<"\n";
+ return false;
+ }
+
+ JitPath = UniqueDebugDir.str();
+
+ return true;
+}
+
+bool PerfJITEventListener::OpenMarker() {
+ long pgsz;
+
+ pgsz = ::sysconf(_SC_PAGESIZE);
+ if (pgsz == -1)
+ return false;
+
+ /*
+ * We mmap the jitdump to create an MMAP RECORD in perf.data file. The mmap
+ * is captured either live (perf record running when we mmap) or in deferred
+ * mode, via /proc/PID/maps the MMAP record is used as a marker of a jitdump
+ * file for more meta data info about the jitted code. Perf report/annotate
+ * detect this special filename and process the jitdump file.
+ *
+ * Mapping must be PROT_EXEC to ensure it is captured by perf record
+ * even when not using -d option.
+ */
+ MarkerAddr = ::mmap(NULL, pgsz, PROT_READ|PROT_EXEC, MAP_PRIVATE, Fd, 0);
+
+ if (MarkerAddr == MAP_FAILED) {
+ errs() << "could not mmap JIT marker\n";
+ return false;
+ }
+ return true;
+}
+
+bool PerfJITEventListener::FillMachine(LLVMPerfJitHeader &hdr) {
+ ssize_t sret;
+ char id[16];
+ int fd;
+ struct {
+ uint16_t e_type;
+ uint16_t e_machine;
+ } info;
+
+ fd = ::open("/proc/self/exe", O_RDONLY);
+ if (fd == -1) {
+ errs() << "could not open /proc/self/exe\n";
+ return false;
+ }
+
+ sret = ::read(fd, id, sizeof(id));
+ if (sret != sizeof(id)) {
+ errs() << "could not read elf signature from /proc/self/exe\n";
+ goto error;
+ }
+
+ /* check ELF signature */
+ if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') {
+ errs() << "invalid elf signature\n";
+ goto error;
+ }
+
+ sret = ::read(fd, &info, sizeof(info));
+ if (sret != sizeof(info)) {
+ errs() << "could not read machine identification\n";
+ goto error;
+ }
+
+ hdr.ElfMach = info.e_machine;
+ error:
+ close(fd);
+ return true;
+}
+
+void PerfJITEventListener::CloseMarker() {
+ long pgsz;
+
+ if (!MarkerAddr)
+ return;
+
+ pgsz = ::sysconf(_SC_PAGESIZE);
+ if (pgsz == -1)
+ return;
+
+ munmap(MarkerAddr, pgsz);
+ MarkerAddr = nullptr;
+}
+
+void PerfJITEventListener::NotifyCode(Expected<llvm::StringRef> &Symbol, uint64_t CodeAddr, uint64_t CodeSize) {
+ static int code_generation = 1;
+ LLVMPerfJitRecordCodeLoad rec;
+
+ assert(SuccessfullyInitialized);
+
+ // 0 length functions can't have samples.
+ if (CodeSize == 0)
+ return;
+
+ rec.Prefix.Id = JIT_CODE_LOAD;
+ rec.Prefix.TotalSize =
+ sizeof(rec) + // debug record itself
+ Symbol->size() + 1 + // symbol name
+ CodeSize; // and code
+ rec.Prefix.Timestamp = perf_get_timestamp();
+
+ rec.CodeSize = CodeSize;
+ rec.Vma = 0;
+ rec.CodeAddr = CodeAddr;
+ rec.Pid = Pid;
+ rec.Tid = gettid();
+
+ // get code index inside lock to avoid race condition
+ MutexGuard Guard(Mutex);
+
+ rec.CodeIndex = code_generation++;
+
+ Dumpstream->write(reinterpret_cast<const char *>(&rec), sizeof(rec));
+ Dumpstream->write(Symbol->data(), Symbol->size() + 1);
+ Dumpstream->write(reinterpret_cast<const char *>(CodeAddr), CodeSize);
+}
+
+void PerfJITEventListener::NotifyDebug(uint64_t CodeAddr, DILineInfoTable Lines) {
+ LLVMPerfJitRecordDebugInfo rec;
+
+ assert(SuccessfullyInitialized);
+
+ // Didn't get useful debug info.
+ if (Lines.empty())
+ return;
+
+ rec.Prefix.Id = JIT_CODE_DEBUG_INFO;
+ rec.Prefix.TotalSize = sizeof(rec); // will be increased further
+ rec.Prefix.Timestamp = perf_get_timestamp();
+ rec.CodeAddr = CodeAddr;
+ rec.NrEntry = Lines.size();
+
+ /* compute total size size of record (variable due to filenames) */
+ DILineInfoTable::iterator Begin = Lines.begin();
+ DILineInfoTable::iterator End = Lines.end();
+ for (DILineInfoTable::iterator It = Begin; It != End; ++It) {
+ DILineInfo &line = It->second;
+ rec.Prefix.TotalSize += sizeof(LLVMPerfJitDebugEntry);
+ rec.Prefix.TotalSize += line.FileName.size() + 1;
+ }
+
+ Dumpstream->write(reinterpret_cast<const char *>(&rec), sizeof(rec));
+
+ // The debug_entry describes the source line information. It is defined as follows in order:
+ // * uint64_t code_addr: address of function for which the debug information is generated
+ // * uint32_t line : source file line number (starting at 1)
+ // * uint32_t discrim : column discriminator, 0 is default
+ // * char name[n] : source file name in ASCII, including null termination
+
+ MutexGuard Guard(Mutex);
+
+ for (DILineInfoTable::iterator It = Begin; It != End; ++It) {
+ LLVMPerfJitDebugEntry LineInfo;
+ DILineInfo &Line = It->second;
+
+ LineInfo.Addr = It->first;
+ // For reasons unknown to me either llvm offsets or perf's use of them is
+ // offset by 0x40. Inquiring.
+ LineInfo.Addr += 0x40;
+ LineInfo.Lineno = Line.Line;
+ LineInfo.Discrim = Line.Discriminator;
+
+ Dumpstream->write(reinterpret_cast<const char *>(&LineInfo), sizeof(LineInfo));
+ Dumpstream->write(Line.FileName.c_str(), Line.FileName.size() + 1);
+ }
+}
+
+} // end anonymous namespace
+
+namespace llvm {
+JITEventListener *JITEventListener::createPerfJITEventListener() {
+ return new PerfJITEventListener();
+}
+} // end llvm namespace
diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt
index f02e19313b7..5f235b6f6f3 100644
--- a/tools/lli/CMakeLists.txt
+++ b/tools/lli/CMakeLists.txt
@@ -36,6 +36,15 @@ if( LLVM_USE_INTEL_JITEVENTS )
)
endif( LLVM_USE_INTEL_JITEVENTS )
+if( LLVM_USE_PERF )
+ set(LLVM_LINK_COMPONENTS
+ ${LLVM_LINK_COMPONENTS}
+ DebugInfoDWARF
+ PerfJITEvents
+ Object
+ )
+endif( LLVM_USE_PERF )
+
add_llvm_tool(lli
lli.cpp
OrcLazyJIT.cpp
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index 0823ff469de..9e488a293e2 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -546,6 +546,8 @@ int main(int argc, char **argv, char * const *envp) {
JITEventListener::createOProfileJITEventListener());
EE->RegisterJITEventListener(
JITEventListener::createIntelJITEventListener());
+ EE->RegisterJITEventListener(
+ JITEventListener::createPerfJITEventListener());
if (!NoLazyCompilation && RemoteMCJIT) {
errs() << "warning: remote mcjit does not support lazy compilation\n";
--
2.11.0.22.g8d7a455.dirty
#include <stdint.h>
#include <stdbool.h>
bool stupid_isprime(uint64_t num)
{
if (num == 2)
return true;
if (num < 1 || num % 2 == 0)
return false;
for(uint64_t i = 3; i < num / 2; i+= 2) {
if (num % i == 0)
return false;
}
return true;
}
int main(int argc, char **argv)
{
int numprimes = 0;
for (uint64_t num = argc; num < 100000; num++)
{
if (stupid_isprime(num))
numprimes++;
}
return numprimes;
}