17330f729Sjoerg //===- GsymReader.cpp -----------------------------------------------------===//
27330f729Sjoerg //
3*82d56013Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*82d56013Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*82d56013Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg
97330f729Sjoerg #include "llvm/DebugInfo/GSYM/GsymReader.h"
107330f729Sjoerg
117330f729Sjoerg #include <assert.h>
127330f729Sjoerg #include <inttypes.h>
137330f729Sjoerg #include <stdio.h>
147330f729Sjoerg #include <stdlib.h>
157330f729Sjoerg
167330f729Sjoerg #include "llvm/DebugInfo/GSYM/GsymCreator.h"
177330f729Sjoerg #include "llvm/DebugInfo/GSYM/InlineInfo.h"
187330f729Sjoerg #include "llvm/DebugInfo/GSYM/LineTable.h"
197330f729Sjoerg #include "llvm/Support/BinaryStreamReader.h"
207330f729Sjoerg #include "llvm/Support/DataExtractor.h"
217330f729Sjoerg #include "llvm/Support/MemoryBuffer.h"
227330f729Sjoerg
237330f729Sjoerg using namespace llvm;
247330f729Sjoerg using namespace gsym;
257330f729Sjoerg
GsymReader(std::unique_ptr<MemoryBuffer> Buffer)267330f729Sjoerg GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) :
277330f729Sjoerg MemBuffer(std::move(Buffer)),
287330f729Sjoerg Endian(support::endian::system_endianness()) {}
297330f729Sjoerg
307330f729Sjoerg GsymReader::GsymReader(GsymReader &&RHS) = default;
317330f729Sjoerg
327330f729Sjoerg GsymReader::~GsymReader() = default;
337330f729Sjoerg
openFile(StringRef Filename)347330f729Sjoerg llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {
357330f729Sjoerg // Open the input file and return an appropriate error if needed.
367330f729Sjoerg ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
377330f729Sjoerg MemoryBuffer::getFileOrSTDIN(Filename);
387330f729Sjoerg auto Err = BuffOrErr.getError();
397330f729Sjoerg if (Err)
407330f729Sjoerg return llvm::errorCodeToError(Err);
417330f729Sjoerg return create(BuffOrErr.get());
427330f729Sjoerg }
437330f729Sjoerg
copyBuffer(StringRef Bytes)447330f729Sjoerg llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {
457330f729Sjoerg auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
467330f729Sjoerg return create(MemBuffer);
477330f729Sjoerg }
487330f729Sjoerg
497330f729Sjoerg llvm::Expected<llvm::gsym::GsymReader>
create(std::unique_ptr<MemoryBuffer> & MemBuffer)507330f729Sjoerg GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
517330f729Sjoerg if (!MemBuffer.get())
527330f729Sjoerg return createStringError(std::errc::invalid_argument,
537330f729Sjoerg "invalid memory buffer");
547330f729Sjoerg GsymReader GR(std::move(MemBuffer));
557330f729Sjoerg llvm::Error Err = GR.parse();
567330f729Sjoerg if (Err)
577330f729Sjoerg return std::move(Err);
587330f729Sjoerg return std::move(GR);
597330f729Sjoerg }
607330f729Sjoerg
617330f729Sjoerg llvm::Error
parse()627330f729Sjoerg GsymReader::parse() {
637330f729Sjoerg BinaryStreamReader FileData(MemBuffer->getBuffer(),
647330f729Sjoerg support::endian::system_endianness());
657330f729Sjoerg // Check for the magic bytes. This file format is designed to be mmap'ed
667330f729Sjoerg // into a process and accessed as read only. This is done for performance
677330f729Sjoerg // and efficiency for symbolicating and parsing GSYM data.
687330f729Sjoerg if (FileData.readObject(Hdr))
697330f729Sjoerg return createStringError(std::errc::invalid_argument,
707330f729Sjoerg "not enough data for a GSYM header");
717330f729Sjoerg
727330f729Sjoerg const auto HostByteOrder = support::endian::system_endianness();
737330f729Sjoerg switch (Hdr->Magic) {
747330f729Sjoerg case GSYM_MAGIC:
757330f729Sjoerg Endian = HostByteOrder;
767330f729Sjoerg break;
777330f729Sjoerg case GSYM_CIGAM:
787330f729Sjoerg // This is a GSYM file, but not native endianness.
797330f729Sjoerg Endian = sys::IsBigEndianHost ? support::little : support::big;
807330f729Sjoerg Swap.reset(new SwappedData);
817330f729Sjoerg break;
827330f729Sjoerg default:
837330f729Sjoerg return createStringError(std::errc::invalid_argument,
847330f729Sjoerg "not a GSYM file");
857330f729Sjoerg }
867330f729Sjoerg
877330f729Sjoerg bool DataIsLittleEndian = HostByteOrder != support::little;
887330f729Sjoerg // Read a correctly byte swapped header if we need to.
897330f729Sjoerg if (Swap) {
907330f729Sjoerg DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
917330f729Sjoerg if (auto ExpectedHdr = Header::decode(Data))
927330f729Sjoerg Swap->Hdr = ExpectedHdr.get();
937330f729Sjoerg else
947330f729Sjoerg return ExpectedHdr.takeError();
957330f729Sjoerg Hdr = &Swap->Hdr;
967330f729Sjoerg }
977330f729Sjoerg
987330f729Sjoerg // Detect errors in the header and report any that are found. If we make it
997330f729Sjoerg // past this without errors, we know we have a good magic value, a supported
1007330f729Sjoerg // version number, verified address offset size and a valid UUID size.
1017330f729Sjoerg if (Error Err = Hdr->checkForError())
1027330f729Sjoerg return Err;
1037330f729Sjoerg
1047330f729Sjoerg if (!Swap) {
1057330f729Sjoerg // This is the native endianness case that is most common and optimized for
1067330f729Sjoerg // efficient lookups. Here we just grab pointers to the native data and
1077330f729Sjoerg // use ArrayRef objects to allow efficient read only access.
1087330f729Sjoerg
1097330f729Sjoerg // Read the address offsets.
1107330f729Sjoerg if (FileData.padToAlignment(Hdr->AddrOffSize) ||
1117330f729Sjoerg FileData.readArray(AddrOffsets,
1127330f729Sjoerg Hdr->NumAddresses * Hdr->AddrOffSize))
1137330f729Sjoerg return createStringError(std::errc::invalid_argument,
1147330f729Sjoerg "failed to read address table");
1157330f729Sjoerg
1167330f729Sjoerg // Read the address info offsets.
1177330f729Sjoerg if (FileData.padToAlignment(4) ||
1187330f729Sjoerg FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses))
1197330f729Sjoerg return createStringError(std::errc::invalid_argument,
1207330f729Sjoerg "failed to read address info offsets table");
1217330f729Sjoerg
1227330f729Sjoerg // Read the file table.
1237330f729Sjoerg uint32_t NumFiles = 0;
1247330f729Sjoerg if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles))
1257330f729Sjoerg return createStringError(std::errc::invalid_argument,
1267330f729Sjoerg "failed to read file table");
1277330f729Sjoerg
1287330f729Sjoerg // Get the string table.
1297330f729Sjoerg FileData.setOffset(Hdr->StrtabOffset);
1307330f729Sjoerg if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize))
1317330f729Sjoerg return createStringError(std::errc::invalid_argument,
1327330f729Sjoerg "failed to read string table");
1337330f729Sjoerg } else {
1347330f729Sjoerg // This is the non native endianness case that is not common and not
1357330f729Sjoerg // optimized for lookups. Here we decode the important tables into local
1367330f729Sjoerg // storage and then set the ArrayRef objects to point to these swapped
1377330f729Sjoerg // copies of the read only data so lookups can be as efficient as possible.
1387330f729Sjoerg DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);
1397330f729Sjoerg
1407330f729Sjoerg // Read the address offsets.
1417330f729Sjoerg uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize);
1427330f729Sjoerg Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize);
1437330f729Sjoerg switch (Hdr->AddrOffSize) {
1447330f729Sjoerg case 1:
1457330f729Sjoerg if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))
1467330f729Sjoerg return createStringError(std::errc::invalid_argument,
1477330f729Sjoerg "failed to read address table");
1487330f729Sjoerg break;
1497330f729Sjoerg case 2:
1507330f729Sjoerg if (!Data.getU16(&Offset,
1517330f729Sjoerg reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),
1527330f729Sjoerg Hdr->NumAddresses))
1537330f729Sjoerg return createStringError(std::errc::invalid_argument,
1547330f729Sjoerg "failed to read address table");
1557330f729Sjoerg break;
1567330f729Sjoerg case 4:
1577330f729Sjoerg if (!Data.getU32(&Offset,
1587330f729Sjoerg reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),
1597330f729Sjoerg Hdr->NumAddresses))
1607330f729Sjoerg return createStringError(std::errc::invalid_argument,
1617330f729Sjoerg "failed to read address table");
1627330f729Sjoerg break;
1637330f729Sjoerg case 8:
1647330f729Sjoerg if (!Data.getU64(&Offset,
1657330f729Sjoerg reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),
1667330f729Sjoerg Hdr->NumAddresses))
1677330f729Sjoerg return createStringError(std::errc::invalid_argument,
1687330f729Sjoerg "failed to read address table");
1697330f729Sjoerg }
1707330f729Sjoerg AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);
1717330f729Sjoerg
1727330f729Sjoerg // Read the address info offsets.
1737330f729Sjoerg Offset = alignTo(Offset, 4);
1747330f729Sjoerg Swap->AddrInfoOffsets.resize(Hdr->NumAddresses);
1757330f729Sjoerg if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses))
1767330f729Sjoerg AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets);
1777330f729Sjoerg else
1787330f729Sjoerg return createStringError(std::errc::invalid_argument,
1797330f729Sjoerg "failed to read address table");
1807330f729Sjoerg // Read the file table.
1817330f729Sjoerg const uint32_t NumFiles = Data.getU32(&Offset);
1827330f729Sjoerg if (NumFiles > 0) {
1837330f729Sjoerg Swap->Files.resize(NumFiles);
1847330f729Sjoerg if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))
1857330f729Sjoerg Files = ArrayRef<FileEntry>(Swap->Files);
1867330f729Sjoerg else
1877330f729Sjoerg return createStringError(std::errc::invalid_argument,
1887330f729Sjoerg "failed to read file table");
1897330f729Sjoerg }
1907330f729Sjoerg // Get the string table.
1917330f729Sjoerg StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,
1927330f729Sjoerg Hdr->StrtabSize);
1937330f729Sjoerg if (StrTab.Data.empty())
1947330f729Sjoerg return createStringError(std::errc::invalid_argument,
1957330f729Sjoerg "failed to read string table");
1967330f729Sjoerg }
1977330f729Sjoerg return Error::success();
1987330f729Sjoerg
1997330f729Sjoerg }
2007330f729Sjoerg
getHeader() const2017330f729Sjoerg const Header &GsymReader::getHeader() const {
2027330f729Sjoerg // The only way to get a GsymReader is from GsymReader::openFile(...) or
2037330f729Sjoerg // GsymReader::copyBuffer() and the header must be valid and initialized to
2047330f729Sjoerg // a valid pointer value, so the assert below should not trigger.
2057330f729Sjoerg assert(Hdr);
2067330f729Sjoerg return *Hdr;
2077330f729Sjoerg }
2087330f729Sjoerg
getAddress(size_t Index) const2097330f729Sjoerg Optional<uint64_t> GsymReader::getAddress(size_t Index) const {
2107330f729Sjoerg switch (Hdr->AddrOffSize) {
2117330f729Sjoerg case 1: return addressForIndex<uint8_t>(Index);
2127330f729Sjoerg case 2: return addressForIndex<uint16_t>(Index);
2137330f729Sjoerg case 4: return addressForIndex<uint32_t>(Index);
2147330f729Sjoerg case 8: return addressForIndex<uint64_t>(Index);
2157330f729Sjoerg }
2167330f729Sjoerg return llvm::None;
2177330f729Sjoerg }
2187330f729Sjoerg
getAddressInfoOffset(size_t Index) const2197330f729Sjoerg Optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
2207330f729Sjoerg const auto NumAddrInfoOffsets = AddrInfoOffsets.size();
2217330f729Sjoerg if (Index < NumAddrInfoOffsets)
2227330f729Sjoerg return AddrInfoOffsets[Index];
2237330f729Sjoerg return llvm::None;
2247330f729Sjoerg }
2257330f729Sjoerg
2267330f729Sjoerg Expected<uint64_t>
getAddressIndex(const uint64_t Addr) const2277330f729Sjoerg GsymReader::getAddressIndex(const uint64_t Addr) const {
228*82d56013Sjoerg if (Addr >= Hdr->BaseAddress) {
2297330f729Sjoerg const uint64_t AddrOffset = Addr - Hdr->BaseAddress;
230*82d56013Sjoerg Optional<uint64_t> AddrOffsetIndex;
2317330f729Sjoerg switch (Hdr->AddrOffSize) {
232*82d56013Sjoerg case 1:
233*82d56013Sjoerg AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
234*82d56013Sjoerg break;
235*82d56013Sjoerg case 2:
236*82d56013Sjoerg AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);
237*82d56013Sjoerg break;
238*82d56013Sjoerg case 4:
239*82d56013Sjoerg AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);
240*82d56013Sjoerg break;
241*82d56013Sjoerg case 8:
242*82d56013Sjoerg AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);
243*82d56013Sjoerg break;
244*82d56013Sjoerg default:
2457330f729Sjoerg return createStringError(std::errc::invalid_argument,
2467330f729Sjoerg "unsupported address offset size %u",
2477330f729Sjoerg Hdr->AddrOffSize);
2487330f729Sjoerg }
249*82d56013Sjoerg if (AddrOffsetIndex)
250*82d56013Sjoerg return *AddrOffsetIndex;
251*82d56013Sjoerg }
252*82d56013Sjoerg return createStringError(std::errc::invalid_argument,
253*82d56013Sjoerg "address 0x%" PRIx64 " is not in GSYM", Addr);
254*82d56013Sjoerg
255*82d56013Sjoerg }
2567330f729Sjoerg
getFunctionInfo(uint64_t Addr) const2577330f729Sjoerg llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
2587330f729Sjoerg Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
2597330f729Sjoerg if (!AddressIndex)
2607330f729Sjoerg return AddressIndex.takeError();
2617330f729Sjoerg // Address info offsets size should have been checked in parse().
2627330f729Sjoerg assert(*AddressIndex < AddrInfoOffsets.size());
2637330f729Sjoerg auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
2647330f729Sjoerg DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
2657330f729Sjoerg if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) {
2667330f729Sjoerg auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr);
2677330f729Sjoerg if (ExpectedFI) {
2687330f729Sjoerg if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0)
2697330f729Sjoerg return ExpectedFI;
2707330f729Sjoerg return createStringError(std::errc::invalid_argument,
271*82d56013Sjoerg "address 0x%" PRIx64 " is not in GSYM", Addr);
2727330f729Sjoerg }
2737330f729Sjoerg }
2747330f729Sjoerg return createStringError(std::errc::invalid_argument,
2757330f729Sjoerg "failed to extract address[%" PRIu64 "]",
2767330f729Sjoerg *AddressIndex);
2777330f729Sjoerg }
278*82d56013Sjoerg
lookup(uint64_t Addr) const279*82d56013Sjoerg llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const {
280*82d56013Sjoerg Expected<uint64_t> AddressIndex = getAddressIndex(Addr);
281*82d56013Sjoerg if (!AddressIndex)
282*82d56013Sjoerg return AddressIndex.takeError();
283*82d56013Sjoerg // Address info offsets size should have been checked in parse().
284*82d56013Sjoerg assert(*AddressIndex < AddrInfoOffsets.size());
285*82d56013Sjoerg auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex];
286*82d56013Sjoerg DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4);
287*82d56013Sjoerg if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex))
288*82d56013Sjoerg return FunctionInfo::lookup(Data, *this, *OptAddr, Addr);
289*82d56013Sjoerg return createStringError(std::errc::invalid_argument,
290*82d56013Sjoerg "failed to extract address[%" PRIu64 "]",
291*82d56013Sjoerg *AddressIndex);
292*82d56013Sjoerg }
293*82d56013Sjoerg
dump(raw_ostream & OS)294*82d56013Sjoerg void GsymReader::dump(raw_ostream &OS) {
295*82d56013Sjoerg const auto &Header = getHeader();
296*82d56013Sjoerg // Dump the GSYM header.
297*82d56013Sjoerg OS << Header << "\n";
298*82d56013Sjoerg // Dump the address table.
299*82d56013Sjoerg OS << "Address Table:\n";
300*82d56013Sjoerg OS << "INDEX OFFSET";
301*82d56013Sjoerg
302*82d56013Sjoerg switch (Hdr->AddrOffSize) {
303*82d56013Sjoerg case 1: OS << "8 "; break;
304*82d56013Sjoerg case 2: OS << "16"; break;
305*82d56013Sjoerg case 4: OS << "32"; break;
306*82d56013Sjoerg case 8: OS << "64"; break;
307*82d56013Sjoerg default: OS << "??"; break;
308*82d56013Sjoerg }
309*82d56013Sjoerg OS << " (ADDRESS)\n";
310*82d56013Sjoerg OS << "====== =============================== \n";
311*82d56013Sjoerg for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
312*82d56013Sjoerg OS << format("[%4u] ", I);
313*82d56013Sjoerg switch (Hdr->AddrOffSize) {
314*82d56013Sjoerg case 1: OS << HEX8(getAddrOffsets<uint8_t>()[I]); break;
315*82d56013Sjoerg case 2: OS << HEX16(getAddrOffsets<uint16_t>()[I]); break;
316*82d56013Sjoerg case 4: OS << HEX32(getAddrOffsets<uint32_t>()[I]); break;
317*82d56013Sjoerg case 8: OS << HEX32(getAddrOffsets<uint64_t>()[I]); break;
318*82d56013Sjoerg default: break;
319*82d56013Sjoerg }
320*82d56013Sjoerg OS << " (" << HEX64(*getAddress(I)) << ")\n";
321*82d56013Sjoerg }
322*82d56013Sjoerg // Dump the address info offsets table.
323*82d56013Sjoerg OS << "\nAddress Info Offsets:\n";
324*82d56013Sjoerg OS << "INDEX Offset\n";
325*82d56013Sjoerg OS << "====== ==========\n";
326*82d56013Sjoerg for (uint32_t I = 0; I < Header.NumAddresses; ++I)
327*82d56013Sjoerg OS << format("[%4u] ", I) << HEX32(AddrInfoOffsets[I]) << "\n";
328*82d56013Sjoerg // Dump the file table.
329*82d56013Sjoerg OS << "\nFiles:\n";
330*82d56013Sjoerg OS << "INDEX DIRECTORY BASENAME PATH\n";
331*82d56013Sjoerg OS << "====== ========== ========== ==============================\n";
332*82d56013Sjoerg for (uint32_t I = 0; I < Files.size(); ++I) {
333*82d56013Sjoerg OS << format("[%4u] ", I) << HEX32(Files[I].Dir) << ' '
334*82d56013Sjoerg << HEX32(Files[I].Base) << ' ';
335*82d56013Sjoerg dump(OS, getFile(I));
336*82d56013Sjoerg OS << "\n";
337*82d56013Sjoerg }
338*82d56013Sjoerg OS << "\n" << StrTab << "\n";
339*82d56013Sjoerg
340*82d56013Sjoerg for (uint32_t I = 0; I < Header.NumAddresses; ++I) {
341*82d56013Sjoerg OS << "FunctionInfo @ " << HEX32(AddrInfoOffsets[I]) << ": ";
342*82d56013Sjoerg if (auto FI = getFunctionInfo(*getAddress(I)))
343*82d56013Sjoerg dump(OS, *FI);
344*82d56013Sjoerg else
345*82d56013Sjoerg logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");
346*82d56013Sjoerg }
347*82d56013Sjoerg }
348*82d56013Sjoerg
dump(raw_ostream & OS,const FunctionInfo & FI)349*82d56013Sjoerg void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI) {
350*82d56013Sjoerg OS << FI.Range << " \"" << getString(FI.Name) << "\"\n";
351*82d56013Sjoerg if (FI.OptLineTable)
352*82d56013Sjoerg dump(OS, *FI.OptLineTable);
353*82d56013Sjoerg if (FI.Inline)
354*82d56013Sjoerg dump(OS, *FI.Inline);
355*82d56013Sjoerg }
356*82d56013Sjoerg
dump(raw_ostream & OS,const LineTable & LT)357*82d56013Sjoerg void GsymReader::dump(raw_ostream &OS, const LineTable <) {
358*82d56013Sjoerg OS << "LineTable:\n";
359*82d56013Sjoerg for (auto &LE: LT) {
360*82d56013Sjoerg OS << " " << HEX64(LE.Addr) << ' ';
361*82d56013Sjoerg if (LE.File)
362*82d56013Sjoerg dump(OS, getFile(LE.File));
363*82d56013Sjoerg OS << ':' << LE.Line << '\n';
364*82d56013Sjoerg }
365*82d56013Sjoerg }
366*82d56013Sjoerg
dump(raw_ostream & OS,const InlineInfo & II,uint32_t Indent)367*82d56013Sjoerg void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {
368*82d56013Sjoerg if (Indent == 0)
369*82d56013Sjoerg OS << "InlineInfo:\n";
370*82d56013Sjoerg else
371*82d56013Sjoerg OS.indent(Indent);
372*82d56013Sjoerg OS << II.Ranges << ' ' << getString(II.Name);
373*82d56013Sjoerg if (II.CallFile != 0) {
374*82d56013Sjoerg if (auto File = getFile(II.CallFile)) {
375*82d56013Sjoerg OS << " called from ";
376*82d56013Sjoerg dump(OS, File);
377*82d56013Sjoerg OS << ':' << II.CallLine;
378*82d56013Sjoerg }
379*82d56013Sjoerg }
380*82d56013Sjoerg OS << '\n';
381*82d56013Sjoerg for (const auto &ChildII: II.Children)
382*82d56013Sjoerg dump(OS, ChildII, Indent + 2);
383*82d56013Sjoerg }
384*82d56013Sjoerg
dump(raw_ostream & OS,Optional<FileEntry> FE)385*82d56013Sjoerg void GsymReader::dump(raw_ostream &OS, Optional<FileEntry> FE) {
386*82d56013Sjoerg if (FE) {
387*82d56013Sjoerg // IF we have the file from index 0, then don't print anything
388*82d56013Sjoerg if (FE->Dir == 0 && FE->Base == 0)
389*82d56013Sjoerg return;
390*82d56013Sjoerg StringRef Dir = getString(FE->Dir);
391*82d56013Sjoerg StringRef Base = getString(FE->Base);
392*82d56013Sjoerg if (!Dir.empty()) {
393*82d56013Sjoerg OS << Dir;
394*82d56013Sjoerg if (Dir.contains('\\') && !Dir.contains('/'))
395*82d56013Sjoerg OS << '\\';
396*82d56013Sjoerg else
397*82d56013Sjoerg OS << '/';
398*82d56013Sjoerg }
399*82d56013Sjoerg if (!Base.empty()) {
400*82d56013Sjoerg OS << Base;
401*82d56013Sjoerg }
402*82d56013Sjoerg if (!Dir.empty() || !Base.empty())
403*82d56013Sjoerg return;
404*82d56013Sjoerg }
405*82d56013Sjoerg OS << "<invalid-file>";
406*82d56013Sjoerg }
407