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