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