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:
SymbolizerOptTable()75 SymbolizerOptTable() : GenericOptTable(InfoTable) {
76 setGroupedShortOptions(true);
77 }
78 };
79 } // namespace
80
81 static std::string ToolName;
82
printError(const ErrorInfoBase & EI,StringRef AuxInfo)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>
print(const Request & Request,Expected<T> & ResOrErr,DIPrinter & Printer)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
enableDebuginfod(LLVMSymbolizer & Symbolizer,const opt::ArgList & Args)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
getSpaceDelimitedWord(StringRef & Source)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
makeStringError(StringRef Msg)154 static Error makeStringError(StringRef Msg) {
155 return make_error<StringError>(Msg, inconvertibleErrorCode());
156 }
157
parseCommand(StringRef BinaryName,bool IsAddr2Line,StringRef InputString,Command & Cmd,std::string & ModuleName,object::BuildID & BuildID,StringRef & Symbol,uint64_t & Offset)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>
executeCommand(StringRef ModuleName,const T & ModuleSpec,Command Cmd,StringRef Symbol,uint64_t Offset,uint64_t AdjustVMA,bool ShouldInline,OutputStyle Style,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)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
printUnknownLineInfo(std::string ModuleName,DIPrinter & Printer)325 static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
326 Request SymRequest = {ModuleName, std::nullopt, StringRef()};
327 Printer.print(SymRequest, DILineInfo());
328 }
329
symbolizeInput(const opt::InputArgList & Args,object::BuildIDRef IncomingBuildID,uint64_t AdjustVMA,bool IsAddr2Line,OutputStyle Style,StringRef InputString,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)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
printHelp(StringRef ToolName,const SymbolizerOptTable & Tbl,raw_ostream & OS)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
parseOptions(int Argc,char * Argv[],bool IsAddr2Line,StringSaver & Saver,SymbolizerOptTable & Tbl)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>
parseIntArg(const opt::InputArgList & Args,int ID,T & Value)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
decideHowToPrintFunctions(const opt::InputArgList & Args,bool IsAddr2Line)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
parseColorArg(const opt::InputArgList & Args)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
parseBuildIDArg(const opt::InputArgList & Args,int ID)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.
filterMarkup(const opt::InputArgList & Args,LLVMSymbolizer & Symbolizer)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
llvm_symbolizer_main(int argc,char ** argv,const llvm::ToolContext &)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