1//===- GsymReader.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/DebugInfo/GSYM/GsymReader.h"
10
11#include <assert.h>
12#include <inttypes.h>
13#include <stdio.h>
14#include <stdlib.h>
15
16#include "llvm/DebugInfo/GSYM/GsymReaderV1.h"
17#include "llvm/DebugInfo/GSYM/GsymReaderV2.h"
18#include "llvm/DebugInfo/GSYM/Header.h"
19#include "llvm/DebugInfo/GSYM/HeaderV2.h"
20#include "llvm/DebugInfo/GSYM/InlineInfo.h"
21#include "llvm/DebugInfo/GSYM/LineTable.h"
22#include "llvm/Support/MemoryBuffer.h"
23
24using namespace llvm;
25using namespace gsym;
26
27GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer,
28 llvm::endianness Endian)
29 : MemBuffer(std::move(Buffer)), Endian(Endian),
30 AddrInfoOffsetsData(StringRef(), true), FileEntryData(StringRef(), true) {
31}
32
33/// Check magic bytes, determine endianness, and return the GSYM version and
34/// endianness. If magic bytes are invalid, return error.
35static Expected<std::pair<uint16_t, llvm::endianness>>
36checkMagicAndDetectVersionEndian(StringRef Bytes) {
37 if (Bytes.size() < 6)
38 return createStringError(EC: std::errc::invalid_argument,
39 Fmt: "data too small to be a GSYM file");
40 // Detect host endian
41 const auto HostEndian = llvm::endianness::native;
42 const bool IsHostLittleEndian = (HostEndian == llvm::endianness::little);
43 // Read magic bytes using host endian
44 GsymDataExtractor Data(Bytes, IsHostLittleEndian);
45 uint64_t Offset = 0;
46 uint32_t Magic = Data.getU32(offset_ptr: &Offset);
47 llvm::endianness FileEndian;
48 // If magic bytes looks alright, the host and the file have the same
49 // endianness, vice versa.
50 if (Magic == GSYM_MAGIC) {
51 FileEndian = HostEndian;
52 } else if (Magic == GSYM_CIGAM) {
53 FileEndian =
54 IsHostLittleEndian ? llvm::endianness::big : llvm::endianness::little;
55 // Re-create GsymDataExtractor with correct endianness to read version.
56 Data = GsymDataExtractor(Bytes, !IsHostLittleEndian);
57 } else {
58 return createStringError(EC: std::errc::invalid_argument,
59 Fmt: "not a GSYM file (bad magic)");
60 }
61 // Read version using the correct endian
62 uint16_t Version = Data.getU16(offset_ptr: &Offset);
63 return std::make_pair(x&: Version, y&: FileEndian);
64}
65
66llvm::Expected<std::unique_ptr<GsymReader>>
67GsymReader::openFile(StringRef Filename) {
68 // Open the input file and return an appropriate error if needed.
69 ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
70 MemoryBuffer::getFileOrSTDIN(Filename);
71 auto Err = BuffOrErr.getError();
72 if (Err)
73 return llvm::errorCodeToError(EC: Err);
74 auto &Buf = BuffOrErr.get();
75 Buf->randomAccessIfMmap();
76 return create(MemBuffer&: Buf);
77}
78
79llvm::Expected<std::unique_ptr<GsymReader>>
80GsymReader::copyBuffer(StringRef Bytes) {
81 auto MemBuffer = MemoryBuffer::getMemBufferCopy(InputData: Bytes, BufferName: "GSYM bytes");
82 return create(MemBuffer);
83}
84
85llvm::Expected<std::unique_ptr<GsymReader>>
86GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
87 if (!MemBuffer)
88 return createStringError(EC: std::errc::invalid_argument,
89 Fmt: "invalid memory buffer");
90 Expected<std::pair<uint16_t, llvm::endianness>> VersionEndianOrErr =
91 checkMagicAndDetectVersionEndian(Bytes: MemBuffer->getBuffer());
92 if (!VersionEndianOrErr)
93 return VersionEndianOrErr.takeError();
94 uint16_t Version;
95 llvm::endianness Endian;
96 std::tie(args&: Version, args&: Endian) = *VersionEndianOrErr;
97 std::unique_ptr<GsymReader> GR;
98 switch (Version) {
99 case Header::getVersion():
100 GR.reset(p: new GsymReaderV1(std::move(MemBuffer), Endian));
101 break;
102 case HeaderV2::getVersion():
103 GR.reset(p: new GsymReaderV2(std::move(MemBuffer), Endian));
104 break;
105 default:
106 return createStringError(EC: std::errc::invalid_argument,
107 Fmt: "unsupported GSYM version %u", Vals: Version);
108 }
109 if (auto Err = GR->parse())
110 return std::move(Err);
111 return std::move(GR);
112}
113
114llvm::Error GsymReader::parse() {
115 // Step 1: Parse the version-specific header and populate GlobalDataSections.
116 if (auto Err = parseHeaderAndGlobalDataEntries())
117 return Err;
118
119 // Step 2: Validate that all required sections are present and consistent.
120 for (auto Type :
121 {GlobalInfoType::AddrOffsets, GlobalInfoType::AddrInfoOffsets,
122 GlobalInfoType::StringTable, GlobalInfoType::FileTable,
123 GlobalInfoType::FunctionInfo})
124 if (!GlobalDataSections.count(x: Type))
125 return createStringError(
126 EC: std::errc::invalid_argument, Fmt: "missing required section type %s (%u)",
127 Vals: getNameForGlobalInfoType(Type).data(), Vals: static_cast<uint32_t>(Type));
128
129 if (GlobalDataSections[GlobalInfoType::AddrOffsets].FileSize !=
130 static_cast<uint64_t>(getNumAddresses()) * getAddressOffsetSize())
131 return createStringError(EC: std::errc::invalid_argument,
132 Fmt: "AddrOffsets section size mismatch");
133
134 if (GlobalDataSections[GlobalInfoType::AddrInfoOffsets].FileSize !=
135 static_cast<uint64_t>(getNumAddresses()) * getAddressInfoOffsetSize())
136 return createStringError(EC: std::errc::invalid_argument,
137 Fmt: "AddrInfoOffsets section size mismatch");
138
139 // Step 3: Parse each global data section.
140 llvm::Expected<StringRef> Bytes =
141 getRequiredGlobalDataBytes(Type: GlobalInfoType::AddrOffsets);
142 if (!Bytes)
143 return Bytes.takeError();
144 if (auto Err = parseAddrOffsets(Bytes: *Bytes))
145 return Err;
146
147 Bytes = getRequiredGlobalDataBytes(Type: GlobalInfoType::AddrInfoOffsets);
148 if (!Bytes)
149 return Bytes.takeError();
150 if (auto Err = setAddrInfoOffsetsData(*Bytes))
151 return Err;
152
153 Bytes = getRequiredGlobalDataBytes(Type: GlobalInfoType::StringTable);
154 if (!Bytes)
155 return Bytes.takeError();
156 if (auto Err = setStringTableData(*Bytes))
157 return Err;
158
159 Bytes = getRequiredGlobalDataBytes(Type: GlobalInfoType::FileTable);
160 if (!Bytes)
161 return Bytes.takeError();
162 if (auto Err = setFileTableData(*Bytes))
163 return Err;
164
165 return Error::success();
166}
167
168llvm::Error GsymReader::parseGlobalDataEntries(uint64_t Offset) {
169 if (getVersion() < HeaderV2::getVersion())
170 return createStringError(EC: std::errc::invalid_argument,
171 Fmt: "GlobalData section not supported in GSYM V1");
172
173 const StringRef Buf = MemBuffer->getBuffer();
174 const uint64_t BufSize = Buf.size();
175 GsymDataExtractor Data(Buf, isLittleEndian());
176 while (Offset + sizeof(GlobalData) <= BufSize) {
177 auto GDOrErr = GlobalData::decode(GsymData&: Data, Offset);
178 if (!GDOrErr)
179 return GDOrErr.takeError();
180 const GlobalData &GD = *GDOrErr;
181
182 if (GD.Type == GlobalInfoType::EndOfList)
183 return Error::success();
184
185 if (GD.FileSize == 0)
186 return createStringError(EC: std::errc::invalid_argument,
187 Fmt: "GlobalData section type %u has zero size",
188 Vals: static_cast<uint32_t>(GD.Type));
189
190 if (GD.FileOffset + GD.FileSize > BufSize)
191 return createStringError(
192 EC: std::errc::invalid_argument,
193 Fmt: "GlobalData section type %u extends beyond "
194 "buffer (offset=%" PRIu64 ", size=%" PRIu64 ", bufsize=%" PRIu64 ")",
195 Vals: static_cast<uint32_t>(GD.Type), Vals: GD.FileOffset, Vals: GD.FileSize, Vals: BufSize);
196
197 GlobalDataSections[GD.Type] = GD;
198 }
199 return createStringError(EC: std::errc::invalid_argument,
200 Fmt: "GlobalData array not terminated by EndOfList");
201}
202
203llvm::Error GsymReader::parseAddrOffsets(StringRef Bytes) {
204 const uint8_t AddrOffSize = getAddressOffsetSize();
205 const uint32_t NumAddrs = getNumAddresses();
206 const size_t TotalBytes = NumAddrs * AddrOffSize;
207 if (Bytes.size() < TotalBytes)
208 return createStringError(EC: std::errc::invalid_argument,
209 Fmt: "failed to read address table");
210
211 // Parse the non-swap case
212 if (Endian == llvm::endianness::native) {
213 AddrOffsets = ArrayRef<uint8_t>(
214 reinterpret_cast<const uint8_t *>(Bytes.data()), TotalBytes);
215 return Error::success();
216 }
217
218 // Parse the swap case
219 GsymDataExtractor Data(Bytes, isLittleEndian());
220 uint64_t Offset = 0;
221 SwappedAddrOffsets.resize(new_size: TotalBytes);
222 switch (AddrOffSize) {
223 case 1:
224 if (!Data.getU8(offset_ptr: &Offset, dst: SwappedAddrOffsets.data(), count: NumAddrs))
225 return createStringError(EC: std::errc::invalid_argument,
226 Fmt: "failed to read address table");
227 break;
228 case 2:
229 if (!Data.getU16(offset_ptr: &Offset,
230 dst: reinterpret_cast<uint16_t *>(SwappedAddrOffsets.data()),
231 count: NumAddrs))
232 return createStringError(EC: std::errc::invalid_argument,
233 Fmt: "failed to read address table");
234 break;
235 case 4:
236 if (!Data.getU32(offset_ptr: &Offset,
237 dst: reinterpret_cast<uint32_t *>(SwappedAddrOffsets.data()),
238 count: NumAddrs))
239 return createStringError(EC: std::errc::invalid_argument,
240 Fmt: "failed to read address table");
241 break;
242 case 8:
243 if (!Data.getU64(offset_ptr: &Offset,
244 dst: reinterpret_cast<uint64_t *>(SwappedAddrOffsets.data()),
245 count: NumAddrs))
246 return createStringError(EC: std::errc::invalid_argument,
247 Fmt: "failed to read address table");
248 break;
249 }
250 AddrOffsets = ArrayRef<uint8_t>(SwappedAddrOffsets);
251 return Error::success();
252}
253
254llvm::Error GsymReader::setAddrInfoOffsetsData(StringRef Bytes) {
255 AddrInfoOffsetsData = GsymDataExtractor(Bytes, isLittleEndian());
256 return Error::success();
257}
258
259llvm::Error GsymReader::setStringTableData(StringRef Bytes) {
260 StrTab.Data = Bytes;
261 return Error::success();
262}
263
264llvm::Error GsymReader::setFileTableData(StringRef Bytes) {
265 const uint8_t StrpSize = getStringOffsetSize();
266 GsymDataExtractor Data(Bytes, isLittleEndian(), StrpSize);
267 uint64_t Offset = 0;
268 uint32_t NumFiles = Data.getU32(offset_ptr: &Offset);
269 uint64_t EntriesSize =
270 static_cast<uint64_t>(NumFiles) * FileEntry::getEncodedSize(StringOffsetSize: StrpSize);
271 if (Bytes.size() < Offset + EntriesSize)
272 return createStringError(EC: std::errc::invalid_argument,
273 Fmt: "FileTable section too small for %u files",
274 Vals: NumFiles);
275 FileEntryData = GsymDataExtractor(Data, Offset, EntriesSize);
276 return Error::success();
277}
278
279std::optional<GlobalData> GsymReader::getGlobalData(GlobalInfoType Type) const {
280 auto It = GlobalDataSections.find(x: Type);
281 if (It == GlobalDataSections.end())
282 return std::nullopt;
283 return It->second;
284}
285
286llvm::Expected<StringRef>
287GsymReader::getRequiredGlobalDataBytes(GlobalInfoType Type) const {
288 if (auto Data = getOptionalGlobalDataBytes(Type))
289 return *Data;
290 const char *TypeName = getNameForGlobalInfoType(Type).data();
291 std::optional<GlobalData> GD = getGlobalData(Type);
292 // We have a GlobalData entry but didn't get any bytes — the file may be
293 // truncated.
294 if (GD)
295 return createStringError(
296 EC: std::errc::invalid_argument,
297 Fmt: "missing bytes for %s, GSYM file might be truncated", Vals: TypeName);
298 return createStringError(EC: std::errc::invalid_argument,
299 Fmt: "missing required section type %s", Vals: TypeName);
300}
301
302std::optional<StringRef>
303GsymReader::getOptionalGlobalDataBytes(GlobalInfoType Type) const {
304 std::optional<GlobalData> GD = getGlobalData(Type);
305 if (!GD)
306 return std::nullopt;
307 StringRef Buf = MemBuffer->getBuffer();
308 if (GD->FileSize == 0 || GD->FileOffset + GD->FileSize > Buf.size())
309 return std::nullopt;
310 return Buf.substr(Start: GD->FileOffset, N: GD->FileSize);
311}
312
313std::optional<uint64_t> GsymReader::getAddress(size_t Index) const {
314 switch (getAddressOffsetSize()) {
315 case 1: return addressForIndex<uint8_t>(Index);
316 case 2: return addressForIndex<uint16_t>(Index);
317 case 4: return addressForIndex<uint32_t>(Index);
318 case 8: return addressForIndex<uint64_t>(Index);
319 default:
320 llvm_unreachable("unsupported address offset size");
321 }
322 return std::nullopt;
323}
324
325std::optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
326 if (Index >= getNumAddresses())
327 return std::nullopt;
328 const uint8_t AddrInfoOffsetSize = getAddressInfoOffsetSize();
329 uint64_t Offset = Index * AddrInfoOffsetSize;
330 uint64_t AddrInfoOffset =
331 AddrInfoOffsetsData.getUnsigned(offset_ptr: &Offset, byte_size: AddrInfoOffsetSize);
332 // V1 stores absolute file offsets in AddrInfoOffsets, so no base offset is
333 // needed. V2+ stores offsets relative to the FunctionInfo section start.
334 if (getVersion() != Header::getVersion())
335 AddrInfoOffset +=
336 GlobalDataSections.at(k: GlobalInfoType::FunctionInfo).FileOffset;
337 return AddrInfoOffset;
338}
339
340Expected<uint64_t> GsymReader::getAddressIndex(const uint64_t Addr) const {
341 const uint64_t BaseAddr = getBaseAddress();
342 if (Addr >= BaseAddr) {
343 const uint64_t AddrOffset = Addr - BaseAddr;
344 std::optional<uint64_t> AddrOffsetIndex;
345 switch (getAddressOffsetSize()) {
346 case 1:
347 AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
348 break;
349 case 2:
350 AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);
351 break;
352 case 4:
353 AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);
354 break;
355 case 8:
356 AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);
357 break;
358 default:
359 return createStringError(EC: std::errc::invalid_argument,
360 Fmt: "unsupported address offset size %u",
361 Vals: getAddressOffsetSize());
362 }
363 if (AddrOffsetIndex)
364 return *AddrOffsetIndex;
365 }
366 return createStringError(EC: std::errc::invalid_argument,
367 Fmt: "address 0x%" PRIx64 " is not in GSYM", Vals: Addr);
368}
369
370llvm::Expected<GsymDataExtractor>
371GsymReader::getFunctionInfoDataForAddress(uint64_t Addr,
372 uint64_t &FuncStartAddr) const {
373 Expected<uint64_t> ExpectedAddrIdx = getAddressIndex(Addr);
374 if (!ExpectedAddrIdx)
375 return ExpectedAddrIdx.takeError();
376 const uint64_t FirstAddrIdx = *ExpectedAddrIdx;
377 // The AddrIdx is the first index of the function info entries that match
378 // \a Addr. We need to iterate over all function info objects that start with
379 // the same address until we find a range that contains \a Addr.
380 std::optional<uint64_t> FirstFuncStartAddr;
381 const size_t NumAddresses = getNumAddresses();
382 for (uint64_t AddrIdx = FirstAddrIdx; AddrIdx < NumAddresses; ++AddrIdx) {
383 auto ExpextedData = getFunctionInfoDataAtIndex(AddrIdx, FuncStartAddr);
384 // If there was an error, return the error.
385 if (!ExpextedData)
386 return ExpextedData;
387
388 // Remember the first function start address if it hasn't already been set.
389 // If it is already valid, check to see if it matches the first function
390 // start address and only continue if it matches.
391 if (FirstFuncStartAddr.has_value()) {
392 if (*FirstFuncStartAddr != FuncStartAddr)
393 break; // Done with consecutive function entries with same address.
394 } else {
395 FirstFuncStartAddr = FuncStartAddr;
396 }
397 // Make sure the current function address ranges contains \a Addr.
398 // Some symbols on Darwin don't have valid sizes, so if we run into a
399 // symbol with zero size, then we have found a match for our address.
400
401 // The first thing the encoding of a FunctionInfo object is the function
402 // size.
403 uint64_t Offset = 0;
404 uint32_t FuncSize = ExpextedData->getU32(offset_ptr: &Offset);
405 if (FuncSize == 0 ||
406 AddressRange(FuncStartAddr, FuncStartAddr + FuncSize).contains(Addr))
407 return ExpextedData;
408 }
409 return createStringError(EC: std::errc::invalid_argument,
410 Fmt: "address 0x%" PRIx64 " is not in GSYM", Vals: Addr);
411}
412
413llvm::Expected<GsymDataExtractor>
414GsymReader::getFunctionInfoDataAtIndex(uint64_t AddrIdx,
415 uint64_t &FuncStartAddr) const {
416 const std::optional<uint64_t> AddrInfoOffset = getAddressInfoOffset(Index: AddrIdx);
417 if (AddrInfoOffset == std::nullopt)
418 return createStringError(EC: std::errc::invalid_argument,
419 Fmt: "invalid address index %" PRIu64, Vals: AddrIdx);
420 assert((Endian == endianness::big || Endian == endianness::little) &&
421 "Endian must be either big or little");
422 StringRef Bytes = MemBuffer->getBuffer().substr(Start: *AddrInfoOffset);
423 if (Bytes.empty())
424 return createStringError(EC: std::errc::invalid_argument,
425 Fmt: "invalid address info offset 0x%" PRIx64,
426 Vals: *AddrInfoOffset);
427 std::optional<uint64_t> OptFuncStartAddr = getAddress(Index: AddrIdx);
428 if (!OptFuncStartAddr)
429 return createStringError(EC: std::errc::invalid_argument,
430 Fmt: "failed to extract address[%" PRIu64 "]", Vals: AddrIdx);
431 FuncStartAddr = *OptFuncStartAddr;
432 GsymDataExtractor Data(Bytes, isLittleEndian(), getStringOffsetSize());
433 return Data;
434}
435
436llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
437 uint64_t FuncStartAddr = 0;
438 if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
439 return FunctionInfo::decode(Data&: *ExpectedData, BaseAddr: FuncStartAddr);
440 else
441 return ExpectedData.takeError();
442}
443
444llvm::Expected<FunctionInfo>
445GsymReader::getFunctionInfoAtIndex(uint64_t Idx) const {
446 uint64_t FuncStartAddr = 0;
447 if (auto ExpectedData = getFunctionInfoDataAtIndex(AddrIdx: Idx, FuncStartAddr))
448 return FunctionInfo::decode(Data&: *ExpectedData, BaseAddr: FuncStartAddr);
449 else
450 return ExpectedData.takeError();
451}
452
453llvm::Expected<LookupResult> GsymReader::lookup(
454 uint64_t Addr,
455 std::optional<GsymDataExtractor> *MergedFunctionsData) const {
456 uint64_t FuncStartAddr = 0;
457 if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
458 return FunctionInfo::lookup(Data&: *ExpectedData, GR: *this, FuncAddr: FuncStartAddr, Addr,
459 MergedFuncsData: MergedFunctionsData);
460 else
461 return ExpectedData.takeError();
462}
463
464llvm::Expected<std::vector<LookupResult>>
465GsymReader::lookupAll(uint64_t Addr) const {
466 std::vector<LookupResult> Results;
467 std::optional<GsymDataExtractor> MergedFunctionsData;
468
469 // First perform a lookup to get the primary function info result.
470 auto MainResult = lookup(Addr, MergedFunctionsData: &MergedFunctionsData);
471 if (!MainResult)
472 return MainResult.takeError();
473
474 // Add the main result as the first entry.
475 Results.push_back(x: std::move(*MainResult));
476
477 // Now process any merged functions data that was found during the lookup.
478 if (MergedFunctionsData) {
479 // Get data extractors for each merged function.
480 auto ExpectedMergedFuncExtractors =
481 MergedFunctionsInfo::getFuncsDataExtractors(Data&: *MergedFunctionsData);
482 if (!ExpectedMergedFuncExtractors)
483 return ExpectedMergedFuncExtractors.takeError();
484
485 // Process each merged function data.
486 for (GsymDataExtractor &MergedData : *ExpectedMergedFuncExtractors) {
487 if (auto FI = FunctionInfo::lookup(Data&: MergedData, GR: *this,
488 FuncAddr: MainResult->FuncRange.start(), Addr)) {
489 Results.push_back(x: std::move(*FI));
490 } else {
491 return FI.takeError();
492 }
493 }
494 }
495
496 return Results;
497}
498
499void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI,
500 uint32_t Indent) {
501 OS.indent(NumSpaces: Indent);
502 OS << FI.Range << " \"" << getString(Offset: FI.Name) << "\"\n";
503 if (FI.OptLineTable)
504 dump(OS, LT: *FI.OptLineTable, Indent);
505 if (FI.Inline)
506 dump(OS, II: *FI.Inline, Indent);
507
508 if (FI.CallSites)
509 dump(OS, CSIC: *FI.CallSites, Indent);
510
511 if (FI.MergedFunctions) {
512 assert(Indent == 0 && "MergedFunctionsInfo should only exist at top level");
513 dump(OS, MFI: *FI.MergedFunctions);
514 }
515}
516
517void GsymReader::dump(raw_ostream &OS, const MergedFunctionsInfo &MFI) {
518 for (uint32_t inx = 0; inx < MFI.MergedFunctions.size(); inx++) {
519 OS << "++ Merged FunctionInfos[" << inx << "]:\n";
520 dump(OS, FI: MFI.MergedFunctions[inx], Indent: 4);
521 }
522}
523
524void GsymReader::dump(raw_ostream &OS, const CallSiteInfo &CSI) {
525 OS << HEX16(CSI.ReturnOffset);
526
527 std::string Flags;
528 auto addFlag = [&](const char *Flag) {
529 if (!Flags.empty())
530 Flags += " | ";
531 Flags += Flag;
532 };
533
534 if (CSI.Flags == CallSiteInfo::Flags::None)
535 Flags = "None";
536 else {
537 if (CSI.Flags & CallSiteInfo::Flags::InternalCall)
538 addFlag("InternalCall");
539
540 if (CSI.Flags & CallSiteInfo::Flags::ExternalCall)
541 addFlag("ExternalCall");
542 }
543 OS << " Flags[" << Flags << "]";
544
545 if (!CSI.MatchRegex.empty()) {
546 OS << " MatchRegex[";
547 for (uint32_t i = 0; i < CSI.MatchRegex.size(); ++i) {
548 if (i > 0)
549 OS << ";";
550 OS << getString(Offset: CSI.MatchRegex[i]);
551 }
552 OS << "]";
553 }
554}
555
556void GsymReader::dump(raw_ostream &OS, const CallSiteInfoCollection &CSIC,
557 uint32_t Indent) {
558 OS.indent(NumSpaces: Indent);
559 OS << "CallSites (by relative return offset):\n";
560 for (const auto &CS : CSIC.CallSites) {
561 OS.indent(NumSpaces: Indent);
562 OS << " ";
563 dump(OS, CSI: CS);
564 OS << "\n";
565 }
566}
567
568void GsymReader::dump(raw_ostream &OS, const LineTable &LT, uint32_t Indent) {
569 OS.indent(NumSpaces: Indent);
570 OS << "LineTable:\n";
571 for (auto &LE : LT) {
572 OS.indent(NumSpaces: Indent);
573 OS << " " << HEX64(LE.Addr) << ' ';
574 if (LE.File)
575 dump(OS, FE: getFile(Index: LE.File));
576 OS << ':' << LE.Line << '\n';
577 }
578}
579
580void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {
581 if (Indent == 0)
582 OS << "InlineInfo:\n";
583 else
584 OS.indent(NumSpaces: Indent);
585 OS << II.Ranges << ' ' << getString(Offset: II.Name);
586 if (II.CallFile != 0) {
587 if (auto File = getFile(Index: II.CallFile)) {
588 OS << " called from ";
589 dump(OS, FE: File);
590 OS << ':' << II.CallLine;
591 }
592 }
593 OS << '\n';
594 for (const auto &ChildII : II.Children)
595 dump(OS, II: ChildII, Indent: Indent + 2);
596}
597
598void GsymReader::dump(raw_ostream &OS, std::optional<FileEntry> FE) {
599 if (FE) {
600 // IF we have the file from index 0, then don't print anything
601 if (FE->Dir == 0 && FE->Base == 0)
602 return;
603 StringRef Dir = getString(Offset: FE->Dir);
604 StringRef Base = getString(Offset: FE->Base);
605 if (!Dir.empty()) {
606 OS << Dir;
607 if (Dir.contains(C: '\\') && !Dir.contains(C: '/'))
608 OS << '\\';
609 else
610 OS << '/';
611 }
612 if (!Base.empty()) {
613 OS << Base;
614 }
615 if (!Dir.empty() || !Base.empty())
616 return;
617 }
618 OS << "<invalid-file>";
619}
620