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
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 OPTION
64};
65
66#define OPTTABLE_STR_TABLE_CODE
67#include "Opts.inc"
68#undef OPTTABLE_STR_TABLE_CODE
69
70#define OPTTABLE_PREFIXES_TABLE_CODE
71#include "Opts.inc"
72#undef OPTTABLE_PREFIXES_TABLE_CODE
73
74const opt::OptTable::Info InfoTable[] = {
75#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
76#include "Opts.inc"
77#undef OPTION
78};
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: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) {
112 llvm::errs() << Msg << '\n';
113 std::exit(status: 1);
114 });
115 if (Args.hasArg(Ids: 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(OS&: llvm::outs(), Usage: "llvm-gsymutil [options] <input GSYM files>",
126 Title: Overview);
127 std::exit(status: 0);
128 }
129 if (Args.hasArg(Ids: OPT_version)) {
130 llvm::outs() << ToolName << '\n';
131 cl::PrintVersionMessage();
132 std::exit(status: 0);
133 }
134
135 Verbose = Args.hasArg(Ids: OPT_verbose);
136
137 for (const llvm::opt::Arg *A : Args.filtered(Ids: OPT_INPUT))
138 InputFilenames.emplace_back(args: A->getValue());
139
140 if (const llvm::opt::Arg *A = Args.getLastArg(Ids: OPT_convert_EQ))
141 ConvertFilename = A->getValue();
142
143 for (const llvm::opt::Arg *A : Args.filtered(Ids: OPT_arch_EQ))
144 ArchFilters.emplace_back(args: A->getValue());
145
146 if (const llvm::opt::Arg *A = Args.getLastArg(Ids: OPT_out_file_EQ))
147 OutputFilename = A->getValue();
148
149 if (const llvm::opt::Arg *A = Args.getLastArg(Ids: OPT_json_summary_file_EQ))
150 JsonSummaryFile = A->getValue();
151
152 Verify = Args.hasArg(Ids: OPT_verify);
153
154 if (const llvm::opt::Arg *A = Args.getLastArg(Ids: OPT_num_threads_EQ)) {
155 StringRef S{A->getValue()};
156 if (!llvm::to_integer(S, Num&: NumThreads, Base: 0)) {
157 llvm::errs() << ToolName << ": for the --num-threads option: '" << S
158 << "' value invalid for uint argument!\n";
159 std::exit(status: 1);
160 }
161 }
162
163 if (const llvm::opt::Arg *A = Args.getLastArg(Ids: OPT_segment_size_EQ)) {
164 StringRef S{A->getValue()};
165 if (!llvm::to_integer(S, Num&: SegmentSize, Base: 0)) {
166 llvm::errs() << ToolName << ": for the --segment-size option: '" << S
167 << "' value invalid for uint argument!\n";
168 std::exit(status: 1);
169 }
170 }
171
172 Quiet = Args.hasArg(Ids: OPT_quiet);
173
174 for (const llvm::opt::Arg *A : Args.filtered(Ids: OPT_address_EQ)) {
175 StringRef S{A->getValue()};
176 if (!llvm::to_integer(S, Num&: LookupAddresses.emplace_back(), Base: 0)) {
177 llvm::errs() << ToolName << ": for the --address option: '" << S
178 << "' value invalid for uint argument!\n";
179 std::exit(status: 1);
180 }
181 }
182
183 LookupAddressesFromStdin = Args.hasArg(Ids: OPT_addresses_from_stdin);
184 UseMergedFunctions = Args.hasArg(Ids: OPT_merged_functions);
185
186 if (Args.hasArg(Ids: OPT_callsites_yaml_file_EQ)) {
187 CallSiteYamlPath = Args.getLastArgValue(Id: OPT_callsites_yaml_file_EQ);
188 if (CallSiteYamlPath.empty()) {
189 llvm::errs()
190 << ToolName
191 << ": --callsites-yaml-file option requires a non-empty argument.\n";
192 std::exit(status: 1);
193 }
194 }
195
196 LoadDwarfCallSites = Args.hasArg(Ids: OPT_dwarf_callsites);
197
198 for (const llvm::opt::Arg *A :
199 Args.filtered(Ids: OPT_merged_functions_filter_EQ)) {
200 MergedFunctionsFilters.push_back(x: A->getValue());
201 // Validate the filter is only used with correct flags
202 if (LookupAddresses.empty() && !LookupAddressesFromStdin) {
203 llvm::errs() << ToolName
204 << ": --merged-functions-filter can only be used with "
205 "--address/--addresses-from-stdin\n";
206 std::exit(status: 1);
207 }
208 if (!UseMergedFunctions) {
209 llvm::errs()
210 << ToolName
211 << ": --merged-functions-filter requires --merged-functions\n";
212 std::exit(status: 1);
213 }
214 }
215}
216
217/// @}
218//===----------------------------------------------------------------------===//
219
220static void error(Error Err) {
221 if (!Err)
222 return;
223 WithColor::error() << toString(E: std::move(Err)) << "\n";
224 exit(status: 1);
225}
226
227static void error(StringRef Prefix, llvm::Error Err) {
228 if (!Err)
229 return;
230 errs() << Prefix << ": " << Err << "\n";
231 consumeError(Err: std::move(Err));
232 exit(status: 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(status: 1);
240}
241
242static uint32_t getCPUType(MachOObjectFile &MachO) {
243 if (MachO.is64Bit())
244 return MachO.getHeader64().cputype;
245 else
246 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(Radix: 0, Result&: Value))
265 if (Value == getCPUType(MachO&: Obj))
266 return true;
267 }
268 return false;
269}
270
271/// Determine the virtual address that is considered the base address of an ELF
272/// object file.
273///
274/// The base address of an ELF file is the "p_vaddr" of the first program
275/// 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-o
295/// 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(L: 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(L: 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 an
321/// object file.
322///
323/// Since GSYM files are used for symbolication, many clients will need to
324/// easily adjust addresses they find in stack traces so the lookups happen
325/// on unslid addresses from the original object file. If the base address of
326/// a GSYM file is set to the base address of the image, then this address
327/// 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>(Val: &Obj))
334 return getImageBaseAddress(MachO);
335 else if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(Val: &Obj))
336 return getImageBaseAddress(ELFFile: ELFObj->getELFFile());
337 else if (const auto *ELFObj = dyn_cast<object::ELF32BEObjectFile>(Val: &Obj))
338 return getImageBaseAddress(ELFFile: ELFObj->getELFFile());
339 else if (const auto *ELFObj = dyn_cast<object::ELF64LEObjectFile>(Val: &Obj))
340 return getImageBaseAddress(ELFFile: ELFObj->getELFFile());
341 else if (const auto *ELFObj = dyn_cast<object::ELF64BEObjectFile>(Val: &Obj))
342 return getImageBaseAddress(ELFFile: 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 if
354 // we can, then set the base address to use to this value. This will ease
355 // symbolication since clients can slide the GSYM lookup addresses by using
356 // 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() for
362 // 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(Range: 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 L: nullptr,
379 /*DWPName=*/"",
380 /*RecoverableErrorHandler=*/WithColor::defaultErrorHandler,
381 /*WarningHandler=*/WithColor::defaultWarningHandler,
382 /*ThreadSafe*/true);
383 if (!DICtx)
384 return createStringError(EC: std::errc::invalid_argument,
385 Fmt: "unable to create DWARF context");
386
387 // Make a DWARF transformer object and populate the ranges of the code
388 // so we don't end up adding invalid functions to GSYM data.
389 bool IsMachO = dyn_cast<object::MachOObjectFile>(Val: &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(NumThreads: ThreadCount, OS&: Out))
397 return Err;
398
399 // If enabled, merge functions with identical address ranges as merged
400 // functions in the first FunctionInfo with that address range. Do this right
401 // after loading the DWARF data so we don't have to deal with functions from
402 // 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, Output&: 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(YAMLFile: CallSiteYamlPath))
413 return Err;
414
415 // Finalize the GSYM to make it ready to save to disk. This will remove
416 // duplicate FunctionInfo entries where we might have found an entry from
417 // debug info and also a symbol table entry from the object file.
418 if (auto Err = Gsym.finalize(OS&: Out))
419 return Err;
420
421 // Save the GSYM file to disk.
422 llvm::endianness Endian = Obj.makeTriple().isLittleEndian()
423 ? llvm::endianness::little
424 : llvm::endianness::big;
425
426 std::optional<uint64_t> OptSegmentSize;
427 if (SegmentSize > 0)
428 OptSegmentSize = SegmentSize;
429 if (auto Err = Gsym.save(Path: OutFile, ByteOrder: Endian, SegmentSize: OptSegmentSize))
430 return Err;
431
432 // Verify the DWARF if requested. This will ensure all the info in the DWARF
433 // can be looked up in the GSYM and that all lookups get matching data.
434 if (Verify) {
435 if (auto Err = DT.verify(GsymPath: OutFile, OS&: 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(Source: Buffer);
446 error(Prefix: Filename, EC: errorToErrorCode(Err: BinOrErr.takeError()));
447
448 if (auto *Obj = dyn_cast<ObjectFile>(Val: 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&: *Obj, OutFile, Out))
453 return Err;
454 } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(Val: BinOrErr->get())) {
455 // Iterate over all contained architectures and filter out any that were
456 // not specified with the "--arch <arch>" option. If the --arch option was
457 // 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(args: MachOOrErr->release());
464 } else {
465 error(Prefix: Filename, Err: MachOOrErr.takeError());
466 }
467 }
468 if (FilterObjs.empty())
469 error(Prefix: Filename, Err: createStringError(EC: std::errc::invalid_argument,
470 Fmt: "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 the
478 // normal output file. If we are handling multiple architectures append
479 // the architecture name to the end of the out file path so that we
480 // don't overwrite the previous architecture's gsym file.
481 if (FilterObjs.size() > 1) {
482 ArchOutFile.append(n: 1, c: '.');
483 ArchOutFile.append(str: ArchName.str());
484 }
485 outs() << "Output file (" << ArchName << "): " << ArchOutFile << "\n";
486 if (auto Err = handleObjectFile(Obj&: *Obj, OutFile: 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(Prefix: Filename, EC: BuffOrErr.getError());
499 std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
500 return handleBuffer(Filename, Buffer: *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(Path: ConvertFilename)) {
516 if (DsymObjectsOrErr->empty())
517 Objects.push_back(x: ConvertFilename);
518 else
519 llvm::append_range(C&: Objects, R&: *DsymObjectsOrErr);
520 } else {
521 error(Err: DsymObjectsOrErr.takeError());
522 }
523
524 for (StringRef Object : Objects)
525 if (Error Err = handleFileConversionToGSYM(Filename: 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 first
534 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(String: 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 filter
557 if (!MergedFunctionsFilters.empty()) {
558 bool Matches = false;
559 for (const auto &Filter : MergedFunctionsFilters) {
560 Regex Pattern(Filter);
561 if (Pattern.match(String: Results->at(n: i).FuncName)) {
562 Matches = true;
563 break;
564 }
565 }
566 if (!Matches)
567 continue;
568 }
569
570 OS << " " << Results->at(n: 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: *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(E: Result.takeError(), OS, ErrorBanner: "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(Argv0: 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 GSYM
615 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 1
621 if (auto Err = convertFileToGSYM(Out&: Aggregation))
622 error(Prefix: "DWARF conversion failed: ", Err: std::move(Err));
623
624 // Report the errors from aggregator:
625 Aggregation.EnumerateResults(handleCounts: [&](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 '" << JsonSummaryFile
633 << "' for writing: " << EC.message() << '\n';
634 return 1;
635 }
636
637 llvm::json::Object Categories;
638 uint64_t ErrorCount = 0;
639 Aggregation.EnumerateResults(handleCounts: [&](StringRef Category, unsigned Count) {
640 llvm::json::Object Val;
641 Val.try_emplace(K: "count", Args&: Count);
642 Categories.try_emplace(K: Category, Args: std::move(Val));
643 ErrorCount += Count;
644 });
645 llvm::json::Object RootNode;
646 RootNode.try_emplace(K: "error-categories", Args: std::move(Categories));
647 RootNode.try_emplace(K: "error-count", Args&: 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(is&: std::cin, str&: InputLine)) {
667 // Strip newline characters.
668 std::string StrippedInputLine(InputLine);
669 llvm::erase_if(C&: StrippedInputLine,
670 P: [](char c) { return c == '\r' || c == '\n'; });
671
672 StringRef AddrStr, GSYMPath;
673 std::tie(args&: AddrStr, args&: GSYMPath) =
674 llvm::StringRef{StrippedInputLine}.split(Separator: ' ');
675
676 if (GSYMPath != CurrentGSYMPath) {
677 CurrentGsym = GsymReader::openFile(Path: GSYMPath);
678 if (!*CurrentGsym)
679 error(Prefix: GSYMPath, Err: CurrentGsym->takeError());
680 CurrentGSYMPath = GSYMPath;
681 }
682
683 uint64_t Addr;
684 if (AddrStr.getAsInteger(Radix: 0, Result&: Addr)) {
685 OS << "error: invalid address " << AddrStr
686 << ", expected: Address GsymFile.\n";
687 return 1;
688 }
689
690 doLookup(Gsym&: **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 files
700 for (const auto &GSYMPath : InputFilenames) {
701 auto Gsym = GsymReader::openFile(Path: GSYMPath);
702 if (!Gsym)
703 error(Prefix: GSYMPath, Err: Gsym.takeError());
704
705 if (LookupAddresses.empty()) {
706 Gsym->dump(OS&: 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&: *Gsym, Addr, OS);
714 }
715 }
716 return EXIT_SUCCESS;
717}
718