1c9157d92SDimitry Andric //===------- JITLoaderPerf.cpp - Register profiler objects ------*- C++ -*-===//
2c9157d92SDimitry Andric //
3c9157d92SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c9157d92SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5c9157d92SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c9157d92SDimitry Andric //
7c9157d92SDimitry Andric //===----------------------------------------------------------------------===//
8c9157d92SDimitry Andric //
9c9157d92SDimitry Andric // Register objects for access by profilers via the perf JIT interface.
10c9157d92SDimitry Andric //
11c9157d92SDimitry Andric //===----------------------------------------------------------------------===//
12c9157d92SDimitry Andric 
13c9157d92SDimitry Andric #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.h"
14c9157d92SDimitry Andric 
15c9157d92SDimitry Andric #include "llvm/ExecutionEngine/Orc/Shared/PerfSharedStructs.h"
16c9157d92SDimitry Andric 
17c9157d92SDimitry Andric #include "llvm/Support/FileSystem.h"
18c9157d92SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
19c9157d92SDimitry Andric #include "llvm/Support/Path.h"
20c9157d92SDimitry Andric #include "llvm/Support/Process.h"
21c9157d92SDimitry Andric #include "llvm/Support/Threading.h"
22c9157d92SDimitry Andric 
23c9157d92SDimitry Andric #include <mutex>
24c9157d92SDimitry Andric #include <optional>
25c9157d92SDimitry Andric 
26c9157d92SDimitry Andric #ifdef __linux__
27c9157d92SDimitry Andric 
28c9157d92SDimitry Andric #include <sys/mman.h> // mmap()
29c9157d92SDimitry Andric #include <time.h>     // clock_gettime(), time(), localtime_r() */
30c9157d92SDimitry Andric #include <unistd.h>   // for read(), close()
31c9157d92SDimitry Andric 
32c9157d92SDimitry Andric #define DEBUG_TYPE "orc"
33c9157d92SDimitry Andric 
34c9157d92SDimitry Andric // language identifier (XXX: should we generate something better from debug
35c9157d92SDimitry Andric // info?)
36c9157d92SDimitry Andric #define JIT_LANG "llvm-IR"
37c9157d92SDimitry Andric #define LLVM_PERF_JIT_MAGIC                                                    \
38c9157d92SDimitry Andric   ((uint32_t)'J' << 24 | (uint32_t)'i' << 16 | (uint32_t)'T' << 8 |            \
39c9157d92SDimitry Andric    (uint32_t)'D')
40c9157d92SDimitry Andric #define LLVM_PERF_JIT_VERSION 1
41c9157d92SDimitry Andric 
42c9157d92SDimitry Andric using namespace llvm;
43c9157d92SDimitry Andric using namespace llvm::orc;
44c9157d92SDimitry Andric 
45c9157d92SDimitry Andric struct PerfState {
46c9157d92SDimitry Andric   // cache lookups
47c9157d92SDimitry Andric   uint32_t Pid;
48c9157d92SDimitry Andric 
49c9157d92SDimitry Andric   // base directory for output data
50c9157d92SDimitry Andric   std::string JitPath;
51c9157d92SDimitry Andric 
52c9157d92SDimitry Andric   // output data stream, closed via Dumpstream
53c9157d92SDimitry Andric   int DumpFd = -1;
54c9157d92SDimitry Andric 
55c9157d92SDimitry Andric   // output data stream
56c9157d92SDimitry Andric   std::unique_ptr<raw_fd_ostream> Dumpstream;
57c9157d92SDimitry Andric 
58c9157d92SDimitry Andric   // perf mmap marker
59c9157d92SDimitry Andric   void *MarkerAddr = NULL;
60c9157d92SDimitry Andric };
61c9157d92SDimitry Andric 
62c9157d92SDimitry Andric // prevent concurrent dumps from messing up the output file
63c9157d92SDimitry Andric static std::mutex Mutex;
64c9157d92SDimitry Andric static std::optional<PerfState> State;
65c9157d92SDimitry Andric 
66c9157d92SDimitry Andric struct RecHeader {
67c9157d92SDimitry Andric   uint32_t Id;
68c9157d92SDimitry Andric   uint32_t TotalSize;
69c9157d92SDimitry Andric   uint64_t Timestamp;
70c9157d92SDimitry Andric };
71c9157d92SDimitry Andric 
72c9157d92SDimitry Andric struct DIR {
73c9157d92SDimitry Andric   RecHeader Prefix;
74c9157d92SDimitry Andric   uint64_t CodeAddr;
75c9157d92SDimitry Andric   uint64_t NrEntry;
76c9157d92SDimitry Andric };
77c9157d92SDimitry Andric 
78c9157d92SDimitry Andric struct DIE {
79c9157d92SDimitry Andric   uint64_t CodeAddr;
80c9157d92SDimitry Andric   uint32_t Line;
81c9157d92SDimitry Andric   uint32_t Discrim;
82c9157d92SDimitry Andric };
83c9157d92SDimitry Andric 
84c9157d92SDimitry Andric struct CLR {
85c9157d92SDimitry Andric   RecHeader Prefix;
86c9157d92SDimitry Andric   uint32_t Pid;
87c9157d92SDimitry Andric   uint32_t Tid;
88c9157d92SDimitry Andric   uint64_t Vma;
89c9157d92SDimitry Andric   uint64_t CodeAddr;
90c9157d92SDimitry Andric   uint64_t CodeSize;
91c9157d92SDimitry Andric   uint64_t CodeIndex;
92c9157d92SDimitry Andric };
93c9157d92SDimitry Andric 
94c9157d92SDimitry Andric struct UWR {
95c9157d92SDimitry Andric   RecHeader Prefix;
96c9157d92SDimitry Andric   uint64_t UnwindDataSize;
97c9157d92SDimitry Andric   uint64_t EhFrameHeaderSize;
98c9157d92SDimitry Andric   uint64_t MappedSize;
99c9157d92SDimitry Andric };
100c9157d92SDimitry Andric 
timespec_to_ns(const struct timespec * TS)101c9157d92SDimitry Andric static inline uint64_t timespec_to_ns(const struct timespec *TS) {
102c9157d92SDimitry Andric   const uint64_t NanoSecPerSec = 1000000000;
103c9157d92SDimitry Andric   return ((uint64_t)TS->tv_sec * NanoSecPerSec) + TS->tv_nsec;
104c9157d92SDimitry Andric }
105c9157d92SDimitry Andric 
perf_get_timestamp()106c9157d92SDimitry Andric static inline uint64_t perf_get_timestamp() {
107c9157d92SDimitry Andric   timespec TS;
108c9157d92SDimitry Andric   if (clock_gettime(CLOCK_MONOTONIC, &TS))
109c9157d92SDimitry Andric     return 0;
110c9157d92SDimitry Andric 
111c9157d92SDimitry Andric   return timespec_to_ns(&TS);
112c9157d92SDimitry Andric }
113c9157d92SDimitry Andric 
writeDebugRecord(const PerfJITDebugInfoRecord & DebugRecord)114c9157d92SDimitry Andric static void writeDebugRecord(const PerfJITDebugInfoRecord &DebugRecord) {
115c9157d92SDimitry Andric   assert(State && "PerfState not initialized");
116c9157d92SDimitry Andric   LLVM_DEBUG(dbgs() << "Writing debug record with "
117c9157d92SDimitry Andric                     << DebugRecord.Entries.size() << " entries\n");
118c9157d92SDimitry Andric   [[maybe_unused]] size_t Written = 0;
119c9157d92SDimitry Andric   DIR Dir{RecHeader{static_cast<uint32_t>(DebugRecord.Prefix.Id),
120c9157d92SDimitry Andric                     DebugRecord.Prefix.TotalSize, perf_get_timestamp()},
121c9157d92SDimitry Andric           DebugRecord.CodeAddr, DebugRecord.Entries.size()};
122c9157d92SDimitry Andric   State->Dumpstream->write(reinterpret_cast<const char *>(&Dir), sizeof(Dir));
123c9157d92SDimitry Andric   Written += sizeof(Dir);
124c9157d92SDimitry Andric   for (auto &Die : DebugRecord.Entries) {
125c9157d92SDimitry Andric     DIE d{Die.Addr, Die.Lineno, Die.Discrim};
126c9157d92SDimitry Andric     State->Dumpstream->write(reinterpret_cast<const char *>(&d), sizeof(d));
127c9157d92SDimitry Andric     State->Dumpstream->write(Die.Name.data(), Die.Name.size() + 1);
128c9157d92SDimitry Andric     Written += sizeof(d) + Die.Name.size() + 1;
129c9157d92SDimitry Andric   }
130c9157d92SDimitry Andric   LLVM_DEBUG(dbgs() << "wrote " << Written << " bytes of debug info\n");
131c9157d92SDimitry Andric }
132c9157d92SDimitry Andric 
writeCodeRecord(const PerfJITCodeLoadRecord & CodeRecord)133c9157d92SDimitry Andric static void writeCodeRecord(const PerfJITCodeLoadRecord &CodeRecord) {
134c9157d92SDimitry Andric   assert(State && "PerfState not initialized");
135c9157d92SDimitry Andric   uint32_t Tid = get_threadid();
136c9157d92SDimitry Andric   LLVM_DEBUG(dbgs() << "Writing code record with code size "
137c9157d92SDimitry Andric                     << CodeRecord.CodeSize << " and code index "
138c9157d92SDimitry Andric                     << CodeRecord.CodeIndex << "\n");
139c9157d92SDimitry Andric   CLR Clr{RecHeader{static_cast<uint32_t>(CodeRecord.Prefix.Id),
140c9157d92SDimitry Andric                     CodeRecord.Prefix.TotalSize, perf_get_timestamp()},
141c9157d92SDimitry Andric           State->Pid,
142c9157d92SDimitry Andric           Tid,
143c9157d92SDimitry Andric           CodeRecord.Vma,
144c9157d92SDimitry Andric           CodeRecord.CodeAddr,
145c9157d92SDimitry Andric           CodeRecord.CodeSize,
146c9157d92SDimitry Andric           CodeRecord.CodeIndex};
147c9157d92SDimitry Andric   LLVM_DEBUG(dbgs() << "wrote " << sizeof(Clr) << " bytes of CLR, "
148c9157d92SDimitry Andric                     << CodeRecord.Name.size() + 1 << " bytes of name, "
149c9157d92SDimitry Andric                     << CodeRecord.CodeSize << " bytes of code\n");
150c9157d92SDimitry Andric   State->Dumpstream->write(reinterpret_cast<const char *>(&Clr), sizeof(Clr));
151c9157d92SDimitry Andric   State->Dumpstream->write(CodeRecord.Name.data(), CodeRecord.Name.size() + 1);
152c9157d92SDimitry Andric   State->Dumpstream->write((const char *)CodeRecord.CodeAddr,
153c9157d92SDimitry Andric                            CodeRecord.CodeSize);
154c9157d92SDimitry Andric }
155c9157d92SDimitry Andric 
156c9157d92SDimitry Andric static void
writeUnwindRecord(const PerfJITCodeUnwindingInfoRecord & UnwindRecord)157c9157d92SDimitry Andric writeUnwindRecord(const PerfJITCodeUnwindingInfoRecord &UnwindRecord) {
158c9157d92SDimitry Andric   assert(State && "PerfState not initialized");
159c9157d92SDimitry Andric   dbgs() << "Writing unwind record with unwind data size "
160c9157d92SDimitry Andric          << UnwindRecord.UnwindDataSize << " and EH frame header size "
161c9157d92SDimitry Andric          << UnwindRecord.EHFrameHdrSize << " and mapped size "
162c9157d92SDimitry Andric          << UnwindRecord.MappedSize << "\n";
163c9157d92SDimitry Andric   UWR Uwr{RecHeader{static_cast<uint32_t>(UnwindRecord.Prefix.Id),
164c9157d92SDimitry Andric                     UnwindRecord.Prefix.TotalSize, perf_get_timestamp()},
165c9157d92SDimitry Andric           UnwindRecord.UnwindDataSize, UnwindRecord.EHFrameHdrSize,
166c9157d92SDimitry Andric           UnwindRecord.MappedSize};
167c9157d92SDimitry Andric   LLVM_DEBUG(dbgs() << "wrote " << sizeof(Uwr) << " bytes of UWR, "
168c9157d92SDimitry Andric                     << UnwindRecord.EHFrameHdrSize
169c9157d92SDimitry Andric                     << " bytes of EH frame header, "
170c9157d92SDimitry Andric                     << UnwindRecord.UnwindDataSize - UnwindRecord.EHFrameHdrSize
171c9157d92SDimitry Andric                     << " bytes of EH frame\n");
172c9157d92SDimitry Andric   State->Dumpstream->write(reinterpret_cast<const char *>(&Uwr), sizeof(Uwr));
173c9157d92SDimitry Andric   if (UnwindRecord.EHFrameHdrAddr)
174c9157d92SDimitry Andric     State->Dumpstream->write((const char *)UnwindRecord.EHFrameHdrAddr,
175c9157d92SDimitry Andric                              UnwindRecord.EHFrameHdrSize);
176c9157d92SDimitry Andric   else
177c9157d92SDimitry Andric     State->Dumpstream->write(UnwindRecord.EHFrameHdr.data(),
178c9157d92SDimitry Andric                              UnwindRecord.EHFrameHdrSize);
179c9157d92SDimitry Andric   State->Dumpstream->write((const char *)UnwindRecord.EHFrameAddr,
180c9157d92SDimitry Andric                            UnwindRecord.UnwindDataSize -
181c9157d92SDimitry Andric                                UnwindRecord.EHFrameHdrSize);
182c9157d92SDimitry Andric }
183c9157d92SDimitry Andric 
registerJITLoaderPerfImpl(const PerfJITRecordBatch & Batch)184c9157d92SDimitry Andric static Error registerJITLoaderPerfImpl(const PerfJITRecordBatch &Batch) {
185c9157d92SDimitry Andric   if (!State)
186c9157d92SDimitry Andric     return make_error<StringError>("PerfState not initialized",
187c9157d92SDimitry Andric                                    inconvertibleErrorCode());
188c9157d92SDimitry Andric 
189c9157d92SDimitry Andric   // Serialize the batch
190c9157d92SDimitry Andric   std::lock_guard<std::mutex> Lock(Mutex);
191c9157d92SDimitry Andric   if (Batch.UnwindingRecord.Prefix.TotalSize > 0)
192c9157d92SDimitry Andric     writeUnwindRecord(Batch.UnwindingRecord);
193c9157d92SDimitry Andric 
194c9157d92SDimitry Andric   for (const auto &DebugInfo : Batch.DebugInfoRecords)
195c9157d92SDimitry Andric     writeDebugRecord(DebugInfo);
196c9157d92SDimitry Andric 
197c9157d92SDimitry Andric   for (const auto &CodeLoad : Batch.CodeLoadRecords)
198c9157d92SDimitry Andric     writeCodeRecord(CodeLoad);
199c9157d92SDimitry Andric 
200c9157d92SDimitry Andric   State->Dumpstream->flush();
201c9157d92SDimitry Andric 
202c9157d92SDimitry Andric   return Error::success();
203c9157d92SDimitry Andric }
204c9157d92SDimitry Andric 
205c9157d92SDimitry Andric struct Header {
206c9157d92SDimitry Andric   uint32_t Magic;     // characters "JiTD"
207c9157d92SDimitry Andric   uint32_t Version;   // header version
208c9157d92SDimitry Andric   uint32_t TotalSize; // total size of header
209c9157d92SDimitry Andric   uint32_t ElfMach;   // elf mach target
210c9157d92SDimitry Andric   uint32_t Pad1;      // reserved
211c9157d92SDimitry Andric   uint32_t Pid;
212c9157d92SDimitry Andric   uint64_t Timestamp; // timestamp
213c9157d92SDimitry Andric   uint64_t Flags;     // flags
214c9157d92SDimitry Andric };
215c9157d92SDimitry Andric 
OpenMarker(PerfState & State)216c9157d92SDimitry Andric static Error OpenMarker(PerfState &State) {
217c9157d92SDimitry Andric   // We mmap the jitdump to create an MMAP RECORD in perf.data file.  The mmap
218c9157d92SDimitry Andric   // is captured either live (perf record running when we mmap) or in deferred
219c9157d92SDimitry Andric   // mode, via /proc/PID/maps. The MMAP record is used as a marker of a jitdump
220c9157d92SDimitry Andric   // file for more meta data info about the jitted code. Perf report/annotate
221c9157d92SDimitry Andric   // detect this special filename and process the jitdump file.
222c9157d92SDimitry Andric   //
223c9157d92SDimitry Andric   // Mapping must be PROT_EXEC to ensure it is captured by perf record
224c9157d92SDimitry Andric   // even when not using -d option.
225c9157d92SDimitry Andric   State.MarkerAddr =
226c9157d92SDimitry Andric       ::mmap(NULL, sys::Process::getPageSizeEstimate(), PROT_READ | PROT_EXEC,
227c9157d92SDimitry Andric              MAP_PRIVATE, State.DumpFd, 0);
228c9157d92SDimitry Andric 
229c9157d92SDimitry Andric   if (State.MarkerAddr == MAP_FAILED)
230c9157d92SDimitry Andric     return make_error<llvm::StringError>("could not mmap JIT marker",
231c9157d92SDimitry Andric                                          inconvertibleErrorCode());
232c9157d92SDimitry Andric 
233c9157d92SDimitry Andric   return Error::success();
234c9157d92SDimitry Andric }
235c9157d92SDimitry Andric 
CloseMarker(PerfState & State)236c9157d92SDimitry Andric void CloseMarker(PerfState &State) {
237c9157d92SDimitry Andric   if (!State.MarkerAddr)
238c9157d92SDimitry Andric     return;
239c9157d92SDimitry Andric 
240c9157d92SDimitry Andric   munmap(State.MarkerAddr, sys::Process::getPageSizeEstimate());
241c9157d92SDimitry Andric   State.MarkerAddr = nullptr;
242c9157d92SDimitry Andric }
243c9157d92SDimitry Andric 
FillMachine(PerfState & State)244c9157d92SDimitry Andric static Expected<Header> FillMachine(PerfState &State) {
245c9157d92SDimitry Andric   Header Hdr;
246c9157d92SDimitry Andric   Hdr.Magic = LLVM_PERF_JIT_MAGIC;
247c9157d92SDimitry Andric   Hdr.Version = LLVM_PERF_JIT_VERSION;
248c9157d92SDimitry Andric   Hdr.TotalSize = sizeof(Hdr);
249c9157d92SDimitry Andric   Hdr.Pid = State.Pid;
250c9157d92SDimitry Andric   Hdr.Timestamp = perf_get_timestamp();
251c9157d92SDimitry Andric 
252c9157d92SDimitry Andric   char Id[16];
253c9157d92SDimitry Andric   struct {
254c9157d92SDimitry Andric     uint16_t e_type;
255c9157d92SDimitry Andric     uint16_t e_machine;
256c9157d92SDimitry Andric   } Info;
257c9157d92SDimitry Andric 
258c9157d92SDimitry Andric   size_t RequiredMemory = sizeof(Id) + sizeof(Info);
259c9157d92SDimitry Andric 
260c9157d92SDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
261c9157d92SDimitry Andric       MemoryBuffer::getFileSlice("/proc/self/exe", RequiredMemory, 0);
262c9157d92SDimitry Andric 
263c9157d92SDimitry Andric   // This'll not guarantee that enough data was actually read from the
264c9157d92SDimitry Andric   // underlying file. Instead the trailing part of the buffer would be
265c9157d92SDimitry Andric   // zeroed. Given the ELF signature check below that seems ok though,
266c9157d92SDimitry Andric   // it's unlikely that the file ends just after that, and the
267c9157d92SDimitry Andric   // consequence would just be that perf wouldn't recognize the
268c9157d92SDimitry Andric   // signature.
269c9157d92SDimitry Andric   if (!MB)
270c9157d92SDimitry Andric     return make_error<llvm::StringError>("could not open /proc/self/exe",
271c9157d92SDimitry Andric                                          MB.getError());
272c9157d92SDimitry Andric 
273c9157d92SDimitry Andric   memcpy(&Id, (*MB)->getBufferStart(), sizeof(Id));
274c9157d92SDimitry Andric   memcpy(&Info, (*MB)->getBufferStart() + sizeof(Id), sizeof(Info));
275c9157d92SDimitry Andric 
276c9157d92SDimitry Andric   // check ELF signature
277c9157d92SDimitry Andric   if (Id[0] != 0x7f || Id[1] != 'E' || Id[2] != 'L' || Id[3] != 'F')
278c9157d92SDimitry Andric     return make_error<llvm::StringError>("invalid ELF signature",
279c9157d92SDimitry Andric                                          inconvertibleErrorCode());
280c9157d92SDimitry Andric 
281c9157d92SDimitry Andric   Hdr.ElfMach = Info.e_machine;
282c9157d92SDimitry Andric 
283c9157d92SDimitry Andric   return Hdr;
284c9157d92SDimitry Andric }
285c9157d92SDimitry Andric 
InitDebuggingDir(PerfState & State)286c9157d92SDimitry Andric static Error InitDebuggingDir(PerfState &State) {
287c9157d92SDimitry Andric   time_t Time;
288c9157d92SDimitry Andric   struct tm LocalTime;
289c9157d92SDimitry Andric   char TimeBuffer[sizeof("YYYYMMDD")];
290c9157d92SDimitry Andric   SmallString<64> Path;
291c9157d92SDimitry Andric 
292c9157d92SDimitry Andric   // search for location to dump data to
293c9157d92SDimitry Andric   if (const char *BaseDir = getenv("JITDUMPDIR"))
294c9157d92SDimitry Andric     Path.append(BaseDir);
295c9157d92SDimitry Andric   else if (!sys::path::home_directory(Path))
296c9157d92SDimitry Andric     Path = ".";
297c9157d92SDimitry Andric 
298c9157d92SDimitry Andric   // create debug directory
299c9157d92SDimitry Andric   Path += "/.debug/jit/";
300c9157d92SDimitry Andric   if (auto EC = sys::fs::create_directories(Path)) {
301c9157d92SDimitry Andric     std::string ErrStr;
302c9157d92SDimitry Andric     raw_string_ostream ErrStream(ErrStr);
303c9157d92SDimitry Andric     ErrStream << "could not create jit cache directory " << Path << ": "
304c9157d92SDimitry Andric               << EC.message() << "\n";
305c9157d92SDimitry Andric     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
306c9157d92SDimitry Andric   }
307c9157d92SDimitry Andric 
308c9157d92SDimitry Andric   // create unique directory for dump data related to this process
309c9157d92SDimitry Andric   time(&Time);
310c9157d92SDimitry Andric   localtime_r(&Time, &LocalTime);
311c9157d92SDimitry Andric   strftime(TimeBuffer, sizeof(TimeBuffer), "%Y%m%d", &LocalTime);
312c9157d92SDimitry Andric   Path += JIT_LANG "-jit-";
313c9157d92SDimitry Andric   Path += TimeBuffer;
314c9157d92SDimitry Andric 
315c9157d92SDimitry Andric   SmallString<128> UniqueDebugDir;
316c9157d92SDimitry Andric 
317c9157d92SDimitry Andric   using sys::fs::createUniqueDirectory;
318c9157d92SDimitry Andric   if (auto EC = createUniqueDirectory(Path, UniqueDebugDir)) {
319c9157d92SDimitry Andric     std::string ErrStr;
320c9157d92SDimitry Andric     raw_string_ostream ErrStream(ErrStr);
321c9157d92SDimitry Andric     ErrStream << "could not create unique jit cache directory "
322c9157d92SDimitry Andric               << UniqueDebugDir << ": " << EC.message() << "\n";
323c9157d92SDimitry Andric     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
324c9157d92SDimitry Andric   }
325c9157d92SDimitry Andric 
326*a58f00eaSDimitry Andric   State.JitPath = std::string(UniqueDebugDir);
327c9157d92SDimitry Andric 
328c9157d92SDimitry Andric   return Error::success();
329c9157d92SDimitry Andric }
330c9157d92SDimitry Andric 
registerJITLoaderPerfStartImpl()331c9157d92SDimitry Andric static Error registerJITLoaderPerfStartImpl() {
332c9157d92SDimitry Andric   PerfState Tentative;
333c9157d92SDimitry Andric   Tentative.Pid = sys::Process::getProcessId();
334c9157d92SDimitry Andric   // check if clock-source is supported
335c9157d92SDimitry Andric   if (!perf_get_timestamp())
336c9157d92SDimitry Andric     return make_error<StringError>("kernel does not support CLOCK_MONOTONIC",
337c9157d92SDimitry Andric                                    inconvertibleErrorCode());
338c9157d92SDimitry Andric 
339c9157d92SDimitry Andric   if (auto Err = InitDebuggingDir(Tentative))
340c9157d92SDimitry Andric     return Err;
341c9157d92SDimitry Andric 
342c9157d92SDimitry Andric   std::string Filename;
343c9157d92SDimitry Andric   raw_string_ostream FilenameBuf(Filename);
344c9157d92SDimitry Andric   FilenameBuf << Tentative.JitPath << "/jit-" << Tentative.Pid << ".dump";
345c9157d92SDimitry Andric 
346c9157d92SDimitry Andric   // Need to open ourselves, because we need to hand the FD to OpenMarker() and
347c9157d92SDimitry Andric   // raw_fd_ostream doesn't expose the FD.
348c9157d92SDimitry Andric   using sys::fs::openFileForWrite;
349c9157d92SDimitry Andric   if (auto EC = openFileForReadWrite(FilenameBuf.str(), Tentative.DumpFd,
350c9157d92SDimitry Andric                                      sys::fs::CD_CreateNew, sys::fs::OF_None)) {
351c9157d92SDimitry Andric     std::string ErrStr;
352c9157d92SDimitry Andric     raw_string_ostream ErrStream(ErrStr);
353c9157d92SDimitry Andric     ErrStream << "could not open JIT dump file " << FilenameBuf.str() << ": "
354c9157d92SDimitry Andric               << EC.message() << "\n";
355c9157d92SDimitry Andric     return make_error<StringError>(std::move(ErrStr), inconvertibleErrorCode());
356c9157d92SDimitry Andric   }
357c9157d92SDimitry Andric 
358c9157d92SDimitry Andric   Tentative.Dumpstream =
359c9157d92SDimitry Andric       std::make_unique<raw_fd_ostream>(Tentative.DumpFd, true);
360c9157d92SDimitry Andric 
361c9157d92SDimitry Andric   auto Header = FillMachine(Tentative);
362c9157d92SDimitry Andric   if (!Header)
363c9157d92SDimitry Andric     return Header.takeError();
364c9157d92SDimitry Andric 
365c9157d92SDimitry Andric   // signal this process emits JIT information
366c9157d92SDimitry Andric   if (auto Err = OpenMarker(Tentative))
367c9157d92SDimitry Andric     return Err;
368c9157d92SDimitry Andric 
369c9157d92SDimitry Andric   Tentative.Dumpstream->write(reinterpret_cast<const char *>(&Header.get()),
370c9157d92SDimitry Andric                               sizeof(*Header));
371c9157d92SDimitry Andric 
372c9157d92SDimitry Andric   // Everything initialized, can do profiling now.
373c9157d92SDimitry Andric   if (Tentative.Dumpstream->has_error())
374c9157d92SDimitry Andric     return make_error<StringError>("could not write JIT dump header",
375c9157d92SDimitry Andric                                    inconvertibleErrorCode());
376c9157d92SDimitry Andric 
377c9157d92SDimitry Andric   State = std::move(Tentative);
378c9157d92SDimitry Andric   return Error::success();
379c9157d92SDimitry Andric }
380c9157d92SDimitry Andric 
registerJITLoaderPerfEndImpl()381c9157d92SDimitry Andric static Error registerJITLoaderPerfEndImpl() {
382c9157d92SDimitry Andric   if (!State)
383c9157d92SDimitry Andric     return make_error<StringError>("PerfState not initialized",
384c9157d92SDimitry Andric                                    inconvertibleErrorCode());
385c9157d92SDimitry Andric 
386c9157d92SDimitry Andric   RecHeader Close;
387c9157d92SDimitry Andric   Close.Id = static_cast<uint32_t>(PerfJITRecordType::JIT_CODE_CLOSE);
388c9157d92SDimitry Andric   Close.TotalSize = sizeof(Close);
389c9157d92SDimitry Andric   Close.Timestamp = perf_get_timestamp();
390c9157d92SDimitry Andric   State->Dumpstream->write(reinterpret_cast<const char *>(&Close),
391c9157d92SDimitry Andric                            sizeof(Close));
392c9157d92SDimitry Andric   if (State->MarkerAddr)
393c9157d92SDimitry Andric     CloseMarker(*State);
394c9157d92SDimitry Andric 
395c9157d92SDimitry Andric   State.reset();
396c9157d92SDimitry Andric   return Error::success();
397c9157d92SDimitry Andric }
398c9157d92SDimitry Andric 
399c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfImpl(const char * Data,uint64_t Size)400c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfImpl(const char *Data, uint64_t Size) {
401c9157d92SDimitry Andric   using namespace orc::shared;
402c9157d92SDimitry Andric   return WrapperFunction<SPSError(SPSPerfJITRecordBatch)>::handle(
403c9157d92SDimitry Andric              Data, Size, registerJITLoaderPerfImpl)
404c9157d92SDimitry Andric       .release();
405c9157d92SDimitry Andric }
406c9157d92SDimitry Andric 
407c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfStart(const char * Data,uint64_t Size)408c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfStart(const char *Data, uint64_t Size) {
409c9157d92SDimitry Andric   using namespace orc::shared;
410c9157d92SDimitry Andric   return WrapperFunction<SPSError()>::handle(Data, Size,
411c9157d92SDimitry Andric                                              registerJITLoaderPerfStartImpl)
412c9157d92SDimitry Andric       .release();
413c9157d92SDimitry Andric }
414c9157d92SDimitry Andric 
415c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfEnd(const char * Data,uint64_t Size)416c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfEnd(const char *Data, uint64_t Size) {
417c9157d92SDimitry Andric   using namespace orc::shared;
418c9157d92SDimitry Andric   return WrapperFunction<SPSError()>::handle(Data, Size,
419c9157d92SDimitry Andric                                              registerJITLoaderPerfEndImpl)
420c9157d92SDimitry Andric       .release();
421c9157d92SDimitry Andric }
422c9157d92SDimitry Andric 
423c9157d92SDimitry Andric #else
424c9157d92SDimitry Andric 
425c9157d92SDimitry Andric using namespace llvm;
426c9157d92SDimitry Andric using namespace llvm::orc;
427c9157d92SDimitry Andric 
badOS()428c9157d92SDimitry Andric static Error badOS() {
429c9157d92SDimitry Andric   using namespace llvm;
430c9157d92SDimitry Andric   return llvm::make_error<StringError>(
431c9157d92SDimitry Andric       "unsupported OS (perf support is only available on linux!)",
432c9157d92SDimitry Andric       inconvertibleErrorCode());
433c9157d92SDimitry Andric }
434c9157d92SDimitry Andric 
badOSBatch(PerfJITRecordBatch & Batch)435c9157d92SDimitry Andric static Error badOSBatch(PerfJITRecordBatch &Batch) { return badOS(); }
436c9157d92SDimitry Andric 
437c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfImpl(const char * Data,uint64_t Size)438c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfImpl(const char *Data, uint64_t Size) {
439c9157d92SDimitry Andric   using namespace shared;
440c9157d92SDimitry Andric   return WrapperFunction<SPSError(SPSPerfJITRecordBatch)>::handle(Data, Size,
441c9157d92SDimitry Andric                                                                   badOSBatch)
442c9157d92SDimitry Andric       .release();
443c9157d92SDimitry Andric }
444c9157d92SDimitry Andric 
445c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfStart(const char * Data,uint64_t Size)446c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfStart(const char *Data, uint64_t Size) {
447c9157d92SDimitry Andric   using namespace shared;
448c9157d92SDimitry Andric   return WrapperFunction<SPSError()>::handle(Data, Size, badOS).release();
449c9157d92SDimitry Andric }
450c9157d92SDimitry Andric 
451c9157d92SDimitry Andric extern "C" llvm::orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderPerfEnd(const char * Data,uint64_t Size)452c9157d92SDimitry Andric llvm_orc_registerJITLoaderPerfEnd(const char *Data, uint64_t Size) {
453c9157d92SDimitry Andric   using namespace shared;
454c9157d92SDimitry Andric   return WrapperFunction<SPSError()>::handle(Data, Size, badOS).release();
455c9157d92SDimitry Andric }
456c9157d92SDimitry Andric 
457c9157d92SDimitry Andric #endif
458