LLVM: lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
19#include "llvm/Config/config.h"
33#include
34
35#include <sys/mman.h>
36#include <time.h>
37#include <unistd.h>
38
39using namespace llvm;
42
43namespace {
44
45
46
47#define JIT_LANG "llvm-IR"
48#define LLVM_PERF_JIT_MAGIC \
49 ((uint32_t)'J' << 24 | (uint32_t)'i' << 16 | (uint32_t)'T' << 8 | \
50 (uint32_t)'D')
51#define LLVM_PERF_JIT_VERSION 1
52
53
54
55#define JITDUMP_FLAGS_ARCH_TIMESTAMP (1ULL << 0)
56
57struct LLVMPerfJitHeader;
58
60public:
61 PerfJITEventListener();
62 ~PerfJITEventListener() {
63
64
65
66 std::lock_guardsys::Mutex Guard(Mutex);
67 if (MarkerAddr)
68 CloseMarker();
69 }
70
71 void notifyObjectLoaded(ObjectKey K, const ObjectFile &Obj,
72 const RuntimeDyld::LoadedObjectInfo &L) override;
73 void notifyFreeingObject(ObjectKey K) override;
74
75private:
76 bool InitDebuggingDir();
77 bool OpenMarker();
78 void CloseMarker();
79 static bool FillMachine(LLVMPerfJitHeader &hdr);
80
81 void NotifyCode(Expectedllvm::StringRef &Symbol, uint64_t CodeAddr,
83 void NotifyDebug(uint64_t CodeAddr, DILineInfoTable Lines);
84
85
87
88
89 std::string JitPath;
90
91
92 int DumpFd = -1;
93
94
95 std::unique_ptr<raw_fd_ostream> Dumpstream;
96
97
99
100
101 void *MarkerAddr = NULL;
102
103
104 bool SuccessfullyInitialized = false;
105
106
107 uint64_t CodeGeneration = 1;
108};
109
110
111
112enum LLVMPerfJitRecordType {
114 JIT_CODE_MOVE = 1,
118
120};
121
122struct LLVMPerfJitHeader {
123 uint32_t Magic;
124 uint32_t Version;
125 uint32_t TotalSize;
126 uint32_t ElfMach;
127 uint32_t Pad1;
128 uint32_t Pid;
129 uint64_t Timestamp;
130 uint64_t Flags;
131};
132
133
134struct LLVMPerfJitRecordPrefix {
135 uint32_t Id;
136 uint32_t TotalSize;
137 uint64_t Timestamp;
138};
139
140struct LLVMPerfJitRecordCodeLoad {
141 LLVMPerfJitRecordPrefix Prefix;
142
143 uint32_t Pid;
144 uint32_t Tid;
145 uint64_t Vma;
146 uint64_t CodeAddr;
147 uint64_t CodeSize;
148 uint64_t CodeIndex;
149};
150
151struct LLVMPerfJitDebugEntry {
152 uint64_t Addr;
153 int Lineno;
154 int Discrim;
155
156};
157
158struct LLVMPerfJitRecordDebugInfo {
159 LLVMPerfJitRecordPrefix Prefix;
160
161 uint64_t CodeAddr;
162 uint64_t NrEntry;
163
164};
165
166static inline uint64_t timespec_to_ns(const struct timespec *ts) {
167 const uint64_t NanoSecPerSec = 1000000000;
168 return ((uint64_t)ts->tv_sec * NanoSecPerSec) + ts->tv_nsec;
169}
170
171static inline uint64_t perf_get_timestamp(void) {
172 struct timespec ts;
173 int ret;
174
175 ret = clock_gettime(CLOCK_MONOTONIC, &ts);
176 if (ret)
177 return 0;
178
179 return timespec_to_ns(&ts);
180}
181
182PerfJITEventListener::PerfJITEventListener()
183 : Pid(sys::Process::getProcessId()) {
184
185 if (!perf_get_timestamp()) {
186 errs() << "kernel does not support CLOCK_MONOTONIC\n";
187 return;
188 }
189
190 if (!InitDebuggingDir()) {
191 errs() << "could not initialize debugging directory\n";
192 return;
193 }
194
197 FilenameBuf << JitPath << "/jit-" << Pid << ".dump";
198
199
200
204 errs() << "could not open JIT dump file " << Filename << ": "
205 << EC.message() << "\n";
206 return;
207 }
208
209 Dumpstream = std::make_unique<raw_fd_ostream>(DumpFd, true);
210
211 LLVMPerfJitHeader Header = {0, 0, 0, 0, 0, 0, 0, 0};
212 if (!FillMachine(Header))
213 return;
214
215
216 if (!OpenMarker())
217 return;
218
219
222 Header.TotalSize = sizeof(Header);
223 Header.Pid = Pid;
224 Header.Timestamp = perf_get_timestamp();
225 Dumpstream->write(reinterpret_cast<const char *>(&Header), sizeof(Header));
226
227
228 if (!Dumpstream->has_error())
229 SuccessfullyInitialized = true;
230}
231
232void PerfJITEventListener::notifyObjectLoaded(
235
236 if (!SuccessfullyInitialized)
237 return;
238
239 OwningBinary DebugObjOwner = L.getObjectForDebug(Obj);
241
242
244
245
246 for (const std::pair<SymbolRef, uint64_t> &P : computeSymbolSizes(DebugObj)) {
247 SymbolRef Sym = P.first;
248 std::string SourceFileName;
249
250 ExpectedSymbolRef::Type SymTypeOrErr = Sym.getType();
251 if (!SymTypeOrErr) {
252
254 continue;
255 }
258 continue;
259
260 Expected Name = Sym.getName();
261 if (!Name) {
263 continue;
264 }
265
266 Expected<uint64_t> AddrOrErr = Sym.getAddress();
267 if (!AddrOrErr) {
269 continue;
270 }
272 object::SectionedAddress Address;
273 Address.Address = *AddrOrErr;
274
276 if (auto SectOrErr = Sym.getSection())
277 if (*SectOrErr != Obj.section_end())
278 SectionIndex = SectOrErr.get()->getIndex();
279
280
281
283 {*AddrOrErr, SectionIndex}, Size, FileLineInfoKind::AbsoluteFilePath);
284
285 NotifyDebug(*AddrOrErr, Lines);
286 NotifyCode(Name, *AddrOrErr, Size);
287 }
288
289
290 std::lock_guardsys::Mutex Guard(Mutex);
291
292 Dumpstream->flush();
293}
294
295void PerfJITEventListener::notifyFreeingObject(ObjectKey K) {
296
297
298}
299
300bool PerfJITEventListener::InitDebuggingDir() {
301 time_t Time;
302 struct tm LocalTime;
303 char TimeBuffer[sizeof("YYYYMMDD")];
304 SmallString<64> Path;
305
306
307 if (const char *BaseDir = getenv("JITDUMPDIR"))
308 Path.append(BaseDir);
311
312
313 Path += "/.debug/jit/";
315 errs() << "could not create jit cache directory " << Path << ": "
316 << EC.message() << "\n";
317 return false;
318 }
319
320
321 time(&Time);
322 localtime_r(&Time, &LocalTime);
323 strftime(TimeBuffer, sizeof(TimeBuffer), "%Y%m%d", &LocalTime);
325 Path += TimeBuffer;
326
327 SmallString<128> UniqueDebugDir;
328
331 errs() << "could not create unique jit cache directory " << UniqueDebugDir
332 << ": " << EC.message() << "\n";
333 return false;
334 }
335
336 JitPath = std::string(UniqueDebugDir.str());
337
338 return true;
339}
340
341bool PerfJITEventListener::OpenMarker() {
342
343
344
345
346
347
348
349
351 PROT_READ | PROT_EXEC, MAP_PRIVATE, DumpFd, 0);
352
353 if (MarkerAddr == MAP_FAILED) {
354 errs() << "could not mmap JIT marker\n";
355 return false;
356 }
357 return true;
358}
359
360void PerfJITEventListener::CloseMarker() {
361 if (!MarkerAddr)
362 return;
363
365 MarkerAddr = nullptr;
366}
367
368bool PerfJITEventListener::FillMachine(LLVMPerfJitHeader &hdr) {
369 char id[16];
370 struct {
371 uint16_t e_type;
372 uint16_t e_machine;
374
375 size_t RequiredMemory = sizeof(id) + sizeof(info);
376
377 ErrorOr<std::unique_ptr> MB =
379 RequiredMemory,
380 0);
381
382
383
384
385
386
387
388 if (auto EC = MB.getError()) {
389 errs() << "could not open /proc/self/exe: " << EC.message() << "\n";
390 return false;
391 }
392
393 memcpy(&id, (*MB)->getBufferStart(), sizeof(id));
394 memcpy(&info, (*MB)->getBufferStart() + sizeof(id), sizeof(info));
395
396
397 if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') {
398 errs() << "invalid elf signature\n";
399 return false;
400 }
401
402 hdr.ElfMach = info.e_machine;
403
404 return true;
405}
406
407void PerfJITEventListener::NotifyCode(Expectedllvm::StringRef &Symbol,
408 uint64_t CodeAddr, uint64_t CodeSize) {
409 assert(SuccessfullyInitialized);
410
411
413 return;
414
415 LLVMPerfJitRecordCodeLoad rec;
417 rec.Prefix.TotalSize = sizeof(rec) +
418 Symbol->size() + 1 +
420 rec.Prefix.Timestamp = perf_get_timestamp();
421
423 rec.Vma = CodeAddr;
424 rec.CodeAddr = CodeAddr;
425 rec.Pid = Pid;
427
428
429 std::lock_guardsys::Mutex Guard(Mutex);
430
431 rec.CodeIndex = CodeGeneration++;
432
433 Dumpstream->write(reinterpret_cast<const char *>(&rec), sizeof(rec));
434 Dumpstream->write(Symbol->data(), Symbol->size() + 1);
435 Dumpstream->write(reinterpret_cast<const char *>(CodeAddr), CodeSize);
436}
437
438void PerfJITEventListener::NotifyDebug(uint64_t CodeAddr,
440 assert(SuccessfullyInitialized);
441
442
443 if (Lines.empty())
444 return;
445
446 LLVMPerfJitRecordDebugInfo rec;
448 rec.Prefix.TotalSize = sizeof(rec);
449 rec.Prefix.Timestamp = perf_get_timestamp();
450 rec.CodeAddr = CodeAddr;
451 rec.NrEntry = Lines.size();
452
453
457 DILineInfo &line = It->second;
458 rec.Prefix.TotalSize += sizeof(LLVMPerfJitDebugEntry);
459 rec.Prefix.TotalSize += line.FileName.size() + 1;
460 }
461
462
463
464
465
466
467
468
469
470
471 std::lock_guardsys::Mutex Guard(Mutex);
472
473 Dumpstream->write(reinterpret_cast<const char *>(&rec), sizeof(rec));
474
476 LLVMPerfJitDebugEntry LineInfo;
477 DILineInfo &Line = It->second;
478
479 LineInfo.Addr = It->first;
480
481
482
483 LineInfo.Addr += 0x40;
484 LineInfo.Lineno = Line.Line;
485 LineInfo.Discrim = Line.Discriminator;
486
487 Dumpstream->write(reinterpret_cast<const char *>(&LineInfo),
488 sizeof(LineInfo));
489 Dumpstream->write(Line.FileName.c_str(), Line.FileName.size() + 1);
490 }
491}
492
493}
494
495namespace llvm {
497
498
499 static PerfJITEventListener PerfListener;
500 return &PerfListener;
501}
502
503}
504
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define JIT_LANG
Definition PerfJITEventListener.cpp:47
#define LLVM_PERF_JIT_MAGIC
Definition PerfJITEventListener.cpp:48
#define LLVM_PERF_JIT_VERSION
Definition PerfJITEventListener.cpp:51
Provides a library for accessing information about this process and other processes on the operating ...
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
std::error_code getError() const
Error takeError()
Take ownership of the stored error.
JITEventListener - Abstract interface for use by the JIT to notify clients about significant events d...
static JITEventListener * createPerfJITEventListener()
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Map a subrange of the specified file as a MemoryBuffer.
Information about the loaded object.
StringRef str() const
Explicit conversion to StringRef.
This class is the base class for all object file types.
Expected< SymbolRef::Type > getType() const
Expected< StringRef > getName() const
Expected< uint64_t > getAddress() const
Returns the symbol virtual address (i.e.
Expected< section_iterator > getSection() const
Get section this symbol is defined in reference to.
A raw_ostream that writes to an std::string.
A collection of legacy interfaces for querying information about the current executing process.
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
LLVM_C_ABI LLVMJITEventListenerRef LLVMCreatePerfJITEventListener(void)
struct LLVMOpaqueJITEventListener * LLVMJITEventListenerRef
LLVM_ABI std::vector< std::pair< SymbolRef, uint64_t > > computeSymbolSizes(const ObjectFile &O)
@ JIT_CODE_UNWINDING_INFO
std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD, CreationDisposition Disp, OpenFlags Flags, unsigned Mode=0666)
Opens the file with the given name in a write-only or read-write mode, returning its open file descri...
@ CD_CreateNew
CD_CreateNew - When opening a file:
std::error_code openFileForWrite(const Twine &Name, int &ResultFD, CreationDisposition Disp=CD_CreateAlways, OpenFlags Flags=OF_None, unsigned Mode=0666)
Opens the file with the given name in a write-only or read-write mode, returning its open file descri...
LLVM_ABI std::error_code create_directories(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create all the non-existent directories in path.
LLVM_ABI std::error_code createUniqueDirectory(const Twine &Prefix, SmallVectorImpl< char > &ResultPath)
LLVM_ABI bool home_directory(SmallVectorImpl< char > &result)
Get the user's home directory.
SmartMutex< false > Mutex
Mutex - A standard, always enforced mutex.
This is an optimization pass for GlobalISel generic memory operations.
SmallVector< std::pair< uint64_t, DILineInfo >, 16 > DILineInfoTable
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
LLVM_ABI uint64_t get_threadid()
Return the current thread id, as used in various OS system calls.
LLVMAttributeRef wrap(Attribute Attr)
void consumeError(Error Err)
Consume a Error without doing anything.
static const uint64_t UndefSection