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