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