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