From 1db0527249415c5abbcf7425f05e04c7dc1713ef Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 1 Feb 2017 23:10:45 -0800
Subject: [PATCH 5/6] 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/Orc/LLVMBuild.txt              |   2 +-
 lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt   |   5 +
 .../{Orc => PerfJITEvents}/LLVMBuild.txt           |   8 +-
 .../PerfJITEvents/PerfJITEventListener.cpp         | 530 +++++++++++++++++++++
 tools/lli/CMakeLists.txt                           |   9 +
 tools/lli/lli.cpp                                  |   2 +
 12 files changed, 584 insertions(+), 6 deletions(-)
 create mode 100644 lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt
 copy lib/ExecutionEngine/{Orc => PerfJITEvents}/LLVMBuild.txt (72%)
 create mode 100644 lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e2e548df3f..dac3b817477 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -433,6 +433,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.")
 
@@ -639,6 +649,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 d67148f6aa3..59a504ca3cf 100644
--- a/include/llvm/Config/config.h.cmake
+++ b/include/llvm/Config/config.h.cmake
@@ -374,6 +374,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 4b0c5946061..4003b4d7b15 100644
--- a/include/llvm/Config/llvm-config.h.cmake
+++ b/include/llvm/Config/llvm-config.h.cmake
@@ -62,6 +62,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 ff7840f00a4..ad89599f717 100644
--- a/include/llvm/ExecutionEngine/JITEventListener.h
+++ b/include/llvm/ExecutionEngine/JITEventListener.h
@@ -115,6 +115,15 @@ public:
   }
 #endif // USE_OPROFILE
 
+#if 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 84b34919e44..c0dea0550fb 100644
--- a/lib/ExecutionEngine/CMakeLists.txt
+++ b/lib/ExecutionEngine/CMakeLists.txt
@@ -30,3 +30,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/Orc/LLVMBuild.txt b/lib/ExecutionEngine/Orc/LLVMBuild.txt
index 8f05172e77a..ef4ae64e823 100644
--- a/lib/ExecutionEngine/Orc/LLVMBuild.txt
+++ b/lib/ExecutionEngine/Orc/LLVMBuild.txt
@@ -19,4 +19,4 @@
 type = Library
 name = OrcJIT
 parent = ExecutionEngine
-required_libraries = Core ExecutionEngine Object RuntimeDyld Support TransformUtils
+required_libraries = Core ExecutionEngine Object RuntimeDyld Support TransformUtils PerfJITEvents
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/Orc/LLVMBuild.txt b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt
similarity index 72%
copy from lib/ExecutionEngine/Orc/LLVMBuild.txt
copy to lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt
index 8f05172e77a..5175f9dd791 100644
--- a/lib/ExecutionEngine/Orc/LLVMBuild.txt
+++ b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt
@@ -1,4 +1,4 @@
-;===- ./lib/ExecutionEngine/MCJIT/LLVMBuild.txt ----------------*- Conf -*--===;
+;===- ./lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt ----------------*- Conf -*--===;
 ;
 ;                     The LLVM Compiler Infrastructure
 ;
@@ -16,7 +16,7 @@
 ;===------------------------------------------------------------------------===;
 
 [component_0]
-type = Library
-name = OrcJIT
+type = OptionalLibrary
+name = PerfJITEvents
 parent = ExecutionEngine
-required_libraries = Core ExecutionEngine Object RuntimeDyld Support TransformUtils
+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..d8b40e8b949
--- /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:
+// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jitdump-specification.txt
+// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt
+//
+//===----------------------------------------------------------------------===//
+
+#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 1
+
+/* 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
+  std::unique_ptr<DIContext> Context = DWARFContext::create(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 cd43e9d5791..a6c22526ea6 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -496,6 +496,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.14.1.536.g6867272d5b.dirty

