LLVM 24.0.0git
GsymReader.cpp
Go to the documentation of this file.
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
10
11#include <assert.h>
12#include <inttypes.h>
13#include <stdio.h>
14#include <stdlib.h>
15
23#include "llvm/Support/JSON.h"
25
26using namespace llvm;
27using namespace gsym;
28
29GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer,
31 : MemBuffer(std::move(Buffer)), Endian(Endian),
33}
34
35/// Check magic bytes, determine endianness, and return the GSYM version and
36/// endianness. If magic bytes are invalid, return error.
39 if (Bytes.size() < 6)
40 return createStringError(std::errc::invalid_argument,
41 "data too small to be a GSYM file");
42 // Detect host endian
43 const auto HostEndian = llvm::endianness::native;
44 const bool IsHostLittleEndian = (HostEndian == llvm::endianness::little);
45 // Read magic bytes using host endian
46 GsymDataExtractor Data(Bytes, IsHostLittleEndian);
47 uint64_t Offset = 0;
48 uint32_t Magic = Data.getU32(&Offset);
49 llvm::endianness FileEndian;
50 // If magic bytes looks alright, the host and the file have the same
51 // endianness, vice versa.
52 if (Magic == GSYM_MAGIC) {
53 FileEndian = HostEndian;
54 } else if (Magic == GSYM_CIGAM) {
55 FileEndian =
57 // Re-create GsymDataExtractor with correct endianness to read version.
58 Data = GsymDataExtractor(Bytes, !IsHostLittleEndian);
59 } else {
60 return createStringError(std::errc::invalid_argument,
61 "not a GSYM file (bad magic)");
62 }
63 // Read version using the correct endian
64 uint16_t Version = Data.getU16(&Offset);
65 return std::make_pair(Version, FileEndian);
66}
67
68llvm::Expected<std::unique_ptr<GsymReader>>
70 // Open the input file and return an appropriate error if needed.
73 auto Err = BuffOrErr.getError();
74 if (Err)
75 return llvm::errorCodeToError(Err);
76 auto &Buf = BuffOrErr.get();
77 Buf->randomAccessIfMmap();
78 return create(Buf);
79}
80
83 auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
84 return create(MemBuffer);
85}
86
88GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
89 if (!MemBuffer)
90 return createStringError(std::errc::invalid_argument,
91 "invalid memory buffer");
94 if (!VersionEndianOrErr)
95 return VersionEndianOrErr.takeError();
98 std::tie(Version, Endian) = *VersionEndianOrErr;
99 std::unique_ptr<GsymReader> GR;
100 switch (Version) {
101 case Header::getVersion():
102 GR.reset(new GsymReaderV1(std::move(MemBuffer), Endian));
103 break;
105 GR.reset(new GsymReaderV2(std::move(MemBuffer), Endian));
106 break;
107 default:
108 return createStringError(std::errc::invalid_argument,
109 "unsupported GSYM version %u", Version);
110 }
111 if (auto Err = GR->parse())
112 return std::move(Err);
113 return std::move(GR);
114}
115
117 // Step 1: Parse the version-specific header and populate GlobalDataSections.
118 if (auto Err = parseHeaderAndGlobalDataEntries())
119 return Err;
120
121 // Step 2: Validate that all required sections are present and consistent.
122 for (auto Type :
126 if (!GlobalDataSections.count(Type))
127 return createStringError(
128 std::errc::invalid_argument, "missing required section type %s (%u)",
130
133 return createStringError(std::errc::invalid_argument,
134 "AddrOffsets section size mismatch");
135
138 return createStringError(std::errc::invalid_argument,
139 "AddrInfoOffsets section size mismatch");
140
141 // Step 3: Parse each global data section.
144 if (!Bytes)
145 return Bytes.takeError();
146 if (auto Err = parseAddrOffsets(*Bytes))
147 return Err;
148
150 if (!Bytes)
151 return Bytes.takeError();
152 if (auto Err = setAddrInfoOffsetsData(*Bytes))
153 return Err;
154
156 if (!Bytes)
157 return Bytes.takeError();
158 if (auto Err = setStringTableData(*Bytes))
159 return Err;
160
162 if (!Bytes)
163 return Bytes.takeError();
164 if (auto Err = setFileTableData(*Bytes))
165 return Err;
166
167 return Error::success();
168}
169
172 return createStringError(std::errc::invalid_argument,
173 "GlobalData section not supported in GSYM V1");
174
175 const StringRef Buf = MemBuffer->getBuffer();
176 const uint64_t BufSize = Buf.size();
178 while (Offset + sizeof(GlobalData) <= BufSize) {
179 auto GDOrErr = GlobalData::decode(Data, Offset);
180 if (!GDOrErr)
181 return GDOrErr.takeError();
182 const GlobalData &GD = *GDOrErr;
183
185 return Error::success();
186
187 if (GD.FileSize == 0)
188 return createStringError(std::errc::invalid_argument,
189 "GlobalData section type %u has zero size",
190 static_cast<uint32_t>(GD.Type));
191
192 if (GD.FileOffset + GD.FileSize > BufSize)
193 return createStringError(
194 std::errc::invalid_argument,
195 "GlobalData section type %u extends beyond "
196 "buffer (offset=%" PRIu64 ", size=%" PRIu64 ", bufsize=%" PRIu64 ")",
197 static_cast<uint32_t>(GD.Type), GD.FileOffset, GD.FileSize, BufSize);
198
199 GlobalDataSections[GD.Type] = GD;
200 }
201 return createStringError(std::errc::invalid_argument,
202 "GlobalData array not terminated by EndOfList");
203}
204
206 const uint8_t AddrOffSize = getAddressOffsetSize();
207 const uint32_t NumAddrs = getNumAddresses();
208 const size_t TotalBytes = NumAddrs * AddrOffSize;
209 if (Bytes.size() < TotalBytes)
210 return createStringError(std::errc::invalid_argument,
211 "failed to read address table");
212
213 // Parse the non-swap case
216 reinterpret_cast<const uint8_t *>(Bytes.data()), TotalBytes);
217 return Error::success();
218 }
219
220 // Parse the swap case
222 uint64_t Offset = 0;
223 SwappedAddrOffsets.resize(TotalBytes);
224 switch (AddrOffSize) {
225 case 1:
226 if (!Data.getU8(&Offset, SwappedAddrOffsets.data(), NumAddrs))
227 return createStringError(std::errc::invalid_argument,
228 "failed to read address table");
229 break;
230 case 2:
231 if (!Data.getU16(&Offset,
232 reinterpret_cast<uint16_t *>(SwappedAddrOffsets.data()),
233 NumAddrs))
234 return createStringError(std::errc::invalid_argument,
235 "failed to read address table");
236 break;
237 case 4:
238 if (!Data.getU32(&Offset,
239 reinterpret_cast<uint32_t *>(SwappedAddrOffsets.data()),
240 NumAddrs))
241 return createStringError(std::errc::invalid_argument,
242 "failed to read address table");
243 break;
244 case 8:
245 if (!Data.getU64(&Offset,
246 reinterpret_cast<uint64_t *>(SwappedAddrOffsets.data()),
247 NumAddrs))
248 return createStringError(std::errc::invalid_argument,
249 "failed to read address table");
250 break;
251 }
253 return Error::success();
254}
255
260
262 StrTab.Data = Bytes;
263 return Error::success();
264}
267 const uint8_t StrpSize = getStringOffsetSize();
268 GsymDataExtractor Data(Bytes, isLittleEndian(), StrpSize);
269 uint64_t Offset = 0;
270 uint32_t NumFiles = Data.getU32(&Offset);
271 uint64_t EntriesSize =
272 static_cast<uint64_t>(NumFiles) * FileEntry::getEncodedSize(StrpSize);
273 if (Bytes.size() < Offset + EntriesSize)
274 return createStringError(std::errc::invalid_argument,
275 "FileTable section too small for %u files",
276 NumFiles);
278 return Error::success();
279}
281std::optional<GlobalData> GsymReader::getGlobalData(GlobalInfoType Type) const {
282 auto It = GlobalDataSections.find(Type);
283 if (It == GlobalDataSections.end())
284 return std::nullopt;
285 return It->second;
286}
287
291 return *Data;
292 const char *TypeName = getNameForGlobalInfoType(Type).data();
293 std::optional<GlobalData> GD = getGlobalData(Type);
294 // We have a GlobalData entry but didn't get any bytes — the file may be
295 // truncated.
296 if (GD)
297 return createStringError(
298 std::errc::invalid_argument,
299 "missing bytes for %s, GSYM file might be truncated", TypeName);
300 return createStringError(std::errc::invalid_argument,
301 "missing required section type %s", TypeName);
302}
303
304std::optional<StringRef>
306 std::optional<GlobalData> GD = getGlobalData(Type);
307 if (!GD)
308 return std::nullopt;
309 StringRef Buf = MemBuffer->getBuffer();
310 if (GD->FileSize == 0 || GD->FileOffset + GD->FileSize > Buf.size())
311 return std::nullopt;
312 return Buf.substr(GD->FileOffset, GD->FileSize);
313}
314
315std::optional<uint64_t> GsymReader::getAddress(size_t Index) const {
316 switch (getAddressOffsetSize()) {
317 case 1: return addressForIndex<uint8_t>(Index);
318 case 2: return addressForIndex<uint16_t>(Index);
319 case 4: return addressForIndex<uint32_t>(Index);
320 case 8: return addressForIndex<uint64_t>(Index);
321 default:
322 llvm_unreachable("unsupported address offset size");
323 }
324 return std::nullopt;
325}
326
327std::optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
328 if (Index >= getNumAddresses())
329 return std::nullopt;
330 const uint8_t AddrInfoOffsetSize = getAddressInfoOffsetSize();
331 uint64_t Offset = Index * AddrInfoOffsetSize;
332 uint64_t AddrInfoOffset =
333 AddrInfoOffsetsData.getUnsigned(&Offset, AddrInfoOffsetSize);
334 // V1 stores absolute file offsets in AddrInfoOffsets, so no base offset is
335 // needed. V2+ stores offsets relative to the FunctionInfo section start.
337 AddrInfoOffset +=
339 return AddrInfoOffset;
340}
341
343 const uint64_t BaseAddr = getBaseAddress();
344 if (Addr >= BaseAddr) {
345 const uint64_t AddrOffset = Addr - BaseAddr;
346 std::optional<uint64_t> AddrOffsetIndex;
347 switch (getAddressOffsetSize()) {
348 case 1:
349 AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
350 break;
351 case 2:
352 AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);
353 break;
354 case 4:
355 AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);
356 break;
357 case 8:
358 AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);
359 break;
360 default:
361 return createStringError(std::errc::invalid_argument,
362 "unsupported address offset size %u",
364 }
365 if (AddrOffsetIndex)
366 return *AddrOffsetIndex;
367 }
368 return createStringError(std::errc::invalid_argument,
369 "address 0x%" PRIx64 " is not in GSYM", Addr);
370}
371
374 uint64_t &FuncStartAddr) const {
375 Expected<uint64_t> ExpectedAddrIdx = getAddressIndex(Addr);
376 if (!ExpectedAddrIdx)
377 return ExpectedAddrIdx.takeError();
378 const uint64_t FirstAddrIdx = *ExpectedAddrIdx;
379 // The AddrIdx is the first index of the function info entries that match
380 // \a Addr. We need to iterate over all function info objects that start with
381 // the same address until we find a range that contains \a Addr.
382 std::optional<uint64_t> FirstFuncStartAddr;
383 const size_t NumAddresses = getNumAddresses();
384 for (uint64_t AddrIdx = FirstAddrIdx; AddrIdx < NumAddresses; ++AddrIdx) {
385 auto ExpextedData = getFunctionInfoDataAtIndex(AddrIdx, FuncStartAddr);
386 // If there was an error, return the error.
387 if (!ExpextedData)
388 return ExpextedData;
389
390 // Remember the first function start address if it hasn't already been set.
391 // If it is already valid, check to see if it matches the first function
392 // start address and only continue if it matches.
393 if (FirstFuncStartAddr.has_value()) {
394 if (*FirstFuncStartAddr != FuncStartAddr)
395 break; // Done with consecutive function entries with same address.
396 } else {
397 FirstFuncStartAddr = FuncStartAddr;
398 }
399 // Make sure the current function address ranges contains \a Addr.
400 // Some symbols on Darwin don't have valid sizes, so if we run into a
401 // symbol with zero size, then we have found a match for our address.
402
403 // The first thing the encoding of a FunctionInfo object is the function
404 // size.
405 uint64_t Offset = 0;
406 uint32_t FuncSize = ExpextedData->getU32(&Offset);
407 if (FuncSize == 0 ||
408 AddressRange(FuncStartAddr, FuncStartAddr + FuncSize).contains(Addr))
409 return ExpextedData;
410 }
411 return createStringError(std::errc::invalid_argument,
412 "address 0x%" PRIx64 " is not in GSYM", Addr);
413}
414
417 uint64_t &FuncStartAddr) const {
418 const std::optional<uint64_t> AddrInfoOffset = getAddressInfoOffset(AddrIdx);
419 if (AddrInfoOffset == std::nullopt)
420 return createStringError(std::errc::invalid_argument,
421 "invalid address index %" PRIu64, AddrIdx);
423 "Endian must be either big or little");
424 StringRef Bytes = MemBuffer->getBuffer().substr(*AddrInfoOffset);
425 if (Bytes.empty())
426 return createStringError(std::errc::invalid_argument,
427 "invalid address info offset 0x%" PRIx64,
428 *AddrInfoOffset);
429 std::optional<uint64_t> OptFuncStartAddr = getAddress(AddrIdx);
430 if (!OptFuncStartAddr)
431 return createStringError(std::errc::invalid_argument,
432 "failed to extract address[%" PRIu64 "]", AddrIdx);
433 FuncStartAddr = *OptFuncStartAddr;
435 return Data;
436}
437
439 uint64_t FuncStartAddr = 0;
440 if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
441 return FunctionInfo::decode(*ExpectedData, FuncStartAddr);
442 else
443 return ExpectedData.takeError();
444}
445
448 uint64_t FuncStartAddr = 0;
449 if (auto ExpectedData = getFunctionInfoDataAtIndex(Idx, FuncStartAddr))
450 return FunctionInfo::decode(*ExpectedData, FuncStartAddr);
451 else
452 return ExpectedData.takeError();
453}
454
456 uint64_t Addr,
457 std::optional<GsymDataExtractor> *MergedFunctionsData) const {
458 uint64_t FuncStartAddr = 0;
459 if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
460 return FunctionInfo::lookup(*ExpectedData, *this, FuncStartAddr, Addr,
461 MergedFunctionsData);
462 else
463 return ExpectedData.takeError();
464}
465
468 std::vector<LookupResult> Results;
469 std::optional<GsymDataExtractor> MergedFunctionsData;
470
471 // First perform a lookup to get the primary function info result.
472 auto MainResult = lookup(Addr, &MergedFunctionsData);
473 if (!MainResult)
474 return MainResult.takeError();
475
476 // Add the main result as the first entry.
477 Results.push_back(std::move(*MainResult));
478
479 // Now process any merged functions data that was found during the lookup.
480 if (MergedFunctionsData) {
481 // Get data extractors for each merged function.
482 auto ExpectedMergedFuncExtractors =
484 if (!ExpectedMergedFuncExtractors)
485 return ExpectedMergedFuncExtractors.takeError();
486
487 // Process each merged function data.
488 for (GsymDataExtractor &MergedData : *ExpectedMergedFuncExtractors) {
489 if (auto FI = FunctionInfo::lookup(MergedData, *this,
490 MainResult->FuncRange.start(), Addr)) {
491 Results.push_back(std::move(*FI));
492 } else {
493 return FI.takeError();
494 }
495 }
496 }
497
498 return Results;
499}
500
501/// Format raw UUID bytes as a hex string, using the canonical 8-4-4-4-12
502/// dashed layout for the common 16-byte UUID and plain hex otherwise.
503static std::string formatGsymUUID(StringRef Bytes) {
504 std::string Hex = toHex(Bytes, /*LowerCase=*/false);
505 if (Bytes.size() == 16) {
506 Hex.insert(20, "-");
507 Hex.insert(16, "-");
508 Hex.insert(12, "-");
509 Hex.insert(8, "-");
510 }
511 return Hex;
512}
513
515 StringRef GSYMPath) {
516 // The total file size is the size of the in-memory buffer this reader was
517 // created from, so no filesystem access is required and in-memory GSYM data
518 // can be analyzed too.
519 const uint64_t FileSize = MemBuffer->getBufferSize();
520
521 // Section sizes come from the GlobalData directory, which is populated for
522 // both GSYM v1 and v2 readers, so the same logic works for both versions.
523 auto SectionSize = [&](GlobalInfoType Type) -> uint64_t {
524 if (std::optional<GlobalData> GD = getGlobalData(Type))
525 return GD->FileSize;
526 return 0;
527 };
528 const uint64_t AddrTableSize = SectionSize(GlobalInfoType::AddrOffsets);
529 const uint64_t AddrInfoOffsetsSize =
531 const uint64_t FileTableSize = SectionSize(GlobalInfoType::FileTable);
532 const uint64_t StrtabSize = SectionSize(GlobalInfoType::StringTable);
533 const uint64_t FuncInfoSize = SectionSize(GlobalInfoType::FunctionInfo);
534 // The V2 GlobalData directory is an on-disk array of 20-byte entries (Type
535 // u32
536 // + FileOffset u64 + FileSize u64) terminated by an EndOfList entry. V1
537 // synthesizes its GlobalData entries and has no on-disk directory.
538 const uint64_t GlobalDataDirSize =
539 getVersion() >= 2 ? (GlobalDataSections.size() + 1) * 20 : 0;
540 // In V2 the UUID is its own data section; report its payload separately. In
541 // V1 the UUID lives inline in the fixed header, so it is already counted
542 // there.
543 const uint64_t UUIDSize =
544 getVersion() >= 2 ? SectionSize(GlobalInfoType::UUID) : 0;
545 // The fixed file header precedes the GlobalData directory (V2) and the data
546 // sections. Its V2 size is a constant; in V1 (no on-disk directory) the
547 // header ends where the earliest data section begins.
548 uint64_t HeaderSize = HeaderV2::getEncodedSize();
549 if (getVersion() < 2) {
550 uint64_t MinSectionOffset = FileSize;
551 for (const auto &KV : GlobalDataSections)
552 MinSectionOffset = std::min(MinSectionOffset, KV.second.FileOffset);
553 HeaderSize = MinSectionOffset;
554 }
555 // Anything left over (alignment padding between sections) is reported as
556 // padding so that the byte-sizes sum exactly to the file size.
557 const uint64_t KnownSize = HeaderSize + GlobalDataDirSize + UUIDSize +
558 AddrTableSize + AddrInfoOffsetsSize +
559 FileTableSize + StrtabSize + FuncInfoSize;
560 const uint64_t PaddingSize = FileSize > KnownSize ? FileSize - KnownSize : 0;
561 const uint64_t NumAddresses = getNumAddresses();
562
563 // Walk every FunctionInfo to accumulate the per-field byte sizes.
565 FunctionInfoStats Merged;
566 for (uint64_t I = 0; I < NumAddresses; ++I) {
567 uint64_t FuncStartAddr = 0;
568 if (auto ExpData = getFunctionInfoDataAtIndex(I, FuncStartAddr)) {
569 GsymDataExtractor Data = std::move(*ExpData);
571 } else {
572 consumeError(ExpData.takeError());
573 }
574 }
575 // Alignment padding between top-level FunctionInfos (each is 4-byte aligned)
576 // is not attributed to any per-function field; report it as the remainder so
577 // that the sum of the type sizes equals function_info_data.
578 const uint64_t FIAttributed = FI.SizeAndName + FI.LineTableInfo +
579 FI.InlineInfo + FI.CallSiteInfo +
581 const uint64_t Padding =
582 FuncInfoSize > FIAttributed ? FuncInfoSize - FIAttributed : 0;
583
584 const std::string UUIDStr = formatGsymUUID(getUUID());
585
588 json::Object MergedTypes{
589 {"infotype_infolength_count_and_fnsize",
590 static_cast<int64_t>(Merged.InfoTypeInfoLengthCountAndFnSize)},
591 {"size_and_name", static_cast<int64_t>(Merged.SizeAndName)},
592 {"line_table_info", static_cast<int64_t>(Merged.LineTableInfo)},
593 {"inline_info", static_cast<int64_t>(Merged.InlineInfo)},
594 {"call_site_info", static_cast<int64_t>(Merged.CallSiteInfo)},
595 {"merged_func_info", static_cast<int64_t>(Merged.MergedFuncInfo)},
596 {"end_of_list", static_cast<int64_t>(Merged.EndOfList)}};
597
598 json::Object FuncTypes{
599 {"size_and_name", static_cast<int64_t>(FI.SizeAndName)},
600 {"line_table_info", static_cast<int64_t>(FI.LineTableInfo)},
601 {"inline_info", static_cast<int64_t>(FI.InlineInfo)},
602 {"call_site_info", static_cast<int64_t>(FI.CallSiteInfo)},
603 {"merged_func_info", static_cast<int64_t>(FI.MergedFuncInfo)},
604 {"end_of_list", static_cast<int64_t>(FI.EndOfList)},
605 {"padding", static_cast<int64_t>(Padding)},
606 {"merged_func_info_type_sizes", std::move(MergedTypes)}};
607
608 json::Object ByteSizes{
609 {"file_size", static_cast<int64_t>(FileSize)},
610 {"header", static_cast<int64_t>(HeaderSize)},
611 {"global_data_directory", static_cast<int64_t>(GlobalDataDirSize)},
612 {"uuid_section", static_cast<int64_t>(UUIDSize)},
613 {"padding", static_cast<int64_t>(PaddingSize)},
614 {"address_table", static_cast<int64_t>(AddrTableSize)},
615 {"addr_info_offsets", static_cast<int64_t>(AddrInfoOffsetsSize)},
616 {"file_table", static_cast<int64_t>(FileTableSize)},
617 {"string_table", static_cast<int64_t>(StrtabSize)},
618 {"function_info_data", static_cast<int64_t>(FuncInfoSize)},
619 {"function_info_type_sizes", std::move(FuncTypes)}};
620
621 json::Object Root{{"path", GSYMPath.str()},
622 {"uuid", UUIDStr},
623 {"num_addresses", static_cast<int64_t>(NumAddresses)},
624 {"byte-sizes", std::move(ByteSizes)}};
625
626 json::Value V(std::move(Root));
628 OS << formatv("{0:2}", V) << "\n";
629 else
630 OS << V << "\n";
631 return;
632 }
633
634 // Text format output.
635 auto Fmt = [](uint64_t Value) {
636 std::string Num = std::to_string(Value);
637 int InsertPosition = Num.length() - 3;
638 while (InsertPosition > 0) {
639 Num.insert(InsertPosition, ",");
640 InsertPosition -= 3;
641 }
642 return std::string(std::max((size_t)0, 14 - Num.length()), ' ') + Num;
643 };
644 auto Pct = [&](uint64_t Value) -> std::string {
645 char Buf[16];
646 snprintf(Buf, sizeof(Buf), "(%5.2f%%)", 100.0 * Value / FileSize);
647 return Buf;
648 };
649
650 OS << "GSYM statistics for \"" << GSYMPath << "\":\n";
651 OS << " UUID: " << UUIDStr << "\n";
652 OS << " Number of addresses: " << Fmt(NumAddresses) << "\n";
653 OS << " File size: " << Fmt(FileSize) << " bytes\n";
654 OS << " Header: " << Fmt(HeaderSize) << " bytes "
655 << Pct(HeaderSize) << "\n";
656 OS << " Global data dir: " << Fmt(GlobalDataDirSize) << " bytes "
657 << Pct(GlobalDataDirSize) << "\n";
658 OS << " UUID section: " << Fmt(UUIDSize) << " bytes " << Pct(UUIDSize)
659 << "\n";
660 OS << " Address table: " << Fmt(AddrTableSize) << " bytes "
661 << Pct(AddrTableSize) << "\n";
662 OS << " Addr info offsets: " << Fmt(AddrInfoOffsetsSize) << " bytes "
663 << Pct(AddrInfoOffsetsSize) << "\n";
664 OS << " File table: " << Fmt(FileTableSize) << " bytes "
665 << Pct(FileTableSize) << "\n";
666 OS << " String table: " << Fmt(StrtabSize) << " bytes "
667 << Pct(StrtabSize) << "\n";
668 OS << " Function info data: " << Fmt(FuncInfoSize) << " bytes "
669 << Pct(FuncInfoSize) << "\n";
670 OS << " Size and name: " << Fmt(FI.SizeAndName) << " bytes "
671 << Pct(FI.SizeAndName) << "\n";
672 OS << " Line table info: " << Fmt(FI.LineTableInfo) << " bytes "
673 << Pct(FI.LineTableInfo) << "\n";
674 OS << " Inline info: " << Fmt(FI.InlineInfo) << " bytes "
675 << Pct(FI.InlineInfo) << "\n";
676 OS << " Call site info: " << Fmt(FI.CallSiteInfo) << " bytes "
677 << Pct(FI.CallSiteInfo) << "\n";
678 OS << " End of list: " << Fmt(FI.EndOfList) << " bytes "
679 << Pct(FI.EndOfList) << "\n";
680 OS << " Padding: " << Fmt(Padding) << " bytes " << Pct(Padding)
681 << "\n";
682 OS << " Merged func info: " << Fmt(FI.MergedFuncInfo) << " bytes "
683 << Pct(FI.MergedFuncInfo) << "\n";
684 OS << " InfoType/InfoLength/Count/FnSize: "
685 << Fmt(Merged.InfoTypeInfoLengthCountAndFnSize) << " bytes "
686 << Pct(Merged.InfoTypeInfoLengthCountAndFnSize) << "\n";
687 OS << " Size and name: " << Fmt(Merged.SizeAndName) << " bytes "
688 << Pct(Merged.SizeAndName) << "\n";
689 OS << " Line table info: " << Fmt(Merged.LineTableInfo) << " bytes "
690 << Pct(Merged.LineTableInfo) << "\n";
691 OS << " Inline info: " << Fmt(Merged.InlineInfo) << " bytes "
692 << Pct(Merged.InlineInfo) << "\n";
693 OS << " Call site info: " << Fmt(Merged.CallSiteInfo) << " bytes "
694 << Pct(Merged.CallSiteInfo) << "\n";
695 OS << " Merged func info:" << Fmt(Merged.MergedFuncInfo) << " bytes "
696 << Pct(Merged.MergedFuncInfo) << "\n";
697 OS << " End of list: " << Fmt(Merged.EndOfList) << " bytes "
698 << Pct(Merged.EndOfList) << "\n";
699 OS << " Padding: " << Fmt(PaddingSize) << " bytes "
700 << Pct(PaddingSize) << "\n";
701}
702
704 uint32_t Indent) {
705 OS.indent(Indent);
706 OS << FI.Range << " \"" << getString(FI.Name) << "\"\n";
707 if (FI.OptLineTable)
708 dump(OS, *FI.OptLineTable, Indent);
709 if (FI.Inline)
710 dump(OS, *FI.Inline, Indent);
711
712 if (FI.CallSites)
713 dump(OS, *FI.CallSites, Indent);
714
715 if (FI.MergedFunctions) {
716 assert(Indent == 0 && "MergedFunctionsInfo should only exist at top level");
717 dump(OS, *FI.MergedFunctions);
718 }
719}
720
722 for (uint32_t inx = 0; inx < MFI.MergedFunctions.size(); inx++) {
723 OS << "++ Merged FunctionInfos[" << inx << "]:\n";
724 dump(OS, MFI.MergedFunctions[inx], 4);
725 }
726}
727
729 OS << HEX16(CSI.ReturnOffset);
730
731 std::string Flags;
732 auto addFlag = [&](const char *Flag) {
733 if (!Flags.empty())
734 Flags += " | ";
735 Flags += Flag;
736 };
737
739 Flags = "None";
740 else {
742 addFlag("InternalCall");
743
745 addFlag("ExternalCall");
746 }
747 OS << " Flags[" << Flags << "]";
748
749 if (!CSI.MatchRegex.empty()) {
750 OS << " MatchRegex[";
751 for (uint32_t i = 0; i < CSI.MatchRegex.size(); ++i) {
752 if (i > 0)
753 OS << ";";
754 OS << getString(CSI.MatchRegex[i]);
755 }
756 OS << "]";
757 }
758}
759
761 uint32_t Indent) {
762 OS.indent(Indent);
763 OS << "CallSites (by relative return offset):\n";
764 for (const auto &CS : CSIC.CallSites) {
765 OS.indent(Indent);
766 OS << " ";
767 dump(OS, CS);
768 OS << "\n";
769 }
770}
771
772void GsymReader::dump(raw_ostream &OS, const LineTable &LT, uint32_t Indent) {
773 OS.indent(Indent);
774 OS << "LineTable:\n";
775 for (auto &LE : LT) {
776 OS.indent(Indent);
777 OS << " " << HEX64(LE.Addr) << ' ';
778 if (LE.File)
779 dump(OS, getFile(LE.File));
780 OS << ':' << LE.Line << '\n';
781 }
782}
783
785 if (Indent == 0)
786 OS << "InlineInfo:\n";
787 else
788 OS.indent(Indent);
789 OS << II.Ranges << ' ' << getString(II.Name);
790 if (II.CallFile != 0) {
791 if (auto File = getFile(II.CallFile)) {
792 OS << " called from ";
793 dump(OS, File);
794 OS << ':' << II.CallLine;
795 }
796 }
797 OS << '\n';
798 for (const auto &ChildII : II.Children)
799 dump(OS, ChildII, Indent + 2);
800}
801
802void GsymReader::dump(raw_ostream &OS, std::optional<FileEntry> FE) {
803 if (FE) {
804 // IF we have the file from index 0, then don't print anything
805 if (FE->Dir == 0 && FE->Base == 0)
806 return;
807 StringRef Dir = getString(FE->Dir);
808 StringRef Base = getString(FE->Base);
809 if (!Dir.empty()) {
810 OS << Dir;
811 if (Dir.contains('\\') && !Dir.contains('/'))
812 OS << '\\';
813 else
814 OS << '/';
815 }
816 if (!Base.empty()) {
817 OS << Base;
818 }
819 if (!Dir.empty() || !Base.empty())
820 return;
821 }
822 OS << "<invalid-file>";
823}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Function Alias Analysis Results
#define HEX16(v)
#define HEX64(v)
static Expected< std::pair< uint16_t, llvm::endianness > > checkMagicAndDetectVersionEndian(StringRef Bytes)
Check magic bytes, determine endianness, and return the GSYM version and endianness.
static std::string formatGsymUUID(StringRef Bytes)
Format raw UUID bytes as a hex string, using the canonical 8-4-4-4-12 dashed layout for the common 16...
This file supports working with JSON data.
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
static constexpr StringLiteral Filename
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
Definition Value.cpp:484
static Split data
This file contains some functions that are useful when dealing with strings.
A class that represents an address range.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
Represents either an error or a value T.
Definition ErrorOr.h:56
reference get()
Definition ErrorOr.h:149
std::error_code getError() const
Definition ErrorOr.h:152
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition StringRef.h:597
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
constexpr const char * data() const
Get a pointer to the start of the string (which may not be null terminated).
Definition StringRef.h:138
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM Value Representation.
Definition Value.h:75
A DataExtractor subclass that adds GSYM-specific string offset support.
GsymReaderV1 reads GSYM V1 data from a buffer.
GsymReaderV2 reads GSYM V2 data from a buffer.
StringRef getString(gsym_strp_t Offset) const
Get a string from the string table.
Definition GsymReader.h:178
LLVM_ABI llvm::Error setAddrInfoOffsetsData(StringRef Bytes)
Set address info offsets section bytes into AddrInfoOffsetsData.
LLVM_ABI llvm::Error parseGlobalDataEntries(uint64_t Offset)
Parse GlobalData entries starting at Offset into GlobalDataSections.
GsymDataExtractor FileEntryData
Definition GsymReader.h:58
LLVM_ABI llvm::Expected< GsymDataExtractor > getFunctionInfoDataAtIndex(uint64_t AddrIdx, uint64_t &FuncStartAddr) const
Get the function data and address given an address index.
virtual uint8_t getStringOffsetSize() const =0
Get the string offset byte size for this GSYM file.
static LLVM_ABI llvm::Expected< std::unique_ptr< GsymReader > > copyBuffer(StringRef Bytes)
Construct a GsymReader from a buffer.
virtual llvm::Error parseHeaderAndGlobalDataEntries()=0
Parse the version-specific header and populate GlobalDataSections.
std::optional< FileEntry > getFile(uint32_t Index) const
Get the a file entry for the suppplied file index.
Definition GsymReader.h:189
bool isLittleEndian() const
Definition GsymReader.h:68
LLVM_ABI std::optional< GlobalData > getGlobalData(GlobalInfoType Type) const
Get the GlobalData entry for a section type.
ArrayRef< uint8_t > AddrOffsets
Definition GsymReader.h:55
LLVM_ABI llvm::Error setFileTableData(StringRef Bytes)
Set file table section bytes into FileEntryData.
GsymDataExtractor AddrInfoOffsetsData
Definition GsymReader.h:57
static LLVM_ABI llvm::Expected< std::unique_ptr< GsymReader > > create(std::unique_ptr< MemoryBuffer > &MemBuffer)
Create a GSYM from a memory buffer.
LLVM_ABI std::optional< uint64_t > getAddress(size_t Index) const
Gets an address from the address table.
LLVM_ABI std::optional< uint64_t > getAddressInfoOffset(size_t Index) const
Given an address index, get the offset for the FunctionInfo.
LLVM_ABI GsymReader(std::unique_ptr< MemoryBuffer > Buffer, llvm::endianness Endian)
virtual uint16_t getVersion() const =0
Get the GSYM version for this reader.
LLVM_ABI llvm::Error setStringTableData(StringRef Bytes)
Set string table section bytes into StrTab.
LLVM_ABI llvm::Expected< StringRef > getRequiredGlobalDataBytes(GlobalInfoType Type) const
Get the raw bytes for a required GlobalData section as a StringRef.
LLVM_ABI void dumpStatistics(raw_ostream &OS, StatisticsFormat Format=StatisticsFormat::Text, StringRef GSYMPath="")
Dump statistics about the GSYM data contained in this object.
LLVM_ABI llvm::Expected< FunctionInfo > getFunctionInfo(uint64_t Addr) const
Get the full function info for an address.
std::optional< uint64_t > addressForIndex(size_t Index) const
Get an appropriate address from the address table.
Definition GsymReader.h:441
LLVM_ABI llvm::Expected< LookupResult > lookup(uint64_t Addr, std::optional< GsymDataExtractor > *MergedFuncsData=nullptr) const
Lookup an address in the a GSYM.
std::vector< uint8_t > SwappedAddrOffsets
Definition GsymReader.h:56
LLVM_ABI llvm::Error parseAddrOffsets(StringRef Bytes)
Parse address offsets section bytes into AddrOffsets.
LLVM_ABI llvm::Expected< GsymDataExtractor > getFunctionInfoDataForAddress(uint64_t Addr, uint64_t &FuncStartAddr) const
Given an address, find the correct function info data and function address.
LLVM_ABI Expected< uint64_t > getAddressIndex(const uint64_t Addr) const
Given an address, find the address index.
virtual uint64_t getNumAddresses() const =0
Get the number of addresses in this GSYM file.
std::map< GlobalInfoType, GlobalData > GlobalDataSections
Parsed GlobalData entries, keyed by type.
Definition GsymReader.h:54
LLVM_ABI llvm::Error parse()
Parse the GSYM data from the memory buffer.
virtual uint8_t getAddressInfoOffsetSize() const =0
Get the address info offset byte size for this GSYM file.
std::unique_ptr< MemoryBuffer > MemBuffer
Definition GsymReader.h:50
virtual void dump(raw_ostream &OS)=0
Dump the entire Gsym data contained in this object.
virtual StringRef getUUID() const =0
Get the raw UUID bytes for this GSYM file, or an empty ref if none.
static LLVM_ABI llvm::Expected< std::unique_ptr< GsymReader > > openFile(StringRef Path)
Construct a GsymReader from a file on disk.
LLVM_ABI llvm::Expected< FunctionInfo > getFunctionInfoAtIndex(uint64_t AddrIdx) const
Get the full function info given an address index.
virtual uint8_t getAddressOffsetSize() const =0
Get the address offset byte size for this GSYM file.
llvm::endianness Endian
Definition GsymReader.h:51
LLVM_ABI llvm::Expected< std::vector< LookupResult > > lookupAll(uint64_t Addr) const
Lookup all merged functions for a given address.
virtual uint64_t getBaseAddress() const =0
Get the base address of this GSYM file.
std::optional< uint64_t > getAddressOffsetIndex(const uint64_t AddrOffset) const
Lookup an address offset in the AddrOffsets table.
Definition GsymReader.h:459
LLVM_ABI std::optional< StringRef > getOptionalGlobalDataBytes(GlobalInfoType Type) const
Get the raw bytes for an optional GlobalData section as a StringRef.
LineTable class contains deserialized versions of line tables for each function's address ranges.
Definition LineTable.h:119
An Object is a JSON object, which maps strings to heterogenous JSON values.
Definition JSON.h:98
A Value is an JSON value of unknown type.
Definition JSON.h:291
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_ABI StringRef getNameForGlobalInfoType(GlobalInfoType Type)
GlobalInfoType
GlobalInfoType allows GSYM files to encode global information within a GSYM file in a way that is ext...
Definition GlobalData.h:26
constexpr uint32_t GSYM_MAGIC
Definition Header.h:25
constexpr uint32_t GSYM_CIGAM
Definition Header.h:26
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void toHex(ArrayRef< uint8_t > Input, bool LowerCase, SmallVectorImpl< char > &Output)
Convert buffer Input to its hexadecimal representation. The returned string is double the size of Inp...
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1917
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:107
endianness
Definition bit.h:71
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
std::vector< CallSiteInfo > CallSites
uint64_t ReturnOffset
The return offset of the call site - relative to the function start.
std::vector< gsym_strp_t > MatchRegex
Offsets into the string table for function names regex patterns.
static constexpr uint64_t getEncodedSize(uint8_t StringOffsetSize)
Returns the on-disk encoded size of a FileEntry for the given string offset size.
Definition FileEntry.h:38
Byte-size accounting for a FunctionInfo, broken down by field / InfoType.
Function information in GSYM files encodes information for one contiguous address range.
std::optional< InlineInfo > Inline
std::optional< MergedFunctionsInfo > MergedFunctions
static LLVM_ABI llvm::Expected< FunctionInfo > decode(GsymDataExtractor &Data, uint64_t BaseAddr)
Decode an object from a binary data stream.
std::optional< CallSiteInfoCollection > CallSites
static LLVM_ABI void parseStatistics(GsymDataExtractor &Data, FunctionInfoStats &Stats, FunctionInfoStats *MergedFuncInfoStats=nullptr)
Parse the function info data and accumulate the byte size of each field / InfoType into Stats.
gsym_strp_t Name
String table offset in the string table.
std::optional< LineTable > OptLineTable
static LLVM_ABI llvm::Expected< LookupResult > lookup(GsymDataExtractor &Data, const GsymReader &GR, uint64_t FuncAddr, uint64_t Addr, std::optional< GsymDataExtractor > *MergedFuncsData=nullptr)
Lookup an address within a FunctionInfo object's data stream.
GlobalData describes a section of data in a GSYM file by its type, file offset, and size.
Definition GlobalData.h:61
static LLVM_ABI llvm::Expected< GlobalData > decode(GsymDataExtractor &GsymData, uint64_t &Offset)
Decode a GlobalData entry from a binary data stream.
GlobalInfoType Type
Definition GlobalData.h:62
static constexpr uint32_t getVersion()
Return the version of this header.
Definition HeaderV2.h:83
static constexpr uint64_t getEncodedSize()
Return the on-disk encoded size of the header in bytes.
Definition HeaderV2.h:87
static constexpr uint32_t getVersion()
Return the version of this header.
Definition Header.h:89
Inline information stores the name of the inline function along with an array of address ranges.
Definition InlineInfo.h:61
static LLVM_ABI llvm::Expected< std::vector< GsymDataExtractor > > getFuncsDataExtractors(GsymDataExtractor &Data)
Get a vector of GsymDataExtractor objects for the functions in this MergedFunctionsInfo object.
std::vector< FunctionInfo > MergedFunctions