LLVM: lib/DebugInfo/GSYM/GsymCreator.cpp Source File (original) (raw)

1

2

3

4

5

6

7

15

16#include

17#include

18#include

19#include

20

21using namespace llvm;

22using namespace gsym;

23

28

40

42 std::lock_guardstd::mutex Guard(Mutex);

43 const auto NextIndex = Files.size();

44

45 auto R = FileEntryToIndex.insert(std::make_pair(FE, NextIndex));

46 if (R.second)

47 Files.emplace_back(FE);

48 return R.first->second;

49}

50

52

53

54

55 if (FileIdx == 0)

56 return 0;

57 const FileEntry SrcFE = SrcGC.Files[FileIdx];

58

60 SrcFE.Dir == 0

61 ? 0

62 : StrTab.add(SrcGC.StringOffsetMap.find(SrcFE.Dir)->second);

63 uint32_t Base = StrTab.add(SrcGC.StringOffsetMap.find(SrcFE.Base)->second);

65 return insertFileEntry(DstFE);

66}

67

69 std::optional<uint64_t> SegmentSize) const {

70 if (SegmentSize)

71 return saveSegments(Path, ByteOrder, *SegmentSize);

72 std::error_code EC;

74 if (EC)

78}

79

81 std::lock_guardstd::mutex Guard(Mutex);

82 if (Funcs.empty())

84 "no functions to encode");

85 if (!Finalized)

87 "GsymCreator wasn't finalized prior to encoding");

88

89 if (Funcs.size() > UINT32_MAX)

91 "too many FunctionInfos");

92

93 std::optional<uint64_t> BaseAddress = getBaseAddress();

94

95 if (!BaseAddress)

97 "invalid base address");

101 Hdr.AddrOffSize = getAddressOffsetSize();

105 Hdr.StrtabOffset = 0;

106 Hdr.StrtabSize = 0;

107 memset(Hdr.UUID, 0, sizeof(Hdr.UUID));

108 if (UUID.size() > sizeof(Hdr.UUID))

110 "invalid UUID size %u", (uint32_t)UUID.size());

111

112 if (UUID.size() > 0)

113 memcpy(Hdr.UUID, UUID.data(), UUID.size());

114

116 if (Err)

117 return Err;

118

119 const uint64_t MaxAddressOffset = getMaxAddressOffset();

120

122 for (const auto &FuncInfo : Funcs) {

124

125

126

127

128 assert(AddrOffset <= MaxAddressOffset);

129 (void)MaxAddressOffset;

131 case 1:

132 O.writeU8(static_cast<uint8_t>(AddrOffset));

133 break;

134 case 2:

135 O.writeU16(static_cast<uint16_t>(AddrOffset));

136 break;

137 case 4:

138 O.writeU32(static_cast<uint32_t>(AddrOffset));

139 break;

140 case 8:

141 O.writeU64(AddrOffset);

142 break;

143 }

144 }

145

146

147 O.alignTo(4);

148 const off_t AddrInfoOffsetsOffset = O.tell();

149 for (size_t i = 0, n = Funcs.size(); i < n; ++i)

150 O.writeU32(0);

151

152

153 O.alignTo(4);

154 assert(!Files.empty());

155 assert(Files[0].Dir == 0);

157 size_t NumFiles = Files.size();

158 if (NumFiles > UINT32_MAX)

159 return createStringError(std::errc::invalid_argument, "too many files");

160 O.writeU32(static_cast<uint32_t>(NumFiles));

161 for (auto File : Files) {

162 O.writeU32(File.Dir);

163 O.writeU32(File.Base);

164 }

165

166

167 const off_t StrtabOffset = O.tell();

168 StrTab.write(O.get_stream());

169 const off_t StrtabSize = O.tell() - StrtabOffset;

170 std::vector<uint32_t> AddrInfoOffsets;

171

172

173 for (const auto &FuncInfo : Funcs) {

175 AddrInfoOffsets.push_back(OffsetOrErr.get());

176 else

177 return OffsetOrErr.takeError();

178 }

179

182

183

185 for (auto AddrInfoOffset : AddrInfoOffsets) {

186 O.fixup32(AddrInfoOffset, AddrInfoOffsetsOffset + Offset);

188 }

190}

191

193

195 return Loader.loadYAML(YAMLFile);

196}

197

199

200 if (Funcs.size() < 2)

201 return;

202

203

205 std::vector TopLevelFuncs;

206

207

208 TopLevelFuncs.emplace_back(std::move(Funcs.front()));

209

210

211

212

213 for (size_t Idx = 1; Idx < Funcs.size(); ++Idx) {

214 FunctionInfo &TopFunc = TopLevelFuncs.back();

216 if (TopFunc.Range == MatchFunc.Range) {

217

220

221

222

223 else if (TopFunc.MergedFunctions->MergedFunctions.back() == MatchFunc)

224 continue;

226 std::move(MatchFunc));

227 } else

228

229 TopLevelFuncs.emplace_back(std::move(MatchFunc));

230 }

231

232 uint32_t mergedCount = Funcs.size() - TopLevelFuncs.size();

233

234 if (mergedCount != 0)

235 Out << "Have " << mergedCount

236 << " merged functions as children of other functions\n";

237

239}

240

242 std::lock_guardstd::mutex Guard(Mutex);

243 if (Finalized)

244 return createStringError(std::errc::invalid_argument, "already finalized");

245 Finalized = true;

246

247

248 StrTab.finalizeInOrder();

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271 const auto NumBefore = Funcs.size();

272

273

274

275

276 if (!IsSegment) {

277 if (NumBefore > 1) {

278

279

281 std::vector FinalizedFuncs;

282 FinalizedFuncs.reserve(Funcs.size());

283 FinalizedFuncs.emplace_back(std::move(Funcs.front()));

284 for (size_t Idx=1; Idx < NumBefore; ++Idx) {

287

288

289

290 const bool ranges_equal = Prev.Range == Curr.Range;

292

293 if (ranges_equal) {

294

295

296

297

298

299

300 if (!(Prev == Curr)) {

303 "Duplicate address ranges with different debug info.",

305 OS << "warning: same address range contains "

306 "different debug "

307 << "info. Removing:\n"

308 << Prev << "\nIn favor of this one:\n"

309 << Curr << "\n";

310 });

311

312

313

314

316 }

317 } else {

319

320 OS << "warning: function ranges overlap:\n"

321 << Prev << "\n"

322 << Curr << "\n";

323 });

324 FinalizedFuncs.emplace_back(std::move(Curr));

325 }

326 } else {

328

329

330

332 } else {

333 FinalizedFuncs.emplace_back(std::move(Curr));

334 }

335 }

336 }

337 std::swap(Funcs, FinalizedFuncs);

338 }

339

340

341

342

343

344 if (!Funcs.empty() && Funcs.back().Range.size() == 0 && ValidTextRanges) {

346 ValidTextRanges->getRangeThatContains(Funcs.back().Range.start())) {

347 Funcs.back().Range = {Funcs.back().Range.start(), Range->end()};

348 }

349 }

350 Out << "Pruned " << NumBefore - Funcs.size() << " functions, ended with "

351 << Funcs.size() << " total\n";

352 }

354}

355

357

358 if (StrOff == 0)

359 return 0;

360 return StrTab.add(SrcGC.StringOffsetMap.find(StrOff)->second);

361}

362

365 return 0;

366

367

369 std::lock_guardstd::mutex Guard(Mutex);

370 if (Copy) {

371

372

373

374

375

376

377 if (!StrTab.contains(CHStr))

379 CHStr.hash()};

380 }

381 const uint32_t StrOff = StrTab.add(CHStr);

382

383

384

385 StringOffsetMap.try_emplace(StrOff, CHStr);

386 return StrOff;

387}

388

390 auto I = StringOffsetMap.find(Offset);

391 assert(I != StringOffsetMap.end() &&

392 "GsymCreator::getString expects a valid offset as parameter.");

393 return I->second.val();

394}

395

397 std::lock_guardstd::mutex Guard(Mutex);

398 Funcs.emplace_back(std::move(FI));

399}

400

402 std::function<bool(FunctionInfo &)> const &Callback) {

403 std::lock_guardstd::mutex Guard(Mutex);

404 for (auto &FI : Funcs) {

405 if (!Callback(FI))

406 break;

407 }

408}

409

411 std::function<bool(const FunctionInfo &)> const &Callback) const {

412 std::lock_guardstd::mutex Guard(Mutex);

413 for (const auto &FI : Funcs) {

414 if (!Callback(FI))

415 break;

416 }

417}

418

420 std::lock_guardstd::mutex Guard(Mutex);

421 return Funcs.size();

422}

423

425 if (ValidTextRanges)

426 return ValidTextRanges->contains(Addr);

427 return true;

428}

429

430std::optional<uint64_t> GsymCreator::getFirstFunctionAddress() const {

431

432

433

434

435 if ((Finalized || IsSegment) && !Funcs.empty())

436 return std::optional<uint64_t>(Funcs.front().startAddress());

437 return std::nullopt;

438}

439

440std::optional<uint64_t> GsymCreator::getLastFunctionAddress() const {

441

442

443

444

445 if ((Finalized || IsSegment) && !Funcs.empty())

446 return std::optional<uint64_t>(Funcs.back().startAddress());

447 return std::nullopt;

448}

449

450std::optional<uint64_t> GsymCreator::getBaseAddress() const {

451 if (BaseAddress)

452 return BaseAddress;

453 return getFirstFunctionAddress();

454}

455

456uint64_t GsymCreator::getMaxAddressOffset() const {

457 switch (getAddressOffsetSize()) {

458 case 1: return UINT8_MAX;

459 case 2: return UINT16_MAX;

460 case 4: return UINT32_MAX;

462 }

464}

465

466uint8_t GsymCreator::getAddressOffsetSize() const {

467 const std::optional<uint64_t> BaseAddress = getBaseAddress();

468 const std::optional<uint64_t> LastFuncAddr = getLastFunctionAddress();

469 if (BaseAddress && LastFuncAddr) {

470 const uint64_t AddrDelta = *LastFuncAddr - *BaseAddress;

471 if (AddrDelta <= UINT8_MAX)

472 return 1;

473 else if (AddrDelta <= UINT16_MAX)

474 return 2;

475 else if (AddrDelta <= UINT32_MAX)

476 return 4;

477 return 8;

478 }

479 return 1;

480}

481

482uint64_t GsymCreator::calculateHeaderAndTableSize() const {

483 uint64_t Size = sizeof(Header);

484 const size_t NumFuncs = Funcs.size();

485

486 Size += NumFuncs * getAddressOffsetSize();

487

488 Size += NumFuncs * sizeof(uint32_t);

489

490 Size += Files.size() * sizeof(FileEntry);

491

492 Size += StrTab.getSize();

493

495}

496

497

498

499

501 II.Name = copyString(SrcGC, II.Name);

502 II.CallFile = copyFile(SrcGC, II.CallFile);

503 for (auto &ChildII: II.Children)

504 fixupInlineInfo(SrcGC, ChildII);

505}

506

507uint64_t GsymCreator::copyFunctionInfo(const GsymCreator &SrcGC, size_t FuncIdx) {

508

509

510

511 const FunctionInfo &SrcFI = SrcGC.Funcs[FuncIdx];

512

513 FunctionInfo DstFI;

515 DstFI.Name = copyString(SrcGC, SrcFI.Name);

516

518

520

521

522 LineTable &DstLT = DstFI.OptLineTable.value();

523 const size_t NumLines = DstLT.size();

524 for (size_t I=0; I<NumLines; ++I) {

525 LineEntry &LE = DstLT.get(I);

526 LE.File = copyFile(SrcGC, LE.File);

527 }

528 }

529

531

533

534 fixupInlineInfo(SrcGC, *DstFI.Inline);

535 }

536 std::lock_guardstd::mutex Guard(Mutex);

537 Funcs.emplace_back(DstFI);

538 return Funcs.back().cacheEncoding();

539}

540

541llvm::Error GsymCreator::saveSegments(StringRef Path,

543 uint64_t SegmentSize) const {

544 if (SegmentSize == 0)

546 "invalid segment size zero");

547

548 size_t FuncIdx = 0;

549 const size_t NumFuncs = Funcs.size();

550 while (FuncIdx < NumFuncs) {

551 llvm::Expected<std::unique_ptr> ExpectedGC =

553 if (ExpectedGC) {

555 if (!GC)

556 break;

557

558 OutputAggregator Out(nullptr);

560 if (Err)

561 return Err;

562 std::string SegmentedGsymPath;

563 raw_string_ostream SGP(SegmentedGsymPath);

564 std::optional<uint64_t> FirstFuncAddr = GC->getFirstFunctionAddress();

565 if (FirstFuncAddr) {

567 SGP.flush();

568 Err = GC->save(SegmentedGsymPath, ByteOrder, std::nullopt);

569 if (Err)

570 return Err;

571 }

572 } else {

574 }

575 }

577}

578

579llvm::Expected<std::unique_ptr>

581

582 if (FuncIdx >= Funcs.size())

583 return std::unique_ptr();

584

585 std::unique_ptr GC(new GsymCreator(true));

586

587

588 GC->setIsSegment();

589

590

591 if (BaseAddress)

592 GC->setBaseAddress(*BaseAddress);

593

594 GC->setUUID(UUID);

595 const size_t NumFuncs = Funcs.size();

596

597

598

599 uint64_t SegmentFuncInfosSize = 0;

600 for (; FuncIdx < NumFuncs; ++FuncIdx) {

601 const uint64_t HeaderAndTableSize = GC->calculateHeaderAndTableSize();

602 if (HeaderAndTableSize + SegmentFuncInfosSize >= SegmentSize) {

603 if (SegmentFuncInfosSize == 0)

605 "a segment size of %" PRIu64 " is to small to "

606 "fit any function infos, specify a larger value",

607 SegmentSize);

608

609 break;

610 }

611 SegmentFuncInfosSize += alignTo(GC->copyFunctionInfo(*this, FuncIdx), 4);

612 }

613 return std::move(GC);

614}

assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")

#define offsetof(TYPE, MEMBER)

ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High))

uint64_t IntrinsicInst * II

bool intersects(const AddressRange &R) const

bool contains(uint64_t Addr) const

A container which contains a StringRef plus a precomputed hash.

std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)

Subclass of Error for the sole purpose of identifying the success path in the type system.

Lightweight error class with error context and mandatory checking.

static ErrorSuccess success()

Create a success value.

Tagged union holding either a T or a Error.

Error takeError()

Take ownership of the stored error.

reference get()

Returns a reference to the stored T value.

StringRef - Represent a constant reference to a string, i.e.

constexpr bool empty() const

empty - Check if the string is empty.

Utility for building string tables with deduplicated suffixes.

LLVM_ABI size_t add(CachedHashStringRef S, uint8_t Priority=0)

Add a string to the builder.

LLVM_ABI llvm::Error loadYAML(StringRef YAMLFile)

This method reads the specified YAML file, parses its content, and updates the Funcs vector with call...

A simplified binary data writer class that doesn't require targets, target definitions,...

GsymCreator is used to emit GSYM data to a stand alone file or section within a file.

LLVM_ABI void addFunctionInfo(FunctionInfo &&FI)

Add a function info to this GSYM creator.

Definition GsymCreator.cpp:396

LLVM_ABI uint32_t insertString(StringRef S, bool Copy=true)

Insert a string into the GSYM string table.

Definition GsymCreator.cpp:363

LLVM_ABI llvm::Expected< std::unique_ptr< GsymCreator > > createSegment(uint64_t SegmentSize, size_t &FuncIdx) const

Create a segmented GSYM creator starting with function info index FuncIdx.

Definition GsymCreator.cpp:580

LLVM_ABI llvm::Error save(StringRef Path, llvm::endianness ByteOrder, std::optional< uint64_t > SegmentSize=std::nullopt) const

Save a GSYM file to a stand alone file.

Definition GsymCreator.cpp:68

LLVM_ABI void prepareMergedFunctions(OutputAggregator &Out)

Organize merged FunctionInfo's.

Definition GsymCreator.cpp:198

LLVM_ABI StringRef getString(uint32_t Offset)

Retrieve a string from the GSYM string table given its offset.

Definition GsymCreator.cpp:389

LLVM_ABI llvm::Error loadCallSitesFromYAML(StringRef YAMLFile)

Load call site information from a YAML file.

Definition GsymCreator.cpp:192

LLVM_ABI llvm::Error encode(FileWriter &O) const

Encode a GSYM into the file writer stream at the current position.

Definition GsymCreator.cpp:80

LLVM_ABI llvm::Error finalize(OutputAggregator &OS)

Finalize the data in the GSYM creator prior to saving the data out.

Definition GsymCreator.cpp:241

LLVM_ABI uint32_t insertFile(StringRef Path, sys::path::Style Style=sys::path::Style::native)

Insert a file into this GSYM creator.

Definition GsymCreator.cpp:29

LLVM_ABI size_t getNumFunctionInfos() const

Get the current number of FunctionInfo objects contained in this object.

Definition GsymCreator.cpp:419

LLVM_ABI bool IsValidTextAddress(uint64_t Addr) const

Check if an address is a valid code address.

Definition GsymCreator.cpp:424

LLVM_ABI void forEachFunctionInfo(std::function< bool(FunctionInfo &)> const &Callback)

Thread safe iteration over all function infos.

Definition GsymCreator.cpp:401

LLVM_ABI GsymCreator(bool Quiet=false)

Definition GsymCreator.cpp:24

LineEntry & get(size_t i)

void Report(StringRef s, std::function< void(raw_ostream &o)> detailCallback)

A raw_ostream that writes to a file descriptor.

This class implements an extremely fast bulk output stream that can only output to a stream.

#define llvm_unreachable(msg)

Marks that the current location is not supposed to be reachable.

constexpr uint32_t GSYM_MAGIC

constexpr uint32_t GSYM_VERSION

LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)

Get parent path.

LLVM_ABI StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)

Get filename.

SmartMutex< false > Mutex

Mutex - A standard, always enforced mutex.

This is an optimization pass for GlobalISel generic memory operations.

void stable_sort(R &&Range)

Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)

Create formatted StringError object.

FormattedNumber format_hex(uint64_t N, unsigned Width, bool Upper=false)

format_hex - Output N as a fixed width hexadecimal.

uint64_t alignTo(uint64_t Size, Align A)

Returns a multiple of A needed to store Size bytes.

LLVM_ABI Error errorCodeToError(std::error_code EC)

Helper for converting an std::error_code to a Error.

void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)

Implement std::swap in terms of BitVector swap.

Files in GSYM are contained in FileEntry structs where we split the directory and basename into two d...

uint32_t Dir

Offsets in the string table.

Function information in GSYM files encodes information for one contiguous address range.

std::optional< InlineInfo > Inline

std::optional< MergedFunctionsInfo > MergedFunctions

bool hasRichInfo() const

Query if a FunctionInfo has rich debug info.

uint32_t Name

String table offset in the string table.

std::optional< LineTable > OptLineTable

Inline information stores the name of the inline function along with an array of address ranges.