10b57cec5SDimitry Andric //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This utility works much like "addr2line". It is able of transforming 100b57cec5SDimitry Andric // tuples (module name, module offset) to code locations (function name, 110b57cec5SDimitry Andric // file, line number, column number). It is targeted for compiler-rt tools 120b57cec5SDimitry Andric // (especially AddressSanitizer and ThreadSanitizer) that can use it 130b57cec5SDimitry Andric // to symbolize stack traces in their error reports. 140b57cec5SDimitry Andric // 150b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 160b57cec5SDimitry Andric 17e8d8bef9SDimitry Andric #include "Opts.inc" 1881ad6265SDimitry Andric #include "llvm/ADT/StringExtras.h" 190b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 20e8d8bef9SDimitry Andric #include "llvm/Config/config.h" 210b57cec5SDimitry Andric #include "llvm/DebugInfo/Symbolize/DIPrinter.h" 2281ad6265SDimitry Andric #include "llvm/DebugInfo/Symbolize/Markup.h" 2381ad6265SDimitry Andric #include "llvm/DebugInfo/Symbolize/MarkupFilter.h" 2481ad6265SDimitry Andric #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" 250b57cec5SDimitry Andric #include "llvm/DebugInfo/Symbolize/Symbolize.h" 26bdd1243dSDimitry Andric #include "llvm/Debuginfod/BuildIDFetcher.h" 2781ad6265SDimitry Andric #include "llvm/Debuginfod/Debuginfod.h" 280eae32dcSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h" 29e8d8bef9SDimitry Andric #include "llvm/Option/Arg.h" 30e8d8bef9SDimitry Andric #include "llvm/Option/ArgList.h" 31e8d8bef9SDimitry Andric #include "llvm/Option/Option.h" 320b57cec5SDimitry Andric #include "llvm/Support/COM.h" 330b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 340b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 350b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 360b57cec5SDimitry Andric #include "llvm/Support/InitLLVM.h" 370b57cec5SDimitry Andric #include "llvm/Support/Path.h" 38e8d8bef9SDimitry Andric #include "llvm/Support/StringSaver.h" 390b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 40480093f4SDimitry Andric #include <algorithm> 410b57cec5SDimitry Andric #include <cstdio> 420b57cec5SDimitry Andric #include <cstring> 43bdd1243dSDimitry Andric #include <iostream> 440b57cec5SDimitry Andric #include <string> 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric using namespace llvm; 470b57cec5SDimitry Andric using namespace symbolize; 480b57cec5SDimitry Andric 49e8d8bef9SDimitry Andric namespace { 50e8d8bef9SDimitry Andric enum ID { 51e8d8bef9SDimitry Andric OPT_INVALID = 0, // This is not an option ID. 52e8d8bef9SDimitry Andric #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 53e8d8bef9SDimitry Andric HELPTEXT, METAVAR, VALUES) \ 54e8d8bef9SDimitry Andric OPT_##ID, 55e8d8bef9SDimitry Andric #include "Opts.inc" 56e8d8bef9SDimitry Andric #undef OPTION 57e8d8bef9SDimitry Andric }; 580b57cec5SDimitry Andric 59bdd1243dSDimitry Andric #define PREFIX(NAME, VALUE) \ 60bdd1243dSDimitry Andric static constexpr StringLiteral NAME##_init[] = VALUE; \ 61bdd1243dSDimitry Andric static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ 62bdd1243dSDimitry Andric std::size(NAME##_init) - 1); 63e8d8bef9SDimitry Andric #include "Opts.inc" 64e8d8bef9SDimitry Andric #undef PREFIX 650b57cec5SDimitry Andric 66bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = { 67e8d8bef9SDimitry Andric #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 68e8d8bef9SDimitry Andric HELPTEXT, METAVAR, VALUES) \ 69e8d8bef9SDimitry Andric { \ 70e8d8bef9SDimitry Andric PREFIX, NAME, HELPTEXT, \ 71e8d8bef9SDimitry Andric METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 72e8d8bef9SDimitry Andric PARAM, FLAGS, OPT_##GROUP, \ 73e8d8bef9SDimitry Andric OPT_##ALIAS, ALIASARGS, VALUES}, 74e8d8bef9SDimitry Andric #include "Opts.inc" 75e8d8bef9SDimitry Andric #undef OPTION 76e8d8bef9SDimitry Andric }; 770b57cec5SDimitry Andric 78bdd1243dSDimitry Andric class SymbolizerOptTable : public opt::GenericOptTable { 79e8d8bef9SDimitry Andric public: 80bdd1243dSDimitry Andric SymbolizerOptTable() : GenericOptTable(InfoTable) { 81fe6060f1SDimitry Andric setGroupedShortOptions(true); 82fe6060f1SDimitry Andric } 83e8d8bef9SDimitry Andric }; 84e8d8bef9SDimitry Andric } // namespace 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric template <typename T> 87fe6060f1SDimitry Andric static void print(const Request &Request, Expected<T> &ResOrErr, 88fe6060f1SDimitry Andric DIPrinter &Printer) { 89fe6060f1SDimitry Andric if (ResOrErr) { 90fe6060f1SDimitry Andric // No error, print the result. 91fe6060f1SDimitry Andric Printer.print(Request, *ResOrErr); 92fe6060f1SDimitry Andric return; 930b57cec5SDimitry Andric } 940b57cec5SDimitry Andric 95fe6060f1SDimitry Andric // Handle the error. 96fe6060f1SDimitry Andric bool PrintEmpty = true; 97fe6060f1SDimitry Andric handleAllErrors(std::move(ResOrErr.takeError()), 98fe6060f1SDimitry Andric [&](const ErrorInfoBase &EI) { 99fe6060f1SDimitry Andric PrintEmpty = Printer.printError( 100fe6060f1SDimitry Andric Request, EI, "LLVMSymbolizer: error reading file: "); 101fe6060f1SDimitry Andric }); 102fe6060f1SDimitry Andric 103fe6060f1SDimitry Andric if (PrintEmpty) 104fe6060f1SDimitry Andric Printer.print(Request, T()); 105fe6060f1SDimitry Andric } 106fe6060f1SDimitry Andric 107fe6060f1SDimitry Andric enum class OutputStyle { LLVM, GNU, JSON }; 108fe6060f1SDimitry Andric 1090b57cec5SDimitry Andric enum class Command { 1100b57cec5SDimitry Andric Code, 1110b57cec5SDimitry Andric Data, 1120b57cec5SDimitry Andric Frame, 1130b57cec5SDimitry Andric }; 1140b57cec5SDimitry Andric 115bdd1243dSDimitry Andric static void enableDebuginfod(LLVMSymbolizer &Symbolizer, 116bdd1243dSDimitry Andric const opt::ArgList &Args) { 11781ad6265SDimitry Andric static bool IsEnabled = false; 11881ad6265SDimitry Andric if (IsEnabled) 11981ad6265SDimitry Andric return; 12081ad6265SDimitry Andric IsEnabled = true; 12181ad6265SDimitry Andric // Look up symbols using the debuginfod client. 122bdd1243dSDimitry Andric Symbolizer.setBuildIDFetcher(std::make_unique<DebuginfodFetcher>( 123bdd1243dSDimitry Andric Args.getAllArgValues(OPT_debug_file_directory_EQ))); 12481ad6265SDimitry Andric // The HTTPClient must be initialized for use by the debuginfod client. 12581ad6265SDimitry Andric HTTPClient::initialize(); 12681ad6265SDimitry Andric } 12781ad6265SDimitry Andric 128bdd1243dSDimitry Andric static object::BuildID parseBuildID(StringRef Str) { 12981ad6265SDimitry Andric std::string Bytes; 13081ad6265SDimitry Andric if (!tryGetFromHex(Str, Bytes)) 13181ad6265SDimitry Andric return {}; 13281ad6265SDimitry Andric ArrayRef<uint8_t> BuildID(reinterpret_cast<const uint8_t *>(Bytes.data()), 13381ad6265SDimitry Andric Bytes.size()); 134bdd1243dSDimitry Andric return object::BuildID(BuildID.begin(), BuildID.end()); 13581ad6265SDimitry Andric } 13681ad6265SDimitry Andric 137e8d8bef9SDimitry Andric static bool parseCommand(StringRef BinaryName, bool IsAddr2Line, 138e8d8bef9SDimitry Andric StringRef InputString, Command &Cmd, 139bdd1243dSDimitry Andric std::string &ModuleName, object::BuildID &BuildID, 14081ad6265SDimitry Andric uint64_t &ModuleOffset) { 1410b57cec5SDimitry Andric const char kDelimiters[] = " \n\r"; 1420b57cec5SDimitry Andric ModuleName = ""; 1430b57cec5SDimitry Andric if (InputString.consume_front("CODE ")) { 1440b57cec5SDimitry Andric Cmd = Command::Code; 1450b57cec5SDimitry Andric } else if (InputString.consume_front("DATA ")) { 1460b57cec5SDimitry Andric Cmd = Command::Data; 1470b57cec5SDimitry Andric } else if (InputString.consume_front("FRAME ")) { 1480b57cec5SDimitry Andric Cmd = Command::Frame; 1490b57cec5SDimitry Andric } else { 1500b57cec5SDimitry Andric // If no cmd, assume it's CODE. 1510b57cec5SDimitry Andric Cmd = Command::Code; 1520b57cec5SDimitry Andric } 15381ad6265SDimitry Andric 15481ad6265SDimitry Andric const char *Pos; 1550b57cec5SDimitry Andric // Skip delimiters and parse input filename (if needed). 15681ad6265SDimitry Andric if (BinaryName.empty() && BuildID.empty()) { 15781ad6265SDimitry Andric bool HasFilePrefix = false; 15881ad6265SDimitry Andric bool HasBuildIDPrefix = false; 15981ad6265SDimitry Andric while (true) { 16081ad6265SDimitry Andric if (InputString.consume_front("FILE:")) { 16181ad6265SDimitry Andric if (HasFilePrefix) 16281ad6265SDimitry Andric return false; 16381ad6265SDimitry Andric HasFilePrefix = true; 16481ad6265SDimitry Andric continue; 16581ad6265SDimitry Andric } 16681ad6265SDimitry Andric if (InputString.consume_front("BUILDID:")) { 16781ad6265SDimitry Andric if (HasBuildIDPrefix) 16881ad6265SDimitry Andric return false; 16981ad6265SDimitry Andric HasBuildIDPrefix = true; 17081ad6265SDimitry Andric continue; 17181ad6265SDimitry Andric } 17281ad6265SDimitry Andric break; 17381ad6265SDimitry Andric } 17481ad6265SDimitry Andric if (HasFilePrefix && HasBuildIDPrefix) 17581ad6265SDimitry Andric return false; 17681ad6265SDimitry Andric 17781ad6265SDimitry Andric Pos = InputString.data(); 1785ffd83dbSDimitry Andric Pos += strspn(Pos, kDelimiters); 1795ffd83dbSDimitry Andric if (*Pos == '"' || *Pos == '\'') { 1805ffd83dbSDimitry Andric char Quote = *Pos; 1815ffd83dbSDimitry Andric Pos++; 1825ffd83dbSDimitry Andric const char *End = strchr(Pos, Quote); 1835ffd83dbSDimitry Andric if (!End) 1840b57cec5SDimitry Andric return false; 1855ffd83dbSDimitry Andric ModuleName = std::string(Pos, End - Pos); 1865ffd83dbSDimitry Andric Pos = End + 1; 1870b57cec5SDimitry Andric } else { 1885ffd83dbSDimitry Andric int NameLength = strcspn(Pos, kDelimiters); 1895ffd83dbSDimitry Andric ModuleName = std::string(Pos, NameLength); 1905ffd83dbSDimitry Andric Pos += NameLength; 1910b57cec5SDimitry Andric } 19281ad6265SDimitry Andric if (HasBuildIDPrefix) { 19381ad6265SDimitry Andric BuildID = parseBuildID(ModuleName); 19481ad6265SDimitry Andric if (BuildID.empty()) 19581ad6265SDimitry Andric return false; 19681ad6265SDimitry Andric ModuleName.clear(); 19781ad6265SDimitry Andric } 1980b57cec5SDimitry Andric } else { 19981ad6265SDimitry Andric Pos = InputString.data(); 200e8d8bef9SDimitry Andric ModuleName = BinaryName.str(); 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric // Skip delimiters and parse module offset. 2035ffd83dbSDimitry Andric Pos += strspn(Pos, kDelimiters); 2045ffd83dbSDimitry Andric int OffsetLength = strcspn(Pos, kDelimiters); 2055ffd83dbSDimitry Andric StringRef Offset(Pos, OffsetLength); 2065ffd83dbSDimitry Andric // GNU addr2line assumes the offset is hexadecimal and allows a redundant 2075ffd83dbSDimitry Andric // "0x" or "0X" prefix; do the same for compatibility. 2085ffd83dbSDimitry Andric if (IsAddr2Line) 2095ffd83dbSDimitry Andric Offset.consume_front("0x") || Offset.consume_front("0X"); 2105ffd83dbSDimitry Andric return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric 21381ad6265SDimitry Andric template <typename T> 21481ad6265SDimitry Andric void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd, 21581ad6265SDimitry Andric uint64_t Offset, uint64_t AdjustVMA, bool ShouldInline, 21681ad6265SDimitry Andric OutputStyle Style, LLVMSymbolizer &Symbolizer, 217e8d8bef9SDimitry Andric DIPrinter &Printer) { 218fe6060f1SDimitry Andric uint64_t AdjustedOffset = Offset - AdjustVMA; 21981ad6265SDimitry Andric object::SectionedAddress Address = {AdjustedOffset, 22081ad6265SDimitry Andric object::SectionedAddress::UndefSection}; 2210b57cec5SDimitry Andric if (Cmd == Command::Data) { 22281ad6265SDimitry Andric Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(ModuleSpec, Address); 223fe6060f1SDimitry Andric print({ModuleName, Offset}, ResOrErr, Printer); 2240b57cec5SDimitry Andric } else if (Cmd == Command::Frame) { 22581ad6265SDimitry Andric Expected<std::vector<DILocal>> ResOrErr = 22681ad6265SDimitry Andric Symbolizer.symbolizeFrame(ModuleSpec, Address); 227fe6060f1SDimitry Andric print({ModuleName, Offset}, ResOrErr, Printer); 22881ad6265SDimitry Andric } else if (ShouldInline) { 22981ad6265SDimitry Andric Expected<DIInliningInfo> ResOrErr = 23081ad6265SDimitry Andric Symbolizer.symbolizeInlinedCode(ModuleSpec, Address); 231fe6060f1SDimitry Andric print({ModuleName, Offset}, ResOrErr, Printer); 232fe6060f1SDimitry Andric } else if (Style == OutputStyle::GNU) { 233e8d8bef9SDimitry Andric // With PrintFunctions == FunctionNameKind::LinkageName (default) 234e8d8bef9SDimitry Andric // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() 2350b57cec5SDimitry Andric // may override the name of an inlined function with the name of the topmost 2360b57cec5SDimitry Andric // caller function in the inlining chain. This contradicts the existing 2370b57cec5SDimitry Andric // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only 2380b57cec5SDimitry Andric // the topmost function, which suits our needs better. 23981ad6265SDimitry Andric Expected<DIInliningInfo> ResOrErr = 24081ad6265SDimitry Andric Symbolizer.symbolizeInlinedCode(ModuleSpec, Address); 241fe6060f1SDimitry Andric Expected<DILineInfo> Res0OrErr = 242fe6060f1SDimitry Andric !ResOrErr 243fe6060f1SDimitry Andric ? Expected<DILineInfo>(ResOrErr.takeError()) 244fe6060f1SDimitry Andric : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo() 245fe6060f1SDimitry Andric : ResOrErr->getFrame(0)); 246fe6060f1SDimitry Andric print({ModuleName, Offset}, Res0OrErr, Printer); 247d409305fSDimitry Andric } else { 24881ad6265SDimitry Andric Expected<DILineInfo> ResOrErr = 24981ad6265SDimitry Andric Symbolizer.symbolizeCode(ModuleSpec, Address); 250fe6060f1SDimitry Andric print({ModuleName, Offset}, ResOrErr, Printer); 251d409305fSDimitry Andric } 25281ad6265SDimitry Andric Symbolizer.pruneCache(); 25381ad6265SDimitry Andric } 25481ad6265SDimitry Andric 25581ad6265SDimitry Andric static void symbolizeInput(const opt::InputArgList &Args, 256bdd1243dSDimitry Andric object::BuildIDRef IncomingBuildID, 25781ad6265SDimitry Andric uint64_t AdjustVMA, bool IsAddr2Line, 25881ad6265SDimitry Andric OutputStyle Style, StringRef InputString, 25981ad6265SDimitry Andric LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { 26081ad6265SDimitry Andric Command Cmd; 26181ad6265SDimitry Andric std::string ModuleName; 262bdd1243dSDimitry Andric object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end()); 26381ad6265SDimitry Andric uint64_t Offset = 0; 26481ad6265SDimitry Andric if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, 26581ad6265SDimitry Andric StringRef(InputString), Cmd, ModuleName, BuildID, Offset)) { 266bdd1243dSDimitry Andric Printer.printInvalidCommand({ModuleName, std::nullopt}, InputString); 26781ad6265SDimitry Andric return; 26881ad6265SDimitry Andric } 26981ad6265SDimitry Andric bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line); 27081ad6265SDimitry Andric if (!BuildID.empty()) { 27181ad6265SDimitry Andric assert(ModuleName.empty()); 27281ad6265SDimitry Andric if (!Args.hasArg(OPT_no_debuginfod)) 273bdd1243dSDimitry Andric enableDebuginfod(Symbolizer, Args); 27481ad6265SDimitry Andric std::string BuildIDStr = toHex(BuildID); 27581ad6265SDimitry Andric executeCommand(BuildIDStr, BuildID, Cmd, Offset, AdjustVMA, ShouldInline, 27681ad6265SDimitry Andric Style, Symbolizer, Printer); 27781ad6265SDimitry Andric } else { 27881ad6265SDimitry Andric executeCommand(ModuleName, ModuleName, Cmd, Offset, AdjustVMA, ShouldInline, 27981ad6265SDimitry Andric Style, Symbolizer, Printer); 28081ad6265SDimitry Andric } 2810b57cec5SDimitry Andric } 2820b57cec5SDimitry Andric 283e8d8bef9SDimitry Andric static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, 284e8d8bef9SDimitry Andric raw_ostream &OS) { 285e8d8bef9SDimitry Andric const char HelpText[] = " [options] addresses..."; 286fe6060f1SDimitry Andric Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(), 287e8d8bef9SDimitry Andric ToolName.str().c_str()); 288e8d8bef9SDimitry Andric // TODO Replace this with OptTable API once it adds extrahelp support. 289e8d8bef9SDimitry Andric OS << "\nPass @FILE as argument to read options from FILE.\n"; 290e8d8bef9SDimitry Andric } 291e8d8bef9SDimitry Andric 292e8d8bef9SDimitry Andric static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line, 293e8d8bef9SDimitry Andric StringSaver &Saver, 294e8d8bef9SDimitry Andric SymbolizerOptTable &Tbl) { 295e8d8bef9SDimitry Andric StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer"; 296e8d8bef9SDimitry Andric // The environment variable specifies initial options which can be overridden 297e8d8bef9SDimitry Andric // by commnad line options. 298e8d8bef9SDimitry Andric Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" 299e8d8bef9SDimitry Andric : "LLVM_SYMBOLIZER_OPTS"); 300e8d8bef9SDimitry Andric bool HasError = false; 301e8d8bef9SDimitry Andric opt::InputArgList Args = 302e8d8bef9SDimitry Andric Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { 303e8d8bef9SDimitry Andric errs() << ("error: " + Msg + "\n"); 304e8d8bef9SDimitry Andric HasError = true; 305e8d8bef9SDimitry Andric }); 306e8d8bef9SDimitry Andric if (HasError) 307e8d8bef9SDimitry Andric exit(1); 308e8d8bef9SDimitry Andric if (Args.hasArg(OPT_help)) { 309e8d8bef9SDimitry Andric printHelp(ToolName, Tbl, outs()); 310e8d8bef9SDimitry Andric exit(0); 311e8d8bef9SDimitry Andric } 312e8d8bef9SDimitry Andric if (Args.hasArg(OPT_version)) { 313e8d8bef9SDimitry Andric outs() << ToolName << '\n'; 314e8d8bef9SDimitry Andric cl::PrintVersionMessage(); 315e8d8bef9SDimitry Andric exit(0); 316e8d8bef9SDimitry Andric } 317e8d8bef9SDimitry Andric 318e8d8bef9SDimitry Andric return Args; 319e8d8bef9SDimitry Andric } 320e8d8bef9SDimitry Andric 321e8d8bef9SDimitry Andric template <typename T> 322e8d8bef9SDimitry Andric static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { 323e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(ID)) { 324e8d8bef9SDimitry Andric StringRef V(A->getValue()); 325e8d8bef9SDimitry Andric if (!llvm::to_integer(V, Value, 0)) { 326e8d8bef9SDimitry Andric errs() << A->getSpelling() + 327e8d8bef9SDimitry Andric ": expected a non-negative integer, but got '" + V + "'"; 328e8d8bef9SDimitry Andric exit(1); 329e8d8bef9SDimitry Andric } 330e8d8bef9SDimitry Andric } else { 331e8d8bef9SDimitry Andric Value = 0; 332e8d8bef9SDimitry Andric } 333e8d8bef9SDimitry Andric } 334e8d8bef9SDimitry Andric 335e8d8bef9SDimitry Andric static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args, 336e8d8bef9SDimitry Andric bool IsAddr2Line) { 337e8d8bef9SDimitry Andric if (Args.hasArg(OPT_functions)) 338e8d8bef9SDimitry Andric return FunctionNameKind::LinkageName; 339e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ)) 340e8d8bef9SDimitry Andric return StringSwitch<FunctionNameKind>(A->getValue()) 341e8d8bef9SDimitry Andric .Case("none", FunctionNameKind::None) 342e8d8bef9SDimitry Andric .Case("short", FunctionNameKind::ShortName) 343e8d8bef9SDimitry Andric .Default(FunctionNameKind::LinkageName); 344e8d8bef9SDimitry Andric return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName; 345e8d8bef9SDimitry Andric } 346e8d8bef9SDimitry Andric 347bdd1243dSDimitry Andric static std::optional<bool> parseColorArg(const opt::InputArgList &Args) { 34881ad6265SDimitry Andric if (Args.hasArg(OPT_color)) 34981ad6265SDimitry Andric return true; 35081ad6265SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_color_EQ)) 351bdd1243dSDimitry Andric return StringSwitch<std::optional<bool>>(A->getValue()) 35281ad6265SDimitry Andric .Case("always", true) 35381ad6265SDimitry Andric .Case("never", false) 354bdd1243dSDimitry Andric .Case("auto", std::nullopt); 355bdd1243dSDimitry Andric return std::nullopt; 35681ad6265SDimitry Andric } 35781ad6265SDimitry Andric 358bdd1243dSDimitry Andric static object::BuildID parseBuildIDArg(const opt::InputArgList &Args, int ID) { 35981ad6265SDimitry Andric const opt::Arg *A = Args.getLastArg(ID); 36081ad6265SDimitry Andric if (!A) 36181ad6265SDimitry Andric return {}; 36281ad6265SDimitry Andric 36381ad6265SDimitry Andric StringRef V(A->getValue()); 364bdd1243dSDimitry Andric object::BuildID BuildID = parseBuildID(V); 36581ad6265SDimitry Andric if (BuildID.empty()) { 36681ad6265SDimitry Andric errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n"; 36781ad6265SDimitry Andric exit(1); 36881ad6265SDimitry Andric } 36981ad6265SDimitry Andric return BuildID; 37081ad6265SDimitry Andric } 37181ad6265SDimitry Andric 372fcaf7f86SDimitry Andric // Symbolize markup from stdin and write the result to stdout. 373a4a491e2SDimitry Andric static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) { 374a4a491e2SDimitry Andric MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args)); 375fcaf7f86SDimitry Andric std::string InputString; 376fcaf7f86SDimitry Andric while (std::getline(std::cin, InputString)) { 37781ad6265SDimitry Andric InputString += '\n'; 378fcaf7f86SDimitry Andric Filter.filter(InputString); 37981ad6265SDimitry Andric } 380fcaf7f86SDimitry Andric Filter.finish(); 38181ad6265SDimitry Andric } 38281ad6265SDimitry Andric 38381ad6265SDimitry Andric ExitOnError ExitOnErr; 38481ad6265SDimitry Andric 3850b57cec5SDimitry Andric int main(int argc, char **argv) { 3860b57cec5SDimitry Andric InitLLVM X(argc, argv); 387e8d8bef9SDimitry Andric sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded); 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line"); 390e8d8bef9SDimitry Andric BumpPtrAllocator A; 391e8d8bef9SDimitry Andric StringSaver Saver(A); 392e8d8bef9SDimitry Andric SymbolizerOptTable Tbl; 393e8d8bef9SDimitry Andric opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl); 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric LLVMSymbolizer::Options Opts; 396e8d8bef9SDimitry Andric uint64_t AdjustVMA; 397fe6060f1SDimitry Andric PrinterConfig Config; 398e8d8bef9SDimitry Andric parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); 399e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { 400e8d8bef9SDimitry Andric Opts.PathStyle = 401e8d8bef9SDimitry Andric A->getOption().matches(OPT_basenames) 402e8d8bef9SDimitry Andric ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly 403e8d8bef9SDimitry Andric : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; 4040b57cec5SDimitry Andric } else { 405e8d8bef9SDimitry Andric Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; 406e8d8bef9SDimitry Andric } 407e8d8bef9SDimitry Andric Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ); 408e8d8bef9SDimitry Andric Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str(); 409e8d8bef9SDimitry Andric Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line); 410e8d8bef9SDimitry Andric Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str(); 411e8d8bef9SDimitry Andric Opts.FallbackDebugPath = 412e8d8bef9SDimitry Andric Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); 413e8d8bef9SDimitry Andric Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line); 414fe6060f1SDimitry Andric parseIntArg(Args, OPT_print_source_context_lines_EQ, 415fe6060f1SDimitry Andric Config.SourceContextLines); 416e8d8bef9SDimitry Andric Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); 417e8d8bef9SDimitry Andric Opts.UntagAddresses = 418e8d8bef9SDimitry Andric Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); 419e8d8bef9SDimitry Andric Opts.UseDIA = Args.hasArg(OPT_use_dia); 420e8d8bef9SDimitry Andric #if !defined(LLVM_ENABLE_DIA_SDK) 421e8d8bef9SDimitry Andric if (Opts.UseDIA) { 422e8d8bef9SDimitry Andric WithColor::warning() << "DIA not available; using native PDB reader\n"; 423e8d8bef9SDimitry Andric Opts.UseDIA = false; 424e8d8bef9SDimitry Andric } 425e8d8bef9SDimitry Andric #endif 426e8d8bef9SDimitry Andric Opts.UseSymbolTable = true; 42781ad6265SDimitry Andric if (Args.hasArg(OPT_cache_size_EQ)) 42881ad6265SDimitry Andric parseIntArg(Args, OPT_cache_size_EQ, Opts.MaxCacheSize); 429fe6060f1SDimitry Andric Config.PrintAddress = Args.hasArg(OPT_addresses); 430fe6060f1SDimitry Andric Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None; 431fe6060f1SDimitry Andric Config.Pretty = Args.hasArg(OPT_pretty_print); 432fe6060f1SDimitry Andric Config.Verbose = Args.hasArg(OPT_verbose); 433e8d8bef9SDimitry Andric 434e8d8bef9SDimitry Andric for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { 435e8d8bef9SDimitry Andric StringRef Hint(A->getValue()); 436e8d8bef9SDimitry Andric if (sys::path::extension(Hint) == ".dSYM") { 437e8d8bef9SDimitry Andric Opts.DsymHints.emplace_back(Hint); 438e8d8bef9SDimitry Andric } else { 439e8d8bef9SDimitry Andric errs() << "Warning: invalid dSYM hint: \"" << Hint 440e8d8bef9SDimitry Andric << "\" (must have the '.dSYM' extension).\n"; 4410b57cec5SDimitry Andric } 4420b57cec5SDimitry Andric } 443e8d8bef9SDimitry Andric 444a4a491e2SDimitry Andric LLVMSymbolizer Symbolizer(Opts); 445a4a491e2SDimitry Andric 446*1ac55f4cSDimitry Andric if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod())) 447bdd1243dSDimitry Andric enableDebuginfod(Symbolizer, Args); 448a4a491e2SDimitry Andric 44981ad6265SDimitry Andric if (Args.hasArg(OPT_filter_markup)) { 450a4a491e2SDimitry Andric filterMarkup(Args, Symbolizer); 45181ad6265SDimitry Andric return 0; 45281ad6265SDimitry Andric } 45381ad6265SDimitry Andric 454fe6060f1SDimitry Andric auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM; 455e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) { 456fe6060f1SDimitry Andric if (strcmp(A->getValue(), "GNU") == 0) 457fe6060f1SDimitry Andric Style = OutputStyle::GNU; 458fe6060f1SDimitry Andric else if (strcmp(A->getValue(), "JSON") == 0) 459fe6060f1SDimitry Andric Style = OutputStyle::JSON; 460fe6060f1SDimitry Andric else 461fe6060f1SDimitry Andric Style = OutputStyle::LLVM; 462e8d8bef9SDimitry Andric } 463e8d8bef9SDimitry Andric 46481ad6265SDimitry Andric if (Args.hasArg(OPT_build_id_EQ) && Args.hasArg(OPT_obj_EQ)) { 46581ad6265SDimitry Andric errs() << "error: cannot specify both --build-id and --obj\n"; 46681ad6265SDimitry Andric return EXIT_FAILURE; 46781ad6265SDimitry Andric } 468bdd1243dSDimitry Andric object::BuildID BuildID = parseBuildIDArg(Args, OPT_build_id_EQ); 46981ad6265SDimitry Andric 470fe6060f1SDimitry Andric std::unique_ptr<DIPrinter> Printer; 471fe6060f1SDimitry Andric if (Style == OutputStyle::GNU) 472fe6060f1SDimitry Andric Printer = std::make_unique<GNUPrinter>(outs(), errs(), Config); 473fe6060f1SDimitry Andric else if (Style == OutputStyle::JSON) 474fe6060f1SDimitry Andric Printer = std::make_unique<JSONPrinter>(outs(), Config); 475fe6060f1SDimitry Andric else 476fe6060f1SDimitry Andric Printer = std::make_unique<LLVMPrinter>(outs(), errs(), Config); 4770b57cec5SDimitry Andric 478e8d8bef9SDimitry Andric std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT); 479e8d8bef9SDimitry Andric if (InputAddresses.empty()) { 4800b57cec5SDimitry Andric const int kMaxInputStringLength = 1024; 4810b57cec5SDimitry Andric char InputString[kMaxInputStringLength]; 4820b57cec5SDimitry Andric 4830b57cec5SDimitry Andric while (fgets(InputString, sizeof(InputString), stdin)) { 484480093f4SDimitry Andric // Strip newline characters. 485480093f4SDimitry Andric std::string StrippedInputString(InputString); 486e8d8bef9SDimitry Andric llvm::erase_if(StrippedInputString, 487e8d8bef9SDimitry Andric [](char c) { return c == '\r' || c == '\n'; }); 48881ad6265SDimitry Andric symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, 48981ad6265SDimitry Andric StrippedInputString, Symbolizer, *Printer); 4900b57cec5SDimitry Andric outs().flush(); 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric } else { 493fe6060f1SDimitry Andric Printer->listBegin(); 494e8d8bef9SDimitry Andric for (StringRef Address : InputAddresses) 49581ad6265SDimitry Andric symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, Address, 49681ad6265SDimitry Andric Symbolizer, *Printer); 497fe6060f1SDimitry Andric Printer->listEnd(); 4980b57cec5SDimitry Andric } 4990b57cec5SDimitry Andric 5000b57cec5SDimitry Andric return 0; 5010b57cec5SDimitry Andric } 502