xref: /llvm-project/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp (revision 4a1c33d34c31893aa781ac43285ae2100a540fd4)
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