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