xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===//
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 // This utility works much like "addr2line". It is able of transforming
10 // tuples (module name, module offset) to code locations (function name,
11 // file, line number, column number). It is targeted for compiler-rt tools
12 // (especially AddressSanitizer and ThreadSanitizer) that can use it
13 // to symbolize stack traces in their error reports.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "Opts.inc"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Config/config.h"
21 #include "llvm/DebugInfo/Symbolize/DIPrinter.h"
22 #include "llvm/DebugInfo/Symbolize/Markup.h"
23 #include "llvm/DebugInfo/Symbolize/MarkupFilter.h"
24 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
25 #include "llvm/DebugInfo/Symbolize/Symbolize.h"
26 #include "llvm/Debuginfod/BuildIDFetcher.h"
27 #include "llvm/Debuginfod/Debuginfod.h"
28 #include "llvm/Debuginfod/HTTPClient.h"
29 #include "llvm/Option/Arg.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/COM.h"
33 #include "llvm/Support/CommandLine.h"
34 #include "llvm/Support/Debug.h"
35 #include "llvm/Support/Errc.h"
36 #include "llvm/Support/FileSystem.h"
37 #include "llvm/Support/LLVMDriver.h"
38 #include "llvm/Support/Path.h"
39 #include "llvm/Support/StringSaver.h"
40 #include "llvm/Support/WithColor.h"
41 #include "llvm/Support/raw_ostream.h"
42 #include <algorithm>
43 #include <cstdio>
44 #include <cstring>
45 #include <iostream>
46 #include <string>
47 
48 using namespace llvm;
49 using namespace symbolize;
50 
51 namespace {
52 enum ID {
53   OPT_INVALID = 0, // This is not an option ID.
54 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
55 #include "Opts.inc"
56 #undef OPTION
57 };
58 
59 #define PREFIX(NAME, VALUE)                                                    \
60   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
61   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
62                                                 std::size(NAME##_init) - 1);
63 #include "Opts.inc"
64 #undef PREFIX
65 
66 using namespace llvm::opt;
67 static constexpr opt::OptTable::Info InfoTable[] = {
68 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
69 #include "Opts.inc"
70 #undef OPTION
71 };
72 
73 class SymbolizerOptTable : public opt::GenericOptTable {
74 public:
75   SymbolizerOptTable() : GenericOptTable(InfoTable) {
76     setGroupedShortOptions(true);
77   }
78 };
79 } // namespace
80 
81 static std::string ToolName;
82 
83 static void printError(const ErrorInfoBase &EI, StringRef AuxInfo) {
84   WithColor::error(errs(), ToolName);
85   if (!AuxInfo.empty())
86     errs() << "'" << AuxInfo << "': ";
87   EI.log(errs());
88   errs() << '\n';
89 }
90 
91 template <typename T>
92 static void print(const Request &Request, Expected<T> &ResOrErr,
93                   DIPrinter &Printer) {
94   if (ResOrErr) {
95     // No error, print the result.
96     Printer.print(Request, *ResOrErr);
97     return;
98   }
99 
100   // Handle the error.
101   bool PrintEmpty = true;
102   handleAllErrors(std::move(ResOrErr.takeError()),
103                   [&](const ErrorInfoBase &EI) {
104                     PrintEmpty = Printer.printError(Request, EI);
105                   });
106 
107   if (PrintEmpty)
108     Printer.print(Request, T());
109 }
110 
111 enum class OutputStyle { LLVM, GNU, JSON };
112 
113 enum class Command {
114   Code,
115   Data,
116   Frame,
117 };
118 
119 static void enableDebuginfod(LLVMSymbolizer &Symbolizer,
120                              const opt::ArgList &Args) {
121   static bool IsEnabled = false;
122   if (IsEnabled)
123     return;
124   IsEnabled = true;
125   // Look up symbols using the debuginfod client.
126   Symbolizer.setBuildIDFetcher(std::make_unique<DebuginfodFetcher>(
127       Args.getAllArgValues(OPT_debug_file_directory_EQ)));
128   // The HTTPClient must be initialized for use by the debuginfod client.
129   HTTPClient::initialize();
130 }
131 
132 static StringRef getSpaceDelimitedWord(StringRef &Source) {
133   const char kDelimiters[] = " \n\r";
134   const char *Pos = Source.data();
135   StringRef Result;
136   Pos += strspn(Pos, kDelimiters);
137   if (*Pos == '"' || *Pos == '\'') {
138     char Quote = *Pos;
139     Pos++;
140     const char *End = strchr(Pos, Quote);
141     if (!End)
142       return StringRef();
143     Result = StringRef(Pos, End - Pos);
144     Pos = End + 1;
145   } else {
146     int NameLength = strcspn(Pos, kDelimiters);
147     Result = StringRef(Pos, NameLength);
148     Pos += NameLength;
149   }
150   Source = StringRef(Pos, Source.end() - Pos);
151   return Result;
152 }
153 
154 static Error makeStringError(StringRef Msg) {
155   return make_error<StringError>(Msg, inconvertibleErrorCode());
156 }
157 
158 static Error parseCommand(StringRef BinaryName, bool IsAddr2Line,
159                           StringRef InputString, Command &Cmd,
160                           std::string &ModuleName, object::BuildID &BuildID,
161                           StringRef &Symbol, uint64_t &Offset) {
162   ModuleName = BinaryName;
163   if (InputString.consume_front("CODE ")) {
164     Cmd = Command::Code;
165   } else if (InputString.consume_front("DATA ")) {
166     Cmd = Command::Data;
167   } else if (InputString.consume_front("FRAME ")) {
168     Cmd = Command::Frame;
169   } else {
170     // If no cmd, assume it's CODE.
171     Cmd = Command::Code;
172   }
173 
174   // Parse optional input file specification.
175   bool HasFilePrefix = false;
176   bool HasBuildIDPrefix = false;
177   while (!InputString.empty()) {
178     InputString = InputString.ltrim();
179     if (InputString.consume_front("FILE:")) {
180       if (HasFilePrefix || HasBuildIDPrefix)
181         return makeStringError("duplicate input file specification prefix");
182       HasFilePrefix = true;
183       continue;
184     }
185     if (InputString.consume_front("BUILDID:")) {
186       if (HasBuildIDPrefix || HasFilePrefix)
187         return makeStringError("duplicate input file specification prefix");
188       HasBuildIDPrefix = true;
189       continue;
190     }
191     break;
192   }
193 
194   // If an input file is not specified on the command line, try to extract it
195   // from the command.
196   if (HasBuildIDPrefix || HasFilePrefix) {
197     InputString = InputString.ltrim();
198     if (InputString.empty()) {
199       if (HasFilePrefix)
200         return makeStringError("must be followed by an input file");
201       else
202         return makeStringError("must be followed by a hash");
203     }
204 
205     if (!BinaryName.empty() || !BuildID.empty())
206       return makeStringError("input file has already been specified");
207 
208     StringRef Name = getSpaceDelimitedWord(InputString);
209     if (Name.empty())
210       return makeStringError("unbalanced quotes in input file name");
211     if (HasBuildIDPrefix) {
212       BuildID = parseBuildID(Name);
213       if (BuildID.empty())
214         return makeStringError("wrong format of build-id");
215     } else {
216       ModuleName = Name;
217     }
218   } else if (BinaryName.empty() && BuildID.empty()) {
219     // No input file has been specified. If the input string contains at least
220     // two items, assume that the first item is a file name.
221     ModuleName = getSpaceDelimitedWord(InputString);
222     if (ModuleName.empty())
223       return makeStringError("no input filename has been specified");
224   }
225 
226   // Parse address specification, which can be an offset in module or a
227   // symbol with optional offset.
228   InputString = InputString.trim();
229   if (InputString.empty())
230     return makeStringError("no module offset has been specified");
231 
232   // If input string contains a space, ignore everything after it. This behavior
233   // is consistent with GNU addr2line.
234   int AddrSpecLength = InputString.find_first_of(" \n\r");
235   StringRef AddrSpec = InputString.substr(0, AddrSpecLength);
236   bool StartsWithDigit = std::isdigit(AddrSpec.front());
237 
238   // GNU addr2line assumes the address is hexadecimal and allows a redundant
239   // "0x" or "0X" prefix; do the same for compatibility.
240   if (IsAddr2Line)
241     AddrSpec.consume_front("0x") || AddrSpec.consume_front("0X");
242 
243   // If address specification is a number, treat it as a module offset.
244   if (!AddrSpec.getAsInteger(IsAddr2Line ? 16 : 0, Offset)) {
245     // Module offset is an address.
246     Symbol = StringRef();
247     return Error::success();
248   }
249 
250   // If address specification starts with a digit, but is not a number, consider
251   // it as invalid.
252   if (StartsWithDigit || AddrSpec.empty())
253     return makeStringError("expected a number as module offset");
254 
255   // Otherwise it is a symbol name, potentially with an offset.
256   Symbol = AddrSpec;
257   Offset = 0;
258 
259   // If the address specification contains '+', try treating it as
260   // "symbol + offset".
261   size_t Plus = AddrSpec.rfind('+');
262   if (Plus != StringRef::npos) {
263     StringRef SymbolStr = AddrSpec.take_front(Plus);
264     StringRef OffsetStr = AddrSpec.substr(Plus + 1);
265     if (!SymbolStr.empty() && !OffsetStr.empty() &&
266         !OffsetStr.getAsInteger(0, Offset)) {
267       Symbol = SymbolStr;
268       return Error::success();
269     }
270     // The found '+' is not an offset delimiter.
271   }
272 
273   return Error::success();
274 }
275 
276 template <typename T>
277 void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd,
278                     StringRef Symbol, uint64_t Offset, uint64_t AdjustVMA,
279                     bool ShouldInline, OutputStyle Style,
280                     LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
281   uint64_t AdjustedOffset = Offset - AdjustVMA;
282   object::SectionedAddress Address = {AdjustedOffset,
283                                       object::SectionedAddress::UndefSection};
284   Request SymRequest = {
285       ModuleName, Symbol.empty() ? std::make_optional(Offset) : std::nullopt,
286       Symbol};
287   if (Cmd == Command::Data) {
288     Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(ModuleSpec, Address);
289     print(SymRequest, ResOrErr, Printer);
290   } else if (Cmd == Command::Frame) {
291     Expected<std::vector<DILocal>> ResOrErr =
292         Symbolizer.symbolizeFrame(ModuleSpec, Address);
293     print(SymRequest, ResOrErr, Printer);
294   } else if (!Symbol.empty()) {
295     Expected<std::vector<DILineInfo>> ResOrErr =
296         Symbolizer.findSymbol(ModuleSpec, Symbol, Offset);
297     print(SymRequest, ResOrErr, Printer);
298   } else if (ShouldInline) {
299     Expected<DIInliningInfo> ResOrErr =
300         Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
301     print(SymRequest, ResOrErr, Printer);
302   } else if (Style == OutputStyle::GNU) {
303     // With PrintFunctions == FunctionNameKind::LinkageName (default)
304     // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode()
305     // may override the name of an inlined function with the name of the topmost
306     // caller function in the inlining chain. This contradicts the existing
307     // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only
308     // the topmost function, which suits our needs better.
309     Expected<DIInliningInfo> ResOrErr =
310         Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
311     Expected<DILineInfo> Res0OrErr =
312         !ResOrErr
313             ? Expected<DILineInfo>(ResOrErr.takeError())
314             : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo()
315                                                     : ResOrErr->getFrame(0));
316     print(SymRequest, Res0OrErr, Printer);
317   } else {
318     Expected<DILineInfo> ResOrErr =
319         Symbolizer.symbolizeCode(ModuleSpec, Address);
320     print(SymRequest, ResOrErr, Printer);
321   }
322   Symbolizer.pruneCache();
323 }
324 
325 static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
326   Request SymRequest = {ModuleName, std::nullopt, StringRef()};
327   Printer.print(SymRequest, DILineInfo());
328 }
329 
330 static void symbolizeInput(const opt::InputArgList &Args,
331                            object::BuildIDRef IncomingBuildID,
332                            uint64_t AdjustVMA, bool IsAddr2Line,
333                            OutputStyle Style, StringRef InputString,
334                            LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
335   Command Cmd;
336   std::string ModuleName;
337   object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end());
338   uint64_t Offset = 0;
339   StringRef Symbol;
340 
341   // An empty input string may be used to check if the process is alive and
342   // responding to input. Do not emit a message on stderr in this case but
343   // respond on stdout.
344   if (InputString.empty()) {
345     printUnknownLineInfo(ModuleName, Printer);
346     return;
347   }
348   if (Error E = parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
349                              StringRef(InputString), Cmd, ModuleName, BuildID,
350                              Symbol, Offset)) {
351     handleAllErrors(std::move(E), [&](const StringError &EI) {
352       printError(EI, InputString);
353       printUnknownLineInfo(ModuleName, Printer);
354     });
355     return;
356   }
357   bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line);
358   if (!BuildID.empty()) {
359     assert(ModuleName.empty());
360     if (!Args.hasArg(OPT_no_debuginfod))
361       enableDebuginfod(Symbolizer, Args);
362     std::string BuildIDStr = toHex(BuildID);
363     executeCommand(BuildIDStr, BuildID, Cmd, Symbol, Offset, AdjustVMA,
364                    ShouldInline, Style, Symbolizer, Printer);
365   } else {
366     executeCommand(ModuleName, ModuleName, Cmd, Symbol, Offset, AdjustVMA,
367                    ShouldInline, Style, Symbolizer, Printer);
368   }
369 }
370 
371 static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl,
372                       raw_ostream &OS) {
373   const char HelpText[] = " [options] addresses...";
374   Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(),
375                 ToolName.str().c_str());
376   // TODO Replace this with OptTable API once it adds extrahelp support.
377   OS << "\nPass @FILE as argument to read options from FILE.\n";
378 }
379 
380 static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line,
381                                       StringSaver &Saver,
382                                       SymbolizerOptTable &Tbl) {
383   StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer";
384   // The environment variable specifies initial options which can be overridden
385   // by commnad line options.
386   Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS"
387                                                    : "LLVM_SYMBOLIZER_OPTS");
388   bool HasError = false;
389   opt::InputArgList Args =
390       Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
391         errs() << ("error: " + Msg + "\n");
392         HasError = true;
393       });
394   if (HasError)
395     exit(1);
396   if (Args.hasArg(OPT_help)) {
397     printHelp(ToolName, Tbl, outs());
398     exit(0);
399   }
400   if (Args.hasArg(OPT_version)) {
401     outs() << ToolName << '\n';
402     cl::PrintVersionMessage();
403     exit(0);
404   }
405 
406   return Args;
407 }
408 
409 template <typename T>
410 static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
411   if (const opt::Arg *A = Args.getLastArg(ID)) {
412     StringRef V(A->getValue());
413     if (!llvm::to_integer(V, Value, 0)) {
414       errs() << A->getSpelling() +
415                     ": expected a non-negative integer, but got '" + V + "'";
416       exit(1);
417     }
418   } else {
419     Value = 0;
420   }
421 }
422 
423 static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args,
424                                                   bool IsAddr2Line) {
425   if (Args.hasArg(OPT_functions))
426     return FunctionNameKind::LinkageName;
427   if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ))
428     return StringSwitch<FunctionNameKind>(A->getValue())
429         .Case("none", FunctionNameKind::None)
430         .Case("short", FunctionNameKind::ShortName)
431         .Default(FunctionNameKind::LinkageName);
432   return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName;
433 }
434 
435 static std::optional<bool> parseColorArg(const opt::InputArgList &Args) {
436   if (Args.hasArg(OPT_color))
437     return true;
438   if (const opt::Arg *A = Args.getLastArg(OPT_color_EQ))
439     return StringSwitch<std::optional<bool>>(A->getValue())
440         .Case("always", true)
441         .Case("never", false)
442         .Case("auto", std::nullopt);
443   return std::nullopt;
444 }
445 
446 static object::BuildID parseBuildIDArg(const opt::InputArgList &Args, int ID) {
447   const opt::Arg *A = Args.getLastArg(ID);
448   if (!A)
449     return {};
450 
451   StringRef V(A->getValue());
452   object::BuildID BuildID = parseBuildID(V);
453   if (BuildID.empty()) {
454     errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n";
455     exit(1);
456   }
457   return BuildID;
458 }
459 
460 // Symbolize markup from stdin and write the result to stdout.
461 static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) {
462   MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args));
463   std::string InputString;
464   while (std::getline(std::cin, InputString)) {
465     InputString += '\n';
466     Filter.filter(std::move(InputString));
467   }
468   Filter.finish();
469 }
470 
471 int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) {
472   sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded);
473 
474   ToolName = argv[0];
475   bool IsAddr2Line = sys::path::stem(ToolName).contains("addr2line");
476   BumpPtrAllocator A;
477   StringSaver Saver(A);
478   SymbolizerOptTable Tbl;
479   opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl);
480 
481   LLVMSymbolizer::Options Opts;
482   uint64_t AdjustVMA;
483   PrinterConfig Config;
484   parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA);
485   if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) {
486     Opts.PathStyle =
487         A->getOption().matches(OPT_basenames)
488             ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly
489             : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath;
490   } else {
491     Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath;
492   }
493   Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ);
494   Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str();
495   Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line);
496   Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str();
497   Opts.FallbackDebugPath =
498       Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
499   Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line);
500   parseIntArg(Args, OPT_print_source_context_lines_EQ,
501               Config.SourceContextLines);
502   Opts.RelativeAddresses = Args.hasArg(OPT_relative_address);
503   Opts.UntagAddresses =
504       Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line);
505   Opts.UseDIA = Args.hasArg(OPT_use_dia);
506 #if !defined(LLVM_ENABLE_DIA_SDK)
507   if (Opts.UseDIA) {
508     WithColor::warning() << "DIA not available; using native PDB reader\n";
509     Opts.UseDIA = false;
510   }
511 #endif
512   Opts.UseSymbolTable = true;
513   if (Args.hasArg(OPT_cache_size_EQ))
514     parseIntArg(Args, OPT_cache_size_EQ, Opts.MaxCacheSize);
515   Config.PrintAddress = Args.hasArg(OPT_addresses);
516   Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None;
517   Config.Pretty = Args.hasArg(OPT_pretty_print);
518   Config.Verbose = Args.hasArg(OPT_verbose);
519 
520   for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) {
521     StringRef Hint(A->getValue());
522     if (sys::path::extension(Hint) == ".dSYM") {
523       Opts.DsymHints.emplace_back(Hint);
524     } else {
525       errs() << "Warning: invalid dSYM hint: \"" << Hint
526              << "\" (must have the '.dSYM' extension).\n";
527     }
528   }
529 
530   LLVMSymbolizer Symbolizer(Opts);
531 
532   if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod()))
533     enableDebuginfod(Symbolizer, Args);
534 
535   if (Args.hasArg(OPT_filter_markup)) {
536     filterMarkup(Args, Symbolizer);
537     return 0;
538   }
539 
540   auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM;
541   if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) {
542     if (strcmp(A->getValue(), "GNU") == 0)
543       Style = OutputStyle::GNU;
544     else if (strcmp(A->getValue(), "JSON") == 0)
545       Style = OutputStyle::JSON;
546     else
547       Style = OutputStyle::LLVM;
548   }
549 
550   if (Args.hasArg(OPT_build_id_EQ) && Args.hasArg(OPT_obj_EQ)) {
551     errs() << "error: cannot specify both --build-id and --obj\n";
552     return EXIT_FAILURE;
553   }
554   object::BuildID BuildID = parseBuildIDArg(Args, OPT_build_id_EQ);
555 
556   std::unique_ptr<DIPrinter> Printer;
557   if (Style == OutputStyle::GNU)
558     Printer = std::make_unique<GNUPrinter>(outs(), printError, Config);
559   else if (Style == OutputStyle::JSON)
560     Printer = std::make_unique<JSONPrinter>(outs(), Config);
561   else
562     Printer = std::make_unique<LLVMPrinter>(outs(), printError, Config);
563 
564   // When an input file is specified, exit immediately if the file cannot be
565   // read. If getOrCreateModuleInfo succeeds, symbolizeInput will reuse the
566   // cached file handle.
567   if (auto *Arg = Args.getLastArg(OPT_obj_EQ); Arg) {
568     auto Status = Symbolizer.getOrCreateModuleInfo(Arg->getValue());
569     if (!Status) {
570       Request SymRequest = {Arg->getValue(), 0, StringRef()};
571       handleAllErrors(Status.takeError(), [&](const ErrorInfoBase &EI) {
572         Printer->printError(SymRequest, EI);
573       });
574       return EXIT_FAILURE;
575     }
576   }
577 
578   std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT);
579   if (InputAddresses.empty()) {
580     const int kMaxInputStringLength = 1024;
581     char InputString[kMaxInputStringLength];
582 
583     while (fgets(InputString, sizeof(InputString), stdin)) {
584       // Strip newline characters.
585       std::string StrippedInputString(InputString);
586       llvm::erase_if(StrippedInputString,
587                      [](char c) { return c == '\r' || c == '\n'; });
588       symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style,
589                      StrippedInputString, Symbolizer, *Printer);
590       outs().flush();
591     }
592   } else {
593     Printer->listBegin();
594     for (StringRef Address : InputAddresses)
595       symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, Address,
596                      Symbolizer, *Printer);
597     Printer->listEnd();
598   }
599 
600   return 0;
601 }
602