brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.7 KiB · 4c08b57 Raw
718 lines · cpp
1//===-- gsymutil.cpp - GSYM dumping and creation utility for llvm ---------===//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/ADT/STLExtras.h"10#include "llvm/DebugInfo/DIContext.h"11#include "llvm/DebugInfo/DWARF/DWARFContext.h"12#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"13#include "llvm/Object/Archive.h"14#include "llvm/Object/ELFObjectFile.h"15#include "llvm/Object/MachOUniversal.h"16#include "llvm/Object/ObjectFile.h"17#include "llvm/Option/ArgList.h"18#include "llvm/Option/Option.h"19#include "llvm/Support/CommandLine.h"20#include "llvm/Support/Debug.h"21#include "llvm/Support/Format.h"22#include "llvm/Support/JSON.h"23#include "llvm/Support/LLVMDriver.h"24#include "llvm/Support/ManagedStatic.h"25#include "llvm/Support/MemoryBuffer.h"26#include "llvm/Support/PrettyStackTrace.h"27#include "llvm/Support/Regex.h"28#include "llvm/Support/Signals.h"29#include "llvm/Support/TargetSelect.h"30#include "llvm/Support/raw_ostream.h"31#include "llvm/TargetParser/Triple.h"32#include <algorithm>33#include <cstring>34#include <inttypes.h>35#include <iostream>36#include <optional>37#include <string>38#include <system_error>39#include <vector>40 41#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"42#include "llvm/DebugInfo/GSYM/FunctionInfo.h"43#include "llvm/DebugInfo/GSYM/GsymCreator.h"44#include "llvm/DebugInfo/GSYM/GsymReader.h"45#include "llvm/DebugInfo/GSYM/InlineInfo.h"46#include "llvm/DebugInfo/GSYM/LookupResult.h"47#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h"48#include "llvm/DebugInfo/GSYM/OutputAggregator.h"49 50using namespace llvm;51using namespace gsym;52using namespace object;53 54/// @}55/// Command line options.56/// @{57 58using namespace llvm::opt;59enum ID {60  OPT_INVALID = 0, // This is not an option ID.61#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),62#include "Opts.inc"63#undef OPTION64};65 66#define OPTTABLE_STR_TABLE_CODE67#include "Opts.inc"68#undef OPTTABLE_STR_TABLE_CODE69 70#define OPTTABLE_PREFIXES_TABLE_CODE71#include "Opts.inc"72#undef OPTTABLE_PREFIXES_TABLE_CODE73 74const opt::OptTable::Info InfoTable[] = {75#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),76#include "Opts.inc"77#undef OPTION78};79 80class GSYMUtilOptTable : public llvm::opt::GenericOptTable {81public:82  GSYMUtilOptTable()83      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {84    setGroupedShortOptions(true);85  }86};87 88static bool Verbose;89static std::vector<std::string> InputFilenames;90static std::string ConvertFilename;91static std::vector<std::string> ArchFilters;92static std::string OutputFilename;93static std::string JsonSummaryFile;94static bool Verify;95static unsigned NumThreads;96static uint64_t SegmentSize;97static bool Quiet;98static std::vector<uint64_t> LookupAddresses;99static bool LookupAddressesFromStdin;100static bool UseMergedFunctions = false;101static bool LoadDwarfCallSites = false;102static std::string CallSiteYamlPath;103static std::vector<std::string> MergedFunctionsFilters;104 105static void parseArgs(int argc, char **argv) {106  GSYMUtilOptTable Tbl;107  llvm::StringRef ToolName = argv[0];108  llvm::BumpPtrAllocator A;109  llvm::StringSaver Saver{A};110  llvm::opt::InputArgList Args =111      Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {112        llvm::errs() << Msg << '\n';113        std::exit(1);114      });115  if (Args.hasArg(OPT_help)) {116    const char *Overview =117        "A tool for dumping, searching and creating GSYM files.\n\n"118        "Specify one or more GSYM paths as arguments to dump all of the "119        "information in each GSYM file.\n"120        "Specify a single GSYM file along with one or more --lookup options to "121        "lookup addresses within that GSYM file.\n"122        "Use the --convert option to specify a file with option --out-file "123        "option to convert to GSYM format.\n";124 125    Tbl.printHelp(llvm::outs(), "llvm-gsymutil [options] <input GSYM files>",126                  Overview);127    std::exit(0);128  }129  if (Args.hasArg(OPT_version)) {130    llvm::outs() << ToolName << '\n';131    cl::PrintVersionMessage();132    std::exit(0);133  }134 135  Verbose = Args.hasArg(OPT_verbose);136 137  for (const llvm::opt::Arg *A : Args.filtered(OPT_INPUT))138    InputFilenames.emplace_back(A->getValue());139 140  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_convert_EQ))141    ConvertFilename = A->getValue();142 143  for (const llvm::opt::Arg *A : Args.filtered(OPT_arch_EQ))144    ArchFilters.emplace_back(A->getValue());145 146  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_out_file_EQ))147    OutputFilename = A->getValue();148 149  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_json_summary_file_EQ))150    JsonSummaryFile = A->getValue();151 152  Verify = Args.hasArg(OPT_verify);153 154  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_num_threads_EQ)) {155    StringRef S{A->getValue()};156    if (!llvm::to_integer(S, NumThreads, 0)) {157      llvm::errs() << ToolName << ": for the --num-threads option: '" << S158                   << "' value invalid for uint argument!\n";159      std::exit(1);160    }161  }162 163  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_segment_size_EQ)) {164    StringRef S{A->getValue()};165    if (!llvm::to_integer(S, SegmentSize, 0)) {166      llvm::errs() << ToolName << ": for the --segment-size option: '" << S167                   << "' value invalid for uint argument!\n";168      std::exit(1);169    }170  }171 172  Quiet = Args.hasArg(OPT_quiet);173 174  for (const llvm::opt::Arg *A : Args.filtered(OPT_address_EQ)) {175    StringRef S{A->getValue()};176    if (!llvm::to_integer(S, LookupAddresses.emplace_back(), 0)) {177      llvm::errs() << ToolName << ": for the --address option: '" << S178                   << "' value invalid for uint argument!\n";179      std::exit(1);180    }181  }182 183  LookupAddressesFromStdin = Args.hasArg(OPT_addresses_from_stdin);184  UseMergedFunctions = Args.hasArg(OPT_merged_functions);185 186  if (Args.hasArg(OPT_callsites_yaml_file_EQ)) {187    CallSiteYamlPath = Args.getLastArgValue(OPT_callsites_yaml_file_EQ);188    if (CallSiteYamlPath.empty()) {189      llvm::errs()190          << ToolName191          << ": --callsites-yaml-file option requires a non-empty argument.\n";192      std::exit(1);193    }194  }195 196  LoadDwarfCallSites = Args.hasArg(OPT_dwarf_callsites);197 198  for (const llvm::opt::Arg *A :199       Args.filtered(OPT_merged_functions_filter_EQ)) {200    MergedFunctionsFilters.push_back(A->getValue());201    // Validate the filter is only used with correct flags202    if (LookupAddresses.empty() && !LookupAddressesFromStdin) {203      llvm::errs() << ToolName204                   << ": --merged-functions-filter can only be used with "205                      "--address/--addresses-from-stdin\n";206      std::exit(1);207    }208    if (!UseMergedFunctions) {209      llvm::errs()210          << ToolName211          << ": --merged-functions-filter requires --merged-functions\n";212      std::exit(1);213    }214  }215}216 217/// @}218//===----------------------------------------------------------------------===//219 220static void error(Error Err) {221  if (!Err)222    return;223  WithColor::error() << toString(std::move(Err)) << "\n";224  exit(1);225}226 227static void error(StringRef Prefix, llvm::Error Err) {228  if (!Err)229    return;230  errs() << Prefix << ": " << Err << "\n";231  consumeError(std::move(Err));232  exit(1);233}234 235static void error(StringRef Prefix, std::error_code EC) {236  if (!EC)237    return;238  errs() << Prefix << ": " << EC.message() << "\n";239  exit(1);240}241 242static uint32_t getCPUType(MachOObjectFile &MachO) {243  if (MachO.is64Bit())244    return MachO.getHeader64().cputype;245  else246    return MachO.getHeader().cputype;247}248 249/// Return true if the object file has not been filtered by an --arch option.250static bool filterArch(MachOObjectFile &Obj) {251  if (ArchFilters.empty())252    return true;253 254  Triple ObjTriple(Obj.getArchTriple());255  StringRef ObjArch = ObjTriple.getArchName();256 257  for (StringRef Arch : ArchFilters) {258    // Match name.259    if (Arch == ObjArch)260      return true;261 262    // Match architecture number.263    unsigned Value;264    if (!Arch.getAsInteger(0, Value))265      if (Value == getCPUType(Obj))266        return true;267  }268  return false;269}270 271/// Determine the virtual address that is considered the base address of an ELF272/// object file.273///274/// The base address of an ELF file is the "p_vaddr" of the first program275/// header whose "p_type" is PT_LOAD.276///277/// \param ELFFile An ELF object file we will search.278///279/// \returns A valid image base address if we are able to extract one.280template <class ELFT>281static std::optional<uint64_t>282getImageBaseAddress(const object::ELFFile<ELFT> &ELFFile) {283  auto PhdrRangeOrErr = ELFFile.program_headers();284  if (!PhdrRangeOrErr) {285    consumeError(PhdrRangeOrErr.takeError());286    return std::nullopt;287  }288  for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)289    if (Phdr.p_type == ELF::PT_LOAD)290      return (uint64_t)Phdr.p_vaddr;291  return std::nullopt;292}293 294/// Determine the virtual address that is considered the base address of mach-o295/// object file.296///297/// The base address of a mach-o file is the vmaddr of the  "__TEXT" segment.298///299/// \param MachO A mach-o object file we will search.300///301/// \returns A valid image base address if we are able to extract one.302static std::optional<uint64_t>303getImageBaseAddress(const object::MachOObjectFile *MachO) {304  for (const auto &Command : MachO->load_commands()) {305    if (Command.C.cmd == MachO::LC_SEGMENT) {306      MachO::segment_command SLC = MachO->getSegmentLoadCommand(Command);307      StringRef SegName = SLC.segname;308      if (SegName == "__TEXT")309        return SLC.vmaddr;310    } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {311      MachO::segment_command_64 SLC = MachO->getSegment64LoadCommand(Command);312      StringRef SegName = SLC.segname;313      if (SegName == "__TEXT")314        return SLC.vmaddr;315    }316  }317  return std::nullopt;318}319 320/// Determine the virtual address that is considered the base address of an321/// object file.322///323/// Since GSYM files are used for symbolication, many clients will need to324/// easily adjust addresses they find in stack traces so the lookups happen325/// on unslid addresses from the original object file. If the base address of326/// a GSYM file is set to the base address of the image, then this address327/// adjusting is much easier.328///329/// \param Obj An object file we will search.330///331/// \returns A valid image base address if we are able to extract one.332static std::optional<uint64_t> getImageBaseAddress(object::ObjectFile &Obj) {333  if (const auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj))334    return getImageBaseAddress(MachO);335  else if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(&Obj))336    return getImageBaseAddress(ELFObj->getELFFile());337  else if (const auto *ELFObj = dyn_cast<object::ELF32BEObjectFile>(&Obj))338    return getImageBaseAddress(ELFObj->getELFFile());339  else if (const auto *ELFObj = dyn_cast<object::ELF64LEObjectFile>(&Obj))340    return getImageBaseAddress(ELFObj->getELFFile());341  else if (const auto *ELFObj = dyn_cast<object::ELF64BEObjectFile>(&Obj))342    return getImageBaseAddress(ELFObj->getELFFile());343  return std::nullopt;344}345 346static llvm::Error handleObjectFile(ObjectFile &Obj, const std::string &OutFile,347                                    OutputAggregator &Out) {348  auto ThreadCount =349      NumThreads > 0 ? NumThreads : std::thread::hardware_concurrency();350 351  GsymCreator Gsym(Quiet);352 353  // See if we can figure out the base address for a given object file, and if354  // we can, then set the base address to use to this value. This will ease355  // symbolication since clients can slide the GSYM lookup addresses by using356  // the load bias of the shared library.357  if (auto ImageBaseAddr = getImageBaseAddress(Obj))358    Gsym.setBaseAddress(*ImageBaseAddr);359 360  // We need to know where the valid sections are that contain instructions.361  // See header documentation for DWARFTransformer::SetValidTextRanges() for362  // defails.363  AddressRanges TextRanges;364  for (const object::SectionRef &Sect : Obj.sections()) {365    if (!Sect.isText())366      continue;367    const uint64_t Size = Sect.getSize();368    if (Size == 0)369      continue;370    const uint64_t StartAddr = Sect.getAddress();371    TextRanges.insert(AddressRange(StartAddr, StartAddr + Size));372  }373 374  // Make sure there is DWARF to convert first.375  std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(376      Obj,377      /*RelocAction=*/DWARFContext::ProcessDebugRelocations::Process,378      nullptr,379      /*DWPName=*/"",380      /*RecoverableErrorHandler=*/WithColor::defaultErrorHandler,381      /*WarningHandler=*/WithColor::defaultWarningHandler,382      /*ThreadSafe*/true);383  if (!DICtx)384    return createStringError(std::errc::invalid_argument,385                             "unable to create DWARF context");386 387  // Make a DWARF transformer object and populate the ranges of the code388  // so we don't end up adding invalid functions to GSYM data.389  bool IsMachO = dyn_cast<object::MachOObjectFile>(&Obj) != nullptr;390 391  DwarfTransformer DT(*DICtx, Gsym, LoadDwarfCallSites, IsMachO);392  if (!TextRanges.empty())393    Gsym.SetValidTextRanges(TextRanges);394 395  // Convert all DWARF to GSYM.396  if (auto Err = DT.convert(ThreadCount, Out))397    return Err;398 399  // If enabled, merge functions with identical address ranges as merged400  // functions in the first FunctionInfo with that address range. Do this right401  // after loading the DWARF data so we don't have to deal with functions from402  // the symbol table.403  if (UseMergedFunctions)404    Gsym.prepareMergedFunctions(Out);405 406  // Get the UUID and convert symbol table to GSYM.407  if (auto Err = ObjectFileTransformer::convert(Obj, Out, Gsym))408    return Err;409 410  // If any call site YAML files were specified, load them now.411  if (!CallSiteYamlPath.empty())412    if (auto Err = Gsym.loadCallSitesFromYAML(CallSiteYamlPath))413      return Err;414 415  // Finalize the GSYM to make it ready to save to disk. This will remove416  // duplicate FunctionInfo entries where we might have found an entry from417  // debug info and also a symbol table entry from the object file.418  if (auto Err = Gsym.finalize(Out))419    return Err;420 421  // Save the GSYM file to disk.422  llvm::endianness Endian = Obj.makeTriple().isLittleEndian()423                                ? llvm::endianness::little424                                : llvm::endianness::big;425 426  std::optional<uint64_t> OptSegmentSize;427  if (SegmentSize > 0)428    OptSegmentSize = SegmentSize;429  if (auto Err = Gsym.save(OutFile, Endian, OptSegmentSize))430    return Err;431 432  // Verify the DWARF if requested. This will ensure all the info in the DWARF433  // can be looked up in the GSYM and that all lookups get matching data.434  if (Verify) {435    if (auto Err = DT.verify(OutFile, Out))436      return Err;437  }438 439  return Error::success();440}441 442static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer,443                                const std::string &OutFile,444                                OutputAggregator &Out) {445  Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);446  error(Filename, errorToErrorCode(BinOrErr.takeError()));447 448  if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {449    Triple ObjTriple(Obj->makeTriple());450    auto ArchName = ObjTriple.getArchName();451    outs() << "Output file (" << ArchName << "): " << OutFile << "\n";452    if (auto Err = handleObjectFile(*Obj, OutFile, Out))453      return Err;454  } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) {455    // Iterate over all contained architectures and filter out any that were456    // not specified with the "--arch <arch>" option. If the --arch option was457    // not specified on the command line, we will process all architectures.458    std::vector<std::unique_ptr<MachOObjectFile>> FilterObjs;459    for (auto &ObjForArch : Fat->objects()) {460      if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {461        auto &Obj = **MachOOrErr;462        if (filterArch(Obj))463          FilterObjs.emplace_back(MachOOrErr->release());464      } else {465        error(Filename, MachOOrErr.takeError());466      }467    }468    if (FilterObjs.empty())469      error(Filename, createStringError(std::errc::invalid_argument,470                                        "no matching architectures found"));471 472    // Now handle each architecture we need to convert.473    for (auto &Obj : FilterObjs) {474      Triple ObjTriple(Obj->getArchTriple());475      auto ArchName = ObjTriple.getArchName();476      std::string ArchOutFile(OutFile);477      // If we are only handling a single architecture, then we will use the478      // normal output file. If we are handling multiple architectures append479      // the architecture name to the end of the out file path so that we480      // don't overwrite the previous architecture's gsym file.481      if (FilterObjs.size() > 1) {482        ArchOutFile.append(1, '.');483        ArchOutFile.append(ArchName.str());484      }485      outs() << "Output file (" << ArchName << "): " << ArchOutFile << "\n";486      if (auto Err = handleObjectFile(*Obj, ArchOutFile, Out))487        return Err;488    }489  }490  return Error::success();491}492 493static llvm::Error handleFileConversionToGSYM(StringRef Filename,494                                              const std::string &OutFile,495                                              OutputAggregator &Out) {496  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =497      MemoryBuffer::getFileOrSTDIN(Filename);498  error(Filename, BuffOrErr.getError());499  std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());500  return handleBuffer(Filename, *Buffer, OutFile, Out);501}502 503static llvm::Error convertFileToGSYM(OutputAggregator &Out) {504  // Expand any .dSYM bundles to the individual object files contained therein.505  std::vector<std::string> Objects;506  std::string OutFile = OutputFilename;507  if (OutFile.empty()) {508    OutFile = ConvertFilename;509    OutFile += ".gsym";510  }511 512  Out << "Input file: " << ConvertFilename << "\n";513 514  if (auto DsymObjectsOrErr =515          MachOObjectFile::findDsymObjectMembers(ConvertFilename)) {516    if (DsymObjectsOrErr->empty())517      Objects.push_back(ConvertFilename);518    else519      llvm::append_range(Objects, *DsymObjectsOrErr);520  } else {521    error(DsymObjectsOrErr.takeError());522  }523 524  for (StringRef Object : Objects)525    if (Error Err = handleFileConversionToGSYM(Object, OutFile, Out))526      return Err;527  return Error::success();528}529 530static void doLookup(GsymReader &Gsym, uint64_t Addr, raw_ostream &OS) {531  if (UseMergedFunctions) {532    if (auto Results = Gsym.lookupAll(Addr)) {533      // If we have filters, count matching results first534      size_t NumMatching = Results->size();535      if (!MergedFunctionsFilters.empty()) {536        NumMatching = 0;537        for (const auto &Result : *Results) {538          bool Matches = false;539          for (const auto &Filter : MergedFunctionsFilters) {540            Regex Pattern(Filter);541            if (Pattern.match(Result.FuncName)) {542              Matches = true;543              break;544            }545          }546          if (Matches)547            NumMatching++;548        }549      }550 551      OS << "Found " << NumMatching << " function"552         << (NumMatching != 1 ? "s" : "") << " at address " << HEX64(Addr)553         << ":\n";554 555      for (size_t i = 0; i < Results->size(); ++i) {556        // Skip if doesn't match any filter557        if (!MergedFunctionsFilters.empty()) {558          bool Matches = false;559          for (const auto &Filter : MergedFunctionsFilters) {560            Regex Pattern(Filter);561            if (Pattern.match(Results->at(i).FuncName)) {562              Matches = true;563              break;564            }565          }566          if (!Matches)567            continue;568        }569 570        OS << "   " << Results->at(i);571 572        if (i != Results->size() - 1)573          OS << "\n";574      }575    }576  } else { /* UseMergedFunctions == false */577    if (auto Result = Gsym.lookup(Addr)) {578      // If verbose is enabled dump the full function info for the address.579      if (Verbose) {580        if (auto FI = Gsym.getFunctionInfo(Addr)) {581          OS << "FunctionInfo for " << HEX64(Addr) << ":\n";582          Gsym.dump(OS, *FI);583          OS << "\nLookupResult for " << HEX64(Addr) << ":\n";584        }585      }586      // Don't print call site info if --merged-functions is not specified.587      Result->CallSiteFuncRegex.clear();588      OS << Result.get();589    } else {590      if (Verbose)591        OS << "\nLookupResult for " << HEX64(Addr) << ":\n";592      OS << HEX64(Addr) << ": ";593      logAllUnhandledErrors(Result.takeError(), OS, "error: ");594    }595    if (Verbose)596      OS << "\n";597  }598}599 600int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {601  // Print a stack trace if we signal out.602  sys::PrintStackTraceOnErrorSignal(argv[0]);603  PrettyStackTraceProgram X(argc, argv);604  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.605 606  llvm::InitializeAllTargets();607 608  parseArgs(argc, argv);609 610  raw_ostream &OS = outs();611 612  OutputAggregator Aggregation(&OS);613  if (!ConvertFilename.empty()) {614    // Convert DWARF to GSYM615    if (!InputFilenames.empty()) {616      OS << "error: no input files can be specified when using the --convert "617            "option.\n";618      return 1;619    }620    // Call error() if we have an error and it will exit with a status of 1621    if (auto Err = convertFileToGSYM(Aggregation))622      error("DWARF conversion failed: ", std::move(Err));623 624    // Report the errors from aggregator:625    Aggregation.EnumerateResults([&](StringRef category, unsigned count) {626      OS << category << " occurred " << count << " time(s)\n";627    });628    if (!JsonSummaryFile.empty()) {629      std::error_code EC;630      raw_fd_ostream JsonStream(JsonSummaryFile, EC, sys::fs::OF_Text);631      if (EC) {632        OS << "error opening aggregate error json file '" << JsonSummaryFile633           << "' for writing: " << EC.message() << '\n';634        return 1;635      }636 637      llvm::json::Object Categories;638      uint64_t ErrorCount = 0;639      Aggregation.EnumerateResults([&](StringRef Category, unsigned Count) {640        llvm::json::Object Val;641        Val.try_emplace("count", Count);642        Categories.try_emplace(Category, std::move(Val));643        ErrorCount += Count;644      });645      llvm::json::Object RootNode;646      RootNode.try_emplace("error-categories", std::move(Categories));647      RootNode.try_emplace("error-count", ErrorCount);648 649      JsonStream << llvm::json::Value(std::move(RootNode));650    }651    return 0;652  }653 654  if (LookupAddressesFromStdin) {655    if (!LookupAddresses.empty() || !InputFilenames.empty()) {656      OS << "error: no input files or addresses can be specified when using "657            "the --addresses-from-stdin "658            "option.\n";659      return 1;660    }661 662    std::string InputLine;663    std::string CurrentGSYMPath;664    std::optional<Expected<GsymReader>> CurrentGsym;665 666    while (std::getline(std::cin, InputLine)) {667      // Strip newline characters.668      std::string StrippedInputLine(InputLine);669      llvm::erase_if(StrippedInputLine,670                     [](char c) { return c == '\r' || c == '\n'; });671 672      StringRef AddrStr, GSYMPath;673      std::tie(AddrStr, GSYMPath) =674          llvm::StringRef{StrippedInputLine}.split(' ');675 676      if (GSYMPath != CurrentGSYMPath) {677        CurrentGsym = GsymReader::openFile(GSYMPath);678        if (!*CurrentGsym)679          error(GSYMPath, CurrentGsym->takeError());680        CurrentGSYMPath = GSYMPath;681      }682 683      uint64_t Addr;684      if (AddrStr.getAsInteger(0, Addr)) {685        OS << "error: invalid address " << AddrStr686           << ", expected: Address GsymFile.\n";687        return 1;688      }689 690      doLookup(**CurrentGsym, Addr, OS);691 692      OS << "\n";693      OS.flush();694    }695 696    return EXIT_SUCCESS;697  }698 699  // Dump or access data inside GSYM files700  for (const auto &GSYMPath : InputFilenames) {701    auto Gsym = GsymReader::openFile(GSYMPath);702    if (!Gsym)703      error(GSYMPath, Gsym.takeError());704 705    if (LookupAddresses.empty()) {706      Gsym->dump(outs());707      continue;708    }709 710    // Lookup an address in a GSYM file and print any matches.711    OS << "Looking up addresses in \"" << GSYMPath << "\":\n";712    for (auto Addr : LookupAddresses) {713      doLookup(*Gsym, Addr, OS);714    }715  }716  return EXIT_SUCCESS;717}718