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