brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.5 KiB · 0a5bb7c Raw
559 lines · cpp
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-exception6//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/InlineInfo.h"17#include "llvm/DebugInfo/GSYM/LineTable.h"18#include "llvm/Support/BinaryStreamReader.h"19#include "llvm/Support/DataExtractor.h"20#include "llvm/Support/MemoryBuffer.h"21 22using namespace llvm;23using namespace gsym;24 25GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer)26    : MemBuffer(std::move(Buffer)), Endian(llvm::endianness::native) {}27 28GsymReader::GsymReader(GsymReader &&RHS) = default;29 30GsymReader::~GsymReader() = default;31 32llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) {33  // Open the input file and return an appropriate error if needed.34  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =35      MemoryBuffer::getFileOrSTDIN(Filename);36  auto Err = BuffOrErr.getError();37  if (Err)38    return llvm::errorCodeToError(Err);39  return create(BuffOrErr.get());40}41 42llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) {43  auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");44  return create(MemBuffer);45}46 47llvm::Expected<llvm::gsym::GsymReader>48GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {49  if (!MemBuffer)50    return createStringError(std::errc::invalid_argument,51                             "invalid memory buffer");52  GsymReader GR(std::move(MemBuffer));53  llvm::Error Err = GR.parse();54  if (Err)55    return std::move(Err);56  return std::move(GR);57}58 59llvm::Error60GsymReader::parse() {61  BinaryStreamReader FileData(MemBuffer->getBuffer(), llvm::endianness::native);62  // Check for the magic bytes. This file format is designed to be mmap'ed63  // into a process and accessed as read only. This is done for performance64  // and efficiency for symbolicating and parsing GSYM data.65  if (FileData.readObject(Hdr))66    return createStringError(std::errc::invalid_argument,67                             "not enough data for a GSYM header");68 69  const auto HostByteOrder = llvm::endianness::native;70  switch (Hdr->Magic) {71    case GSYM_MAGIC:72      Endian = HostByteOrder;73      break;74    case GSYM_CIGAM:75      // This is a GSYM file, but not native endianness.76      Endian = sys::IsBigEndianHost ? llvm::endianness::little77                                    : llvm::endianness::big;78      Swap.reset(new SwappedData);79      break;80    default:81      return createStringError(std::errc::invalid_argument,82                               "not a GSYM file");83  }84 85  bool DataIsLittleEndian = HostByteOrder != llvm::endianness::little;86  // Read a correctly byte swapped header if we need to.87  if (Swap) {88    DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);89    if (auto ExpectedHdr = Header::decode(Data))90      Swap->Hdr = ExpectedHdr.get();91    else92      return ExpectedHdr.takeError();93    Hdr = &Swap->Hdr;94  }95 96  // Detect errors in the header and report any that are found. If we make it97  // past this without errors, we know we have a good magic value, a supported98  // version number, verified address offset size and a valid UUID size.99  if (Error Err = Hdr->checkForError())100    return Err;101 102  if (!Swap) {103    // This is the native endianness case that is most common and optimized for104    // efficient lookups. Here we just grab pointers to the native data and105    // use ArrayRef objects to allow efficient read only access.106 107    // Read the address offsets.108    if (FileData.padToAlignment(Hdr->AddrOffSize) ||109        FileData.readArray(AddrOffsets,110                           Hdr->NumAddresses * Hdr->AddrOffSize))111      return createStringError(std::errc::invalid_argument,112                              "failed to read address table");113 114    // Read the address info offsets.115    if (FileData.padToAlignment(4) ||116        FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses))117      return createStringError(std::errc::invalid_argument,118                              "failed to read address info offsets table");119 120    // Read the file table.121    uint32_t NumFiles = 0;122    if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles))123      return createStringError(std::errc::invalid_argument,124                              "failed to read file table");125 126    // Get the string table.127    FileData.setOffset(Hdr->StrtabOffset);128    if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize))129      return createStringError(std::errc::invalid_argument,130                              "failed to read string table");131} else {132  // This is the non native endianness case that is not common and not133  // optimized for lookups. Here we decode the important tables into local134  // storage and then set the ArrayRef objects to point to these swapped135  // copies of the read only data so lookups can be as efficient as possible.136  DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4);137 138  // Read the address offsets.139  uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize);140  Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize);141  switch (Hdr->AddrOffSize) {142    case 1:143      if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses))144        return createStringError(std::errc::invalid_argument,145                                  "failed to read address table");146      break;147    case 2:148      if (!Data.getU16(&Offset,149                        reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()),150                        Hdr->NumAddresses))151        return createStringError(std::errc::invalid_argument,152                                  "failed to read address table");153      break;154    case 4:155      if (!Data.getU32(&Offset,156                        reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()),157                        Hdr->NumAddresses))158        return createStringError(std::errc::invalid_argument,159                                  "failed to read address table");160      break;161    case 8:162      if (!Data.getU64(&Offset,163                        reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()),164                        Hdr->NumAddresses))165        return createStringError(std::errc::invalid_argument,166                                  "failed to read address table");167    }168    AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets);169 170    // Read the address info offsets.171    Offset = alignTo(Offset, 4);172    Swap->AddrInfoOffsets.resize(Hdr->NumAddresses);173    if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses))174      AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets);175    else176      return createStringError(std::errc::invalid_argument,177                               "failed to read address table");178    // Read the file table.179    const uint32_t NumFiles = Data.getU32(&Offset);180    if (NumFiles > 0) {181      Swap->Files.resize(NumFiles);182      if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2))183        Files = ArrayRef<FileEntry>(Swap->Files);184      else185        return createStringError(std::errc::invalid_argument,186                                 "failed to read file table");187    }188    // Get the string table.189    StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset,190                                                Hdr->StrtabSize);191    if (StrTab.Data.empty())192      return createStringError(std::errc::invalid_argument,193                               "failed to read string table");194  }195  return Error::success();196 197}198 199const Header &GsymReader::getHeader() const {200  // The only way to get a GsymReader is from GsymReader::openFile(...) or201  // GsymReader::copyBuffer() and the header must be valid and initialized to202  // a valid pointer value, so the assert below should not trigger.203  assert(Hdr);204  return *Hdr;205}206 207std::optional<uint64_t> GsymReader::getAddress(size_t Index) const {208  switch (Hdr->AddrOffSize) {209  case 1: return addressForIndex<uint8_t>(Index);210  case 2: return addressForIndex<uint16_t>(Index);211  case 4: return addressForIndex<uint32_t>(Index);212  case 8: return addressForIndex<uint64_t>(Index);213  }214  return std::nullopt;215}216 217std::optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {218  const auto NumAddrInfoOffsets = AddrInfoOffsets.size();219  if (Index < NumAddrInfoOffsets)220    return AddrInfoOffsets[Index];221  return std::nullopt;222}223 224Expected<uint64_t>225GsymReader::getAddressIndex(const uint64_t Addr) const {226  if (Addr >= Hdr->BaseAddress) {227    const uint64_t AddrOffset = Addr - Hdr->BaseAddress;228    std::optional<uint64_t> AddrOffsetIndex;229    switch (Hdr->AddrOffSize) {230    case 1:231      AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);232      break;233    case 2:234      AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);235      break;236    case 4:237      AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);238      break;239    case 8:240      AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);241      break;242    default:243      return createStringError(std::errc::invalid_argument,244                               "unsupported address offset size %u",245                               Hdr->AddrOffSize);246    }247    if (AddrOffsetIndex)248      return *AddrOffsetIndex;249  }250  return createStringError(std::errc::invalid_argument,251                           "address 0x%" PRIx64 " is not in GSYM", Addr);252 253}254 255llvm::Expected<DataExtractor>256GsymReader::getFunctionInfoDataForAddress(uint64_t Addr,257                                          uint64_t &FuncStartAddr) const {258  Expected<uint64_t> ExpectedAddrIdx = getAddressIndex(Addr);259  if (!ExpectedAddrIdx)260    return ExpectedAddrIdx.takeError();261  const uint64_t FirstAddrIdx = *ExpectedAddrIdx;262  // The AddrIdx is the first index of the function info entries that match263  // \a Addr. We need to iterate over all function info objects that start with264  // the same address until we find a range that contains \a Addr.265  std::optional<uint64_t> FirstFuncStartAddr;266  const size_t NumAddresses = getNumAddresses();267  for (uint64_t AddrIdx = FirstAddrIdx; AddrIdx < NumAddresses; ++AddrIdx) {268    auto ExpextedData = getFunctionInfoDataAtIndex(AddrIdx, FuncStartAddr);269    // If there was an error, return the error.270    if (!ExpextedData)271      return ExpextedData;272 273    // Remember the first function start address if it hasn't already been set.274    // If it is already valid, check to see if it matches the first function275    // start address and only continue if it matches.276    if (FirstFuncStartAddr.has_value()) {277      if (*FirstFuncStartAddr != FuncStartAddr)278        break; // Done with consecutive function entries with same address.279    } else {280      FirstFuncStartAddr = FuncStartAddr;281    }282    // Make sure the current function address ranges contains \a Addr.283    // Some symbols on Darwin don't have valid sizes, so if we run into a284    // symbol with zero size, then we have found a match for our address.285 286    // The first thing the encoding of a FunctionInfo object is the function287    // size.288    uint64_t Offset = 0;289    uint32_t FuncSize = ExpextedData->getU32(&Offset);290    if (FuncSize == 0 ||291        AddressRange(FuncStartAddr, FuncStartAddr + FuncSize).contains(Addr))292      return ExpextedData;293  }294  return createStringError(std::errc::invalid_argument,295                           "address 0x%" PRIx64 " is not in GSYM", Addr);296}297 298llvm::Expected<DataExtractor>299GsymReader::getFunctionInfoDataAtIndex(uint64_t AddrIdx,300                                       uint64_t &FuncStartAddr) const {301  if (AddrIdx >= getNumAddresses())302    return createStringError(std::errc::invalid_argument,303                             "invalid address index %" PRIu64, AddrIdx);304  const uint32_t AddrInfoOffset = AddrInfoOffsets[AddrIdx];305  assert((Endian == endianness::big || Endian == endianness::little) &&306         "Endian must be either big or little");307  StringRef Bytes = MemBuffer->getBuffer().substr(AddrInfoOffset);308  if (Bytes.empty())309    return createStringError(std::errc::invalid_argument,310                             "invalid address info offset 0x%" PRIx32,311                             AddrInfoOffset);312  std::optional<uint64_t> OptFuncStartAddr = getAddress(AddrIdx);313  if (!OptFuncStartAddr)314    return createStringError(std::errc::invalid_argument,315                             "failed to extract address[%" PRIu64 "]", AddrIdx);316  FuncStartAddr = *OptFuncStartAddr;317  return DataExtractor(Bytes, Endian == llvm::endianness::little, 4);318}319 320llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {321  uint64_t FuncStartAddr = 0;322  if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))323    return FunctionInfo::decode(*ExpectedData, FuncStartAddr);324  else325    return ExpectedData.takeError();326}327 328llvm::Expected<FunctionInfo>329GsymReader::getFunctionInfoAtIndex(uint64_t Idx) const {330  uint64_t FuncStartAddr = 0;331  if (auto ExpectedData = getFunctionInfoDataAtIndex(Idx, FuncStartAddr))332    return FunctionInfo::decode(*ExpectedData, FuncStartAddr);333  else334    return ExpectedData.takeError();335}336 337llvm::Expected<LookupResult>338GsymReader::lookup(uint64_t Addr,339                   std::optional<DataExtractor> *MergedFunctionsData) const {340  uint64_t FuncStartAddr = 0;341  if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))342    return FunctionInfo::lookup(*ExpectedData, *this, FuncStartAddr, Addr,343                                MergedFunctionsData);344  else345    return ExpectedData.takeError();346}347 348llvm::Expected<std::vector<LookupResult>>349GsymReader::lookupAll(uint64_t Addr) const {350  std::vector<LookupResult> Results;351  std::optional<DataExtractor> MergedFunctionsData;352 353  // First perform a lookup to get the primary function info result.354  auto MainResult = lookup(Addr, &MergedFunctionsData);355  if (!MainResult)356    return MainResult.takeError();357 358  // Add the main result as the first entry.359  Results.push_back(std::move(*MainResult));360 361  // Now process any merged functions data that was found during the lookup.362  if (MergedFunctionsData) {363    // Get data extractors for each merged function.364    auto ExpectedMergedFuncExtractors =365        MergedFunctionsInfo::getFuncsDataExtractors(*MergedFunctionsData);366    if (!ExpectedMergedFuncExtractors)367      return ExpectedMergedFuncExtractors.takeError();368 369    // Process each merged function data.370    for (DataExtractor &MergedData : *ExpectedMergedFuncExtractors) {371      if (auto FI = FunctionInfo::lookup(MergedData, *this,372                                         MainResult->FuncRange.start(), Addr)) {373        Results.push_back(std::move(*FI));374      } else {375        return FI.takeError();376      }377    }378  }379 380  return Results;381}382 383void GsymReader::dump(raw_ostream &OS) {384  const auto &Header = getHeader();385  // Dump the GSYM header.386  OS << Header << "\n";387  // Dump the address table.388  OS << "Address Table:\n";389  OS << "INDEX  OFFSET";390 391  switch (Hdr->AddrOffSize) {392  case 1: OS << "8 "; break;393  case 2: OS << "16"; break;394  case 4: OS << "32"; break;395  case 8: OS << "64"; break;396  default: OS << "??"; break;397  }398  OS << " (ADDRESS)\n";399  OS << "====== =============================== \n";400  for (uint32_t I = 0; I < Header.NumAddresses; ++I) {401    OS << format("[%4u] ", I);402    switch (Hdr->AddrOffSize) {403    case 1: OS << HEX8(getAddrOffsets<uint8_t>()[I]); break;404    case 2: OS << HEX16(getAddrOffsets<uint16_t>()[I]); break;405    case 4: OS << HEX32(getAddrOffsets<uint32_t>()[I]); break;406    case 8: OS << HEX32(getAddrOffsets<uint64_t>()[I]); break;407    default: break;408    }409    OS << " (" << HEX64(*getAddress(I)) << ")\n";410  }411  // Dump the address info offsets table.412  OS << "\nAddress Info Offsets:\n";413  OS << "INDEX  Offset\n";414  OS << "====== ==========\n";415  for (uint32_t I = 0; I < Header.NumAddresses; ++I)416    OS << format("[%4u] ", I) << HEX32(AddrInfoOffsets[I]) << "\n";417  // Dump the file table.418  OS << "\nFiles:\n";419  OS << "INDEX  DIRECTORY  BASENAME   PATH\n";420  OS << "====== ========== ========== ==============================\n";421  for (uint32_t I = 0; I < Files.size(); ++I) {422    OS << format("[%4u] ", I) << HEX32(Files[I].Dir) << ' '423       << HEX32(Files[I].Base) << ' ';424    dump(OS, getFile(I));425    OS << "\n";426  }427  OS << "\n" << StrTab << "\n";428 429  for (uint32_t I = 0; I < Header.NumAddresses; ++I) {430    OS << "FunctionInfo @ " << HEX32(AddrInfoOffsets[I]) << ": ";431    if (auto FI = getFunctionInfoAtIndex(I))432      dump(OS, *FI);433    else434      logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:");435  }436}437 438void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI,439                      uint32_t Indent) {440  OS.indent(Indent);441  OS << FI.Range << " \"" << getString(FI.Name) << "\"\n";442  if (FI.OptLineTable)443    dump(OS, *FI.OptLineTable, Indent);444  if (FI.Inline)445    dump(OS, *FI.Inline, Indent);446 447  if (FI.CallSites)448    dump(OS, *FI.CallSites, Indent);449 450  if (FI.MergedFunctions) {451    assert(Indent == 0 && "MergedFunctionsInfo should only exist at top level");452    dump(OS, *FI.MergedFunctions);453  }454}455 456void GsymReader::dump(raw_ostream &OS, const MergedFunctionsInfo &MFI) {457  for (uint32_t inx = 0; inx < MFI.MergedFunctions.size(); inx++) {458    OS << "++ Merged FunctionInfos[" << inx << "]:\n";459    dump(OS, MFI.MergedFunctions[inx], 4);460  }461}462 463void GsymReader::dump(raw_ostream &OS, const CallSiteInfo &CSI) {464  OS << HEX16(CSI.ReturnOffset);465 466  std::string Flags;467  auto addFlag = [&](const char *Flag) {468    if (!Flags.empty())469      Flags += " | ";470    Flags += Flag;471  };472 473  if (CSI.Flags == CallSiteInfo::Flags::None)474    Flags = "None";475  else {476    if (CSI.Flags & CallSiteInfo::Flags::InternalCall)477      addFlag("InternalCall");478 479    if (CSI.Flags & CallSiteInfo::Flags::ExternalCall)480      addFlag("ExternalCall");481  }482  OS << " Flags[" << Flags << "]";483 484  if (!CSI.MatchRegex.empty()) {485    OS << " MatchRegex[";486    for (uint32_t i = 0; i < CSI.MatchRegex.size(); ++i) {487      if (i > 0)488        OS << ";";489      OS << getString(CSI.MatchRegex[i]);490    }491    OS << "]";492  }493}494 495void GsymReader::dump(raw_ostream &OS, const CallSiteInfoCollection &CSIC,496                      uint32_t Indent) {497  OS.indent(Indent);498  OS << "CallSites (by relative return offset):\n";499  for (const auto &CS : CSIC.CallSites) {500    OS.indent(Indent);501    OS << "  ";502    dump(OS, CS);503    OS << "\n";504  }505}506 507void GsymReader::dump(raw_ostream &OS, const LineTable &LT, uint32_t Indent) {508  OS.indent(Indent);509  OS << "LineTable:\n";510  for (auto &LE: LT) {511    OS.indent(Indent);512    OS << "  " << HEX64(LE.Addr) << ' ';513    if (LE.File)514      dump(OS, getFile(LE.File));515    OS << ':' << LE.Line << '\n';516  }517}518 519void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {520  if (Indent == 0)521    OS << "InlineInfo:\n";522  else523    OS.indent(Indent);524  OS << II.Ranges << ' ' << getString(II.Name);525  if (II.CallFile != 0) {526    if (auto File = getFile(II.CallFile)) {527      OS << " called from ";528      dump(OS, File);529      OS << ':' << II.CallLine;530    }531  }532  OS << '\n';533  for (const auto &ChildII: II.Children)534    dump(OS, ChildII, Indent + 2);535}536 537void GsymReader::dump(raw_ostream &OS, std::optional<FileEntry> FE) {538  if (FE) {539    // IF we have the file from index 0, then don't print anything540    if (FE->Dir == 0 && FE->Base == 0)541      return;542    StringRef Dir = getString(FE->Dir);543    StringRef Base = getString(FE->Base);544    if (!Dir.empty()) {545      OS << Dir;546      if (Dir.contains('\\') && !Dir.contains('/'))547        OS << '\\';548      else549        OS << '/';550    }551    if (!Base.empty()) {552      OS << Base;553    }554    if (!Dir.empty() || !Base.empty())555      return;556  }557  OS << "<invalid-file>";558}559