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 }

271 uint64_t Size = P.second;

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