xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.cpp (revision 1f4d91ecb8529678a3d3919d7523743bd21942ca)
188e3358fSPrem Chintalapudi //===------- JITLoaderPerf.cpp - Register profiler objects ------*- C++ -*-===//
288e3358fSPrem Chintalapudi //
388e3358fSPrem Chintalapudi // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
488e3358fSPrem Chintalapudi // See https://llvm.org/LICENSE.txt for license information.
588e3358fSPrem Chintalapudi // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
688e3358fSPrem Chintalapudi //
788e3358fSPrem Chintalapudi //===----------------------------------------------------------------------===//
888e3358fSPrem Chintalapudi //
988e3358fSPrem Chintalapudi // Register objects for access by profilers via the perf JIT interface.
1088e3358fSPrem Chintalapudi //
1188e3358fSPrem Chintalapudi //===----------------------------------------------------------------------===//
1288e3358fSPrem Chintalapudi 
1388e3358fSPrem Chintalapudi #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.h"
1488e3358fSPrem Chintalapudi 
1588e3358fSPrem Chintalapudi #include "llvm/ExecutionEngine/Orc/Shared/PerfSharedStructs.h"
1688e3358fSPrem Chintalapudi 
1788e3358fSPrem Chintalapudi #include "llvm/Support/FileSystem.h"
1888e3358fSPrem Chintalapudi #include "llvm/Support/MemoryBuffer.h"
1988e3358fSPrem Chintalapudi #include "llvm/Support/Path.h"
2088e3358fSPrem Chintalapudi #include "llvm/Support/Process.h"
2188e3358fSPrem Chintalapudi #include "llvm/Support/Threading.h"
2288e3358fSPrem Chintalapudi 
2388e3358fSPrem Chintalapudi #include <mutex>
2488e3358fSPrem Chintalapudi #include <optional>
2588e3358fSPrem Chintalapudi 
2688e3358fSPrem Chintalapudi #ifdef __linux__
2788e3358fSPrem Chintalapudi 
2888e3358fSPrem Chintalapudi #include <sys/mman.h> // mmap()
2988e3358fSPrem Chintalapudi #include <time.h>     // clock_gettime(), time(), localtime_r() */
3088e3358fSPrem Chintalapudi 
3188e3358fSPrem Chintalapudi #define DEBUG_TYPE "orc"
3288e3358fSPrem Chintalapudi 
3388e3358fSPrem Chintalapudi // language identifier (XXX: should we generate something better from debug
3488e3358fSPrem Chintalapudi // info?)
3588e3358fSPrem Chintalapudi #define JIT_LANG "llvm-IR"
3688e3358fSPrem Chintalapudi #define LLVM_PERF_JIT_MAGIC                                                    \
3788e3358fSPrem Chintalapudi   ((uint32_t)'J' << 24 | (uint32_t)'i' << 16 | (uint32_t)'T' << 8 |            \
3888e3358fSPrem Chintalapudi    (uint32_t)'D')
3988e3358fSPrem Chintalapudi #define LLVM_PERF_JIT_VERSION 1
4088e3358fSPrem Chintalapudi 
4188e3358fSPrem Chintalapudi using namespace llvm;
4288e3358fSPrem Chintalapudi using namespace llvm::orc;
4388e3358fSPrem Chintalapudi 
4488e3358fSPrem Chintalapudi struct PerfState {
4588e3358fSPrem Chintalapudi   // cache lookups
4688e3358fSPrem Chintalapudi   uint32_t Pid;
4788e3358fSPrem Chintalapudi 
4888e3358fSPrem Chintalapudi   // base directory for output data
4988e3358fSPrem Chintalapudi   std::string JitPath;
5088e3358fSPrem Chintalapudi 
5188e3358fSPrem Chintalapudi   // output data stream, closed via Dumpstream
5288e3358fSPrem Chintalapudi   int DumpFd = -1;
5388e3358fSPrem Chintalapudi 
5488e3358fSPrem Chintalapudi   // output data stream
5588e3358fSPrem Chintalapudi   std::unique_ptr<raw_fd_ostream> Dumpstream;
5688e3358fSPrem Chintalapudi 
5788e3358fSPrem Chintalapudi   // perf mmap marker
5888e3358fSPrem Chintalapudi   void *MarkerAddr = NULL;
5988e3358fSPrem Chintalapudi };
6088e3358fSPrem Chintalapudi 
6188e3358fSPrem Chintalapudi // prevent concurrent dumps from messing up the output file
6288e3358fSPrem Chintalapudi static std::mutex Mutex;
6388e3358fSPrem Chintalapudi static std::optional<PerfState> State;
6488e3358fSPrem Chintalapudi 
6588e3358fSPrem Chintalapudi struct RecHeader {
6688e3358fSPrem Chintalapudi   uint32_t Id;
6788e3358fSPrem Chintalapudi   uint32_t TotalSize;
6888e3358fSPrem Chintalapudi   uint64_t Timestamp;
6988e3358fSPrem Chintalapudi };
7088e3358fSPrem Chintalapudi 
7188e3358fSPrem Chintalapudi struct DIR {
7288e3358fSPrem Chintalapudi   RecHeader Prefix;
7388e3358fSPrem Chintalapudi   uint64_t CodeAddr;
7488e3358fSPrem Chintalapudi   uint64_t NrEntry;
7588e3358fSPrem Chintalapudi };
7688e3358fSPrem Chintalapudi 
7788e3358fSPrem Chintalapudi struct DIE {
7888e3358fSPrem Chintalapudi   uint64_t CodeAddr;
7988e3358fSPrem Chintalapudi   uint32_t Line;
8088e3358fSPrem Chintalapudi   uint32_t Discrim;
8188e3358fSPrem Chintalapudi };
8288e3358fSPrem Chintalapudi 
8388e3358fSPrem Chintalapudi struct CLR {
8488e3358fSPrem Chintalapudi   RecHeader Prefix;
8588e3358fSPrem Chintalapudi   uint32_t Pid;
8688e3358fSPrem Chintalapudi   uint32_t Tid;
8788e3358fSPrem Chintalapudi   uint64_t Vma;
8888e3358fSPrem Chintalapudi   uint64_t CodeAddr;
8988e3358fSPrem Chintalapudi   uint64_t CodeSize;
9088e3358fSPrem Chintalapudi   uint64_t CodeIndex;
9188e3358fSPrem Chintalapudi };
9288e3358fSPrem Chintalapudi 
9388e3358fSPrem Chintalapudi struct UWR {
9488e3358fSPrem Chintalapudi   RecHeader Prefix;
9588e3358fSPrem Chintalapudi   uint64_t UnwindDataSize;
9688e3358fSPrem Chintalapudi   uint64_t EhFrameHeaderSize;
9788e3358fSPrem Chintalapudi   uint64_t MappedSize;
9888e3358fSPrem Chintalapudi };
9988e3358fSPrem Chintalapudi 
10088e3358fSPrem Chintalapudi static inline uint64_t timespec_to_ns(const struct timespec *TS) {
10188e3358fSPrem Chintalapudi   const uint64_t NanoSecPerSec = 1000000000;
10288e3358fSPrem Chintalapudi   return ((uint64_t)TS->tv_sec * NanoSecPerSec) + TS->tv_nsec;
10388e3358fSPrem Chintalapudi }
10488e3358fSPrem Chintalapudi 
10588e3358fSPrem Chintalapudi static inline uint64_t perf_get_timestamp() {
10688e3358fSPrem Chintalapudi   timespec TS;
10788e3358fSPrem Chintalapudi   if (clock_gettime(CLOCK_MONOTONIC, &TS))
10888e3358fSPrem Chintalapudi     return 0;
10988e3358fSPrem Chintalapudi 
11088e3358fSPrem Chintalapudi   return timespec_to_ns(&TS);
11188e3358fSPrem Chintalapudi }
11288e3358fSPrem Chintalapudi 
11388e3358fSPrem Chintalapudi static void writeDebugRecord(const PerfJITDebugInfoRecord &DebugRecord) {
11488e3358fSPrem Chintalapudi   assert(State && "PerfState not initialized");
11588e3358fSPrem Chintalapudi   LLVM_DEBUG(dbgs() << "Writing debug record with "
11688e3358fSPrem Chintalapudi                     << DebugRecord.Entries.size() << " entries\n");
11754a38c9cSJie Fu   [[maybe_unused]] size_t Written = 0;
11888e3358fSPrem Chintalapudi   DIR Dir{RecHeader{static_cast<uint32_t>(DebugRecord.Prefix.Id),
11988e3358fSPrem Chintalapudi                     DebugRecord.Prefix.TotalSize, perf_get_timestamp()},
12088e3358fSPrem Chintalapudi           DebugRecord.CodeAddr, DebugRecord.Entries.size()};
12188e3358fSPrem Chintalapudi   State->Dumpstream->write(reinterpret_cast<const char *>(&Dir), sizeof(Dir));
12288e3358fSPrem Chintalapudi   Written += sizeof(Dir);
12388e3358fSPrem Chintalapudi   for (auto &Die : DebugRecord.Entries) {
12488e3358fSPrem Chintalapudi     DIE d{Die.Addr, Die.Lineno, Die.Discrim};
12588e3358fSPrem Chintalapudi     State->Dumpstream->write(reinterpret_cast<const char *>(&d), sizeof(d));
12688e3358fSPrem Chintalapudi     State->Dumpstream->write(Die.Name.data(), Die.Name.size() + 1);
12788e3358fSPrem Chintalapudi     Written += sizeof(d) + Die.Name.size() + 1;
12888e3358fSPrem Chintalapudi   }
12988e3358fSPrem Chintalapudi   LLVM_DEBUG(dbgs() << "wrote " << Written << " bytes of debug info\n");
13088e3358fSPrem Chintalapudi }
13188e3358fSPrem Chintalapudi 
13288e3358fSPrem Chintalapudi static void writeCodeRecord(const PerfJITCodeLoadRecord &CodeRecord) {
13388e3358fSPrem Chintalapudi   assert(State && "PerfState not initialized");
13488e3358fSPrem Chintalapudi   uint32_t Tid = get_threadid();
13588e3358fSPrem Chintalapudi   LLVM_DEBUG(dbgs() << "Writing code record with code size "
13688e3358fSPrem Chintalapudi                     << CodeRecord.CodeSize << " and code index "
13788e3358fSPrem Chintalapudi                     << CodeRecord.CodeIndex << "\n");
13888e3358fSPrem Chintalapudi   CLR Clr{RecHeader{static_cast<uint32_t>(CodeRecord.Prefix.Id),
13988e3358fSPrem Chintalapudi                     CodeRecord.Prefix.TotalSize, perf_get_timestamp()},
14088e3358fSPrem Chintalapudi           State->Pid,
14188e3358fSPrem Chintalapudi           Tid,
14288e3358fSPrem Chintalapudi           CodeRecord.Vma,
14388e3358fSPrem Chintalapudi           CodeRecord.CodeAddr,
14488e3358fSPrem Chintalapudi           CodeRecord.CodeSize,
14588e3358fSPrem Chintalapudi           CodeRecord.CodeIndex};
14688e3358fSPrem Chintalapudi   LLVM_DEBUG(dbgs() << "wrote " << sizeof(Clr) << " bytes of CLR, "
14788e3358fSPrem Chintalapudi                     << CodeRecord.Name.size() + 1 << " bytes of name, "
14888e3358fSPrem Chintalapudi                     << CodeRecord.CodeSize << " bytes of code\n");
14988e3358fSPrem Chintalapudi   State->Dumpstream->write(reinterpret_cast<const char *>(&Clr), sizeof(Clr));
15088e3358fSPrem Chintalapudi   State->Dumpstream->write(CodeRecord.Name.data(), CodeRecord.Name.size() + 1);
15188e3358fSPrem Chintalapudi   State->Dumpstream->write((const char *)CodeRecord.CodeAddr,
15288e3358fSPrem Chintalapudi                            CodeRecord.CodeSize);
15388e3358fSPrem Chintalapudi }
15488e3358fSPrem Chintalapudi 
15588e3358fSPrem Chintalapudi static void
15688e3358fSPrem Chintalapudi writeUnwindRecord(const PerfJITCodeUnwindingInfoRecord &UnwindRecord) {
15788e3358fSPrem Chintalapudi   assert(State && "PerfState not initialized");
15888e3358fSPrem Chintalapudi   dbgs() << "Writing unwind record with unwind data size "
15988e3358fSPrem Chintalapudi          << UnwindRecord.UnwindDataSize << " and EH frame header size "
16088e3358fSPrem Chintalapudi          << UnwindRecord.EHFrameHdrSize << " and mapped size "
16188e3358fSPrem Chintalapudi          << UnwindRecord.MappedSize << "\n";
16288e3358fSPrem Chintalapudi   UWR Uwr{RecHeader{static_cast<uint32_t>(UnwindRecord.Prefix.Id),
16388e3358fSPrem Chintalapudi                     UnwindRecord.Prefix.TotalSize, perf_get_timestamp()},
16488e3358fSPrem Chintalapudi           UnwindRecord.UnwindDataSize, UnwindRecord.EHFrameHdrSize,
16588e3358fSPrem Chintalapudi           UnwindRecord.MappedSize};
16688e3358fSPrem Chintalapudi   LLVM_DEBUG(dbgs() << "wrote " << sizeof(Uwr) << " bytes of UWR, "
16788e3358fSPrem Chintalapudi                     << UnwindRecord.EHFrameHdrSize
16888e3358fSPrem Chintalapudi                     << " bytes of EH frame header, "
16988e3358fSPrem Chintalapudi                     << UnwindRecord.UnwindDataSize - UnwindRecord.EHFrameHdrSize
17088e3358fSPrem Chintalapudi                     << " bytes of EH frame\n");
17188e3358fSPrem Chintalapudi   State->Dumpstream->write(reinterpret_cast<const char *>(&Uwr), sizeof(Uwr));
17288e3358fSPrem Chintalapudi   if (UnwindRecord.EHFrameHdrAddr)
17388e3358fSPrem Chintalapudi     State->Dumpstream->write((const char *)UnwindRecord.EHFrameHdrAddr,
17488e3358fSPrem Chintalapudi                              UnwindRecord.EHFrameHdrSize);
17588e3358fSPrem Chintalapudi   else
17688e3358fSPrem Chintalapudi     State->Dumpstream->write(UnwindRecord.EHFrameHdr.data(),
17788e3358fSPrem Chintalapudi                              UnwindRecord.EHFrameHdrSize);
17888e3358fSPrem Chintalapudi   State->Dumpstream->write((const char *)UnwindRecord.EHFrameAddr,
17988e3358fSPrem Chintalapudi                            UnwindRecord.UnwindDataSize -
18088e3358fSPrem Chintalapudi                                UnwindRecord.EHFrameHdrSize);
18188e3358fSPrem Chintalapudi }
18288e3358fSPrem Chintalapudi 
18388e3358fSPrem Chintalapudi static Error registerJITLoaderPerfImpl(const PerfJITRecordBatch &Batch) {
18488e3358fSPrem Chintalapudi   if (!State)
18588e3358fSPrem Chintalapudi     return make_error<StringError>("PerfState not initialized",
18688e3358fSPrem Chintalapudi                                    inconvertibleErrorCode());
18788e3358fSPrem Chintalapudi 
18888e3358fSPrem Chintalapudi   // Serialize the batch
18988e3358fSPrem Chintalapudi   std::lock_guard<std::mutex> Lock(Mutex);
19088e3358fSPrem Chintalapudi   if (Batch.UnwindingRecord.Prefix.TotalSize > 0)
19188e3358fSPrem Chintalapudi     writeUnwindRecord(Batch.UnwindingRecord);
19288e3358fSPrem Chintalapudi 
19388e3358fSPrem Chintalapudi   for (const auto &DebugInfo : Batch.DebugInfoRecords)
19488e3358fSPrem Chintalapudi     writeDebugRecord(DebugInfo);
19588e3358fSPrem Chintalapudi 
19688e3358fSPrem Chintalapudi   for (const auto &CodeLoad : Batch.CodeLoadRecords)
19788e3358fSPrem Chintalapudi     writeCodeRecord(CodeLoad);
19888e3358fSPrem Chintalapudi 
19988e3358fSPrem Chintalapudi   State->Dumpstream->flush();
20088e3358fSPrem Chintalapudi 
20188e3358fSPrem Chintalapudi   return Error::success();
20288e3358fSPrem Chintalapudi }
20388e3358fSPrem Chintalapudi 
20488e3358fSPrem Chintalapudi struct Header {
20588e3358fSPrem Chintalapudi   uint32_t Magic;     // characters "JiTD"
20688e3358fSPrem Chintalapudi   uint32_t Version;   // header version
20788e3358fSPrem Chintalapudi   uint32_t TotalSize; // total size of header
20888e3358fSPrem Chintalapudi   uint32_t ElfMach;   // elf mach target
20988e3358fSPrem Chintalapudi   uint32_t Pad1;      // reserved
21088e3358fSPrem Chintalapudi   uint32_t Pid;
21188e3358fSPrem Chintalapudi   uint64_t Timestamp; // timestamp
21288e3358fSPrem Chintalapudi   uint64_t Flags;     // flags
21388e3358fSPrem Chintalapudi };
21488e3358fSPrem Chintalapudi 
21588e3358fSPrem Chintalapudi static Error OpenMarker(PerfState &State) {
21688e3358fSPrem Chintalapudi   // We mmap the jitdump to create an MMAP RECORD in perf.data file.  The mmap
21788e3358fSPrem Chintalapudi   // is captured either live (perf record running when we mmap) or in deferred
21888e3358fSPrem Chintalapudi   // mode, via /proc/PID/maps. The MMAP record is used as a marker of a jitdump
21988e3358fSPrem Chintalapudi   // file for more meta data info about the jitted code. Perf report/annotate
22088e3358fSPrem Chintalapudi   // detect this special filename and process the jitdump file.
22188e3358fSPrem Chintalapudi   //
22288e3358fSPrem Chintalapudi   // Mapping must be PROT_EXEC to ensure it is captured by perf record
22388e3358fSPrem Chintalapudi   // even when not using -d option.
22488e3358fSPrem Chintalapudi   State.MarkerAddr =
22588e3358fSPrem Chintalapudi       ::mmap(NULL, sys::Process::getPageSizeEstimate(), PROT_READ | PROT_EXEC,
22688e3358fSPrem Chintalapudi              MAP_PRIVATE, State.DumpFd, 0);
22788e3358fSPrem Chintalapudi 
22888e3358fSPrem Chintalapudi   if (State.MarkerAddr == MAP_FAILED)
22988e3358fSPrem Chintalapudi     return make_error<llvm::StringError>("could not mmap JIT marker",
23088e3358fSPrem Chintalapudi                                          inconvertibleErrorCode());
23188e3358fSPrem Chintalapudi 
23288e3358fSPrem Chintalapudi   return Error::success();
23388e3358fSPrem Chintalapudi }
23488e3358fSPrem Chintalapudi 
23588e3358fSPrem Chintalapudi void CloseMarker(PerfState &State) {
23688e3358fSPrem Chintalapudi   if (!State.MarkerAddr)
23788e3358fSPrem Chintalapudi     return;
23888e3358fSPrem Chintalapudi 
23988e3358fSPrem Chintalapudi   munmap(State.MarkerAddr, sys::Process::getPageSizeEstimate());
24088e3358fSPrem Chintalapudi   State.MarkerAddr = nullptr;
24188e3358fSPrem Chintalapudi }
24288e3358fSPrem Chintalapudi 
24388e3358fSPrem Chintalapudi static Expected<Header> FillMachine(PerfState &State) {
24488e3358fSPrem Chintalapudi   Header Hdr;
24588e3358fSPrem Chintalapudi   Hdr.Magic = LLVM_PERF_JIT_MAGIC;
24688e3358fSPrem Chintalapudi   Hdr.Version = LLVM_PERF_JIT_VERSION;
24788e3358fSPrem Chintalapudi   Hdr.TotalSize = sizeof(Hdr);
24888e3358fSPrem Chintalapudi   Hdr.Pid = State.Pid;
24988e3358fSPrem Chintalapudi   Hdr.Timestamp = perf_get_timestamp();
25088e3358fSPrem Chintalapudi 
25188e3358fSPrem Chintalapudi   char Id[16];
25288e3358fSPrem Chintalapudi   struct {
25388e3358fSPrem Chintalapudi     uint16_t e_type;
25488e3358fSPrem Chintalapudi     uint16_t e_machine;
25588e3358fSPrem Chintalapudi   } Info;
25688e3358fSPrem Chintalapudi 
25788e3358fSPrem Chintalapudi   size_t RequiredMemory = sizeof(Id) + sizeof(Info);
25888e3358fSPrem Chintalapudi 
25988e3358fSPrem Chintalapudi   ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
26088e3358fSPrem Chintalapudi       MemoryBuffer::getFileSlice("/proc/self/exe", RequiredMemory, 0);
26188e3358fSPrem Chintalapudi 
26288e3358fSPrem Chintalapudi   // This'll not guarantee that enough data was actually read from the
26388e3358fSPrem Chintalapudi   // underlying file. Instead the trailing part of the buffer would be
26488e3358fSPrem Chintalapudi   // zeroed. Given the ELF signature check below that seems ok though,
26588e3358fSPrem Chintalapudi   // it's unlikely that the file ends just after that, and the
26688e3358fSPrem Chintalapudi   // consequence would just be that perf wouldn't recognize the
26788e3358fSPrem Chintalapudi   // signature.
26888e3358fSPrem Chintalapudi   if (!MB)
26988e3358fSPrem Chintalapudi     return make_error<llvm::StringError>("could not open /proc/self/exe",
27088e3358fSPrem Chintalapudi                                          MB.getError());
27188e3358fSPrem Chintalapudi 
27288e3358fSPrem Chintalapudi   memcpy(&Id, (*MB)->getBufferStart(), sizeof(Id));
27388e3358fSPrem Chintalapudi   memcpy(&Info, (*MB)->getBufferStart() + sizeof(Id), sizeof(Info));
27488e3358fSPrem Chintalapudi 
27588e3358fSPrem Chintalapudi   // check ELF signature
27688e3358fSPrem Chintalapudi   if (Id[0] != 0x7f || Id[1] != 'E' || Id[2] != 'L' || Id[3] != 'F')
27788e3358fSPrem Chintalapudi     return make_error<llvm::StringError>("invalid ELF signature",
27888e3358fSPrem Chintalapudi                                          inconvertibleErrorCode());
27988e3358fSPrem Chintalapudi 
28088e3358fSPrem Chintalapudi   Hdr.ElfMach = Info.e_machine;
28188e3358fSPrem Chintalapudi 
28288e3358fSPrem Chintalapudi   return Hdr;
28388e3358fSPrem Chintalapudi }
28488e3358fSPrem Chintalapudi 
28588e3358fSPrem Chintalapudi static Error InitDebuggingDir(PerfState &State) {
28688e3358fSPrem Chintalapudi   time_t Time;
28788e3358fSPrem Chintalapudi   struct tm LocalTime;
28888e3358fSPrem Chintalapudi   char TimeBuffer[sizeof("YYYYMMDD")];
28988e3358fSPrem Chintalapudi   SmallString<64> Path;
29088e3358fSPrem Chintalapudi 
29188e3358fSPrem Chintalapudi   // search for location to dump data to
29288e3358fSPrem Chintalapudi   if (const char *BaseDir = getenv("JITDUMPDIR"))
29388e3358fSPrem Chintalapudi     Path.append(BaseDir);
29488e3358fSPrem Chintalapudi   else if (!sys::path::home_directory(Path))
29588e3358fSPrem Chintalapudi     Path = ".";
29688e3358fSPrem Chintalapudi 
29788e3358fSPrem Chintalapudi   // create debug directory
29888e3358fSPrem Chintalapudi   Path += "/.debug/jit/";
29988e3358fSPrem Chintalapudi   if (auto EC = sys::fs::create_directories(Path)) {
30088e3358fSPrem Chintalapudi     std::string ErrStr;
30188e3358fSPrem Chintalapudi     raw_string_ostream ErrStream(ErrStr);
30288e3358fSPrem Chintalapudi     ErrStream << "could not create jit cache directory " << Path << ": "
30388e3358fSPrem Chintalapudi               << EC.message() << "\n";
30488e3358fSPrem Chintalapudi     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
30588e3358fSPrem Chintalapudi   }
30688e3358fSPrem Chintalapudi 
30788e3358fSPrem Chintalapudi   // create unique directory for dump data related to this process
30888e3358fSPrem Chintalapudi   time(&Time);
30988e3358fSPrem Chintalapudi   localtime_r(&Time, &LocalTime);
31088e3358fSPrem Chintalapudi   strftime(TimeBuffer, sizeof(TimeBuffer), "%Y%m%d", &LocalTime);
31188e3358fSPrem Chintalapudi   Path += JIT_LANG "-jit-";
31288e3358fSPrem Chintalapudi   Path += TimeBuffer;
31388e3358fSPrem Chintalapudi 
31488e3358fSPrem Chintalapudi   SmallString<128> UniqueDebugDir;
31588e3358fSPrem Chintalapudi 
31688e3358fSPrem Chintalapudi   using sys::fs::createUniqueDirectory;
31788e3358fSPrem Chintalapudi   if (auto EC = createUniqueDirectory(Path, UniqueDebugDir)) {
31888e3358fSPrem Chintalapudi     std::string ErrStr;
31988e3358fSPrem Chintalapudi     raw_string_ostream ErrStream(ErrStr);
32088e3358fSPrem Chintalapudi     ErrStream << "could not create unique jit cache directory "
32188e3358fSPrem Chintalapudi               << UniqueDebugDir << ": " << EC.message() << "\n";
32288e3358fSPrem Chintalapudi     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
32388e3358fSPrem Chintalapudi   }
32488e3358fSPrem Chintalapudi 
325b7a66d0fSKazu Hirata   State.JitPath = std::string(UniqueDebugDir);
32688e3358fSPrem Chintalapudi 
32788e3358fSPrem Chintalapudi   return Error::success();
32888e3358fSPrem Chintalapudi }
32988e3358fSPrem Chintalapudi 
33088e3358fSPrem Chintalapudi static Error registerJITLoaderPerfStartImpl() {
33188e3358fSPrem Chintalapudi   PerfState Tentative;
33288e3358fSPrem Chintalapudi   Tentative.Pid = sys::Process::getProcessId();
33388e3358fSPrem Chintalapudi   // check if clock-source is supported
33488e3358fSPrem Chintalapudi   if (!perf_get_timestamp())
33588e3358fSPrem Chintalapudi     return make_error<StringError>("kernel does not support CLOCK_MONOTONIC",
33688e3358fSPrem Chintalapudi                                    inconvertibleErrorCode());
33788e3358fSPrem Chintalapudi 
33888e3358fSPrem Chintalapudi   if (auto Err = InitDebuggingDir(Tentative))
33988e3358fSPrem Chintalapudi     return Err;
34088e3358fSPrem Chintalapudi 
34188e3358fSPrem Chintalapudi   std::string Filename;
34288e3358fSPrem Chintalapudi   raw_string_ostream FilenameBuf(Filename);
34388e3358fSPrem Chintalapudi   FilenameBuf << Tentative.JitPath << "/jit-" << Tentative.Pid << ".dump";
34488e3358fSPrem Chintalapudi 
34588e3358fSPrem Chintalapudi   // Need to open ourselves, because we need to hand the FD to OpenMarker() and
34688e3358fSPrem Chintalapudi   // raw_fd_ostream doesn't expose the FD.
34788e3358fSPrem Chintalapudi   using sys::fs::openFileForWrite;
348*a75565a6SJOE1994   if (auto EC = openFileForReadWrite(Filename, Tentative.DumpFd,
34988e3358fSPrem Chintalapudi                                      sys::fs::CD_CreateNew, sys::fs::OF_None)) {
35088e3358fSPrem Chintalapudi     std::string ErrStr;
35188e3358fSPrem Chintalapudi     raw_string_ostream ErrStream(ErrStr);
352*a75565a6SJOE1994     ErrStream << "could not open JIT dump file " << Filename << ": "
35388e3358fSPrem Chintalapudi               << EC.message() << "\n";
35488e3358fSPrem Chintalapudi     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
35588e3358fSPrem Chintalapudi   }
35688e3358fSPrem Chintalapudi 
35788e3358fSPrem Chintalapudi   Tentative.Dumpstream =
35888e3358fSPrem Chintalapudi       std::make_unique<raw_fd_ostream>(Tentative.DumpFd, true);
35988e3358fSPrem Chintalapudi 
36088e3358fSPrem Chintalapudi   auto Header = FillMachine(Tentative);
36188e3358fSPrem Chintalapudi   if (!Header)
36288e3358fSPrem Chintalapudi     return Header.takeError();
36388e3358fSPrem Chintalapudi 
36488e3358fSPrem Chintalapudi   // signal this process emits JIT information
36588e3358fSPrem Chintalapudi   if (auto Err = OpenMarker(Tentative))
36688e3358fSPrem Chintalapudi     return Err;
36788e3358fSPrem Chintalapudi 
36888e3358fSPrem Chintalapudi   Tentative.Dumpstream->write(reinterpret_cast<const char *>(&Header.get()),
36988e3358fSPrem Chintalapudi                               sizeof(*Header));
37088e3358fSPrem Chintalapudi 
37188e3358fSPrem Chintalapudi   // Everything initialized, can do profiling now.
37288e3358fSPrem Chintalapudi   if (Tentative.Dumpstream->has_error())
37388e3358fSPrem Chintalapudi     return make_error<StringError>("could not write JIT dump header",
37488e3358fSPrem Chintalapudi                                    inconvertibleErrorCode());
37588e3358fSPrem Chintalapudi 
37688e3358fSPrem Chintalapudi   State = std::move(Tentative);
37788e3358fSPrem Chintalapudi   return Error::success();
37888e3358fSPrem Chintalapudi }
37988e3358fSPrem Chintalapudi 
38088e3358fSPrem Chintalapudi static Error registerJITLoaderPerfEndImpl() {
38188e3358fSPrem Chintalapudi   if (!State)
38288e3358fSPrem Chintalapudi     return make_error<StringError>("PerfState not initialized",
38388e3358fSPrem Chintalapudi                                    inconvertibleErrorCode());
38488e3358fSPrem Chintalapudi 
38588e3358fSPrem Chintalapudi   RecHeader Close;
38688e3358fSPrem Chintalapudi   Close.Id = static_cast<uint32_t>(PerfJITRecordType::JIT_CODE_CLOSE);
38788e3358fSPrem Chintalapudi   Close.TotalSize = sizeof(Close);
38888e3358fSPrem Chintalapudi   Close.Timestamp = perf_get_timestamp();
38988e3358fSPrem Chintalapudi   State->Dumpstream->write(reinterpret_cast<const char *>(&Close),
39088e3358fSPrem Chintalapudi                            sizeof(Close));
39188e3358fSPrem Chintalapudi   if (State->MarkerAddr)
39288e3358fSPrem Chintalapudi     CloseMarker(*State);
39388e3358fSPrem Chintalapudi 
39488e3358fSPrem Chintalapudi   State.reset();
39588e3358fSPrem Chintalapudi   return Error::success();
39688e3358fSPrem Chintalapudi }
39788e3358fSPrem Chintalapudi 
39888e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
39988e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfImpl(const char *Data, uint64_t Size) {
40088e3358fSPrem Chintalapudi   using namespace orc::shared;
40188e3358fSPrem Chintalapudi   return WrapperFunction<SPSError(SPSPerfJITRecordBatch)>::handle(
40288e3358fSPrem Chintalapudi              Data, Size, registerJITLoaderPerfImpl)
40388e3358fSPrem Chintalapudi       .release();
40488e3358fSPrem Chintalapudi }
40588e3358fSPrem Chintalapudi 
40688e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
40788e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfStart(const char *Data, uint64_t Size) {
40888e3358fSPrem Chintalapudi   using namespace orc::shared;
40988e3358fSPrem Chintalapudi   return WrapperFunction<SPSError()>::handle(Data, Size,
41088e3358fSPrem Chintalapudi                                              registerJITLoaderPerfStartImpl)
41188e3358fSPrem Chintalapudi       .release();
41288e3358fSPrem Chintalapudi }
41388e3358fSPrem Chintalapudi 
41488e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
41588e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfEnd(const char *Data, uint64_t Size) {
41688e3358fSPrem Chintalapudi   using namespace orc::shared;
41788e3358fSPrem Chintalapudi   return WrapperFunction<SPSError()>::handle(Data, Size,
41888e3358fSPrem Chintalapudi                                              registerJITLoaderPerfEndImpl)
41988e3358fSPrem Chintalapudi       .release();
42088e3358fSPrem Chintalapudi }
42188e3358fSPrem Chintalapudi 
42288e3358fSPrem Chintalapudi #else
42388e3358fSPrem Chintalapudi 
42488e3358fSPrem Chintalapudi using namespace llvm;
42588e3358fSPrem Chintalapudi using namespace llvm::orc;
42688e3358fSPrem Chintalapudi 
42788e3358fSPrem Chintalapudi static Error badOS() {
42888e3358fSPrem Chintalapudi   using namespace llvm;
42988e3358fSPrem Chintalapudi   return llvm::make_error<StringError>(
43088e3358fSPrem Chintalapudi       "unsupported OS (perf support is only available on linux!)",
43188e3358fSPrem Chintalapudi       inconvertibleErrorCode());
43288e3358fSPrem Chintalapudi }
43388e3358fSPrem Chintalapudi 
43488e3358fSPrem Chintalapudi static Error badOSBatch(PerfJITRecordBatch &Batch) { return badOS(); }
43588e3358fSPrem Chintalapudi 
43688e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
43788e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfImpl(const char *Data, uint64_t Size) {
43888e3358fSPrem Chintalapudi   using namespace shared;
43988e3358fSPrem Chintalapudi   return WrapperFunction<SPSError(SPSPerfJITRecordBatch)>::handle(Data, Size,
44088e3358fSPrem Chintalapudi                                                                   badOSBatch)
44188e3358fSPrem Chintalapudi       .release();
44288e3358fSPrem Chintalapudi }
44388e3358fSPrem Chintalapudi 
44488e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
44588e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfStart(const char *Data, uint64_t Size) {
44688e3358fSPrem Chintalapudi   using namespace shared;
44788e3358fSPrem Chintalapudi   return WrapperFunction<SPSError()>::handle(Data, Size, badOS).release();
44888e3358fSPrem Chintalapudi }
44988e3358fSPrem Chintalapudi 
45088e3358fSPrem Chintalapudi extern "C" llvm::orc::shared::CWrapperFunctionResult
45188e3358fSPrem Chintalapudi llvm_orc_registerJITLoaderPerfEnd(const char *Data, uint64_t Size) {
45288e3358fSPrem Chintalapudi   using namespace shared;
45388e3358fSPrem Chintalapudi   return WrapperFunction<SPSError()>::handle(Data, Size, badOS).release();
45488e3358fSPrem Chintalapudi }
45588e3358fSPrem Chintalapudi 
45688e3358fSPrem Chintalapudi #endif
457