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" 355f757f3fSDimitry Andric #include "llvm/Support/Errc.h" 360b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 375f757f3fSDimitry Andric #include "llvm/Support/LLVMDriver.h" 380b57cec5SDimitry Andric #include "llvm/Support/Path.h" 39e8d8bef9SDimitry Andric #include "llvm/Support/StringSaver.h" 4006c3fb27SDimitry Andric #include "llvm/Support/WithColor.h" 410b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 42480093f4SDimitry Andric #include <algorithm> 430b57cec5SDimitry Andric #include <cstdio> 440b57cec5SDimitry Andric #include <cstring> 45bdd1243dSDimitry Andric #include <iostream> 460b57cec5SDimitry Andric #include <string> 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric using namespace llvm; 490b57cec5SDimitry Andric using namespace symbolize; 500b57cec5SDimitry Andric 51e8d8bef9SDimitry Andric namespace { 52e8d8bef9SDimitry Andric enum ID { 53e8d8bef9SDimitry Andric OPT_INVALID = 0, // This is not an option ID. 545f757f3fSDimitry Andric #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 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 665f757f3fSDimitry Andric using namespace llvm::opt; 67bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = { 685f757f3fSDimitry Andric #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 69e8d8bef9SDimitry Andric #include "Opts.inc" 70e8d8bef9SDimitry Andric #undef OPTION 71e8d8bef9SDimitry Andric }; 720b57cec5SDimitry Andric 73bdd1243dSDimitry Andric class SymbolizerOptTable : public opt::GenericOptTable { 74e8d8bef9SDimitry Andric public: 75bdd1243dSDimitry Andric SymbolizerOptTable() : GenericOptTable(InfoTable) { 76fe6060f1SDimitry Andric setGroupedShortOptions(true); 77fe6060f1SDimitry Andric } 78e8d8bef9SDimitry Andric }; 79e8d8bef9SDimitry Andric } // namespace 800b57cec5SDimitry Andric 8106c3fb27SDimitry Andric static std::string ToolName; 8206c3fb27SDimitry Andric 835f757f3fSDimitry Andric static void printError(const ErrorInfoBase &EI, StringRef AuxInfo) { 8406c3fb27SDimitry Andric WithColor::error(errs(), ToolName); 855f757f3fSDimitry Andric if (!AuxInfo.empty()) 865f757f3fSDimitry Andric errs() << "'" << AuxInfo << "': "; 8706c3fb27SDimitry Andric EI.log(errs()); 8806c3fb27SDimitry Andric errs() << '\n'; 8906c3fb27SDimitry Andric } 9006c3fb27SDimitry Andric 910b57cec5SDimitry Andric template <typename T> 92fe6060f1SDimitry Andric static void print(const Request &Request, Expected<T> &ResOrErr, 93fe6060f1SDimitry Andric DIPrinter &Printer) { 94fe6060f1SDimitry Andric if (ResOrErr) { 95fe6060f1SDimitry Andric // No error, print the result. 96fe6060f1SDimitry Andric Printer.print(Request, *ResOrErr); 97fe6060f1SDimitry Andric return; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric 100fe6060f1SDimitry Andric // Handle the error. 101fe6060f1SDimitry Andric bool PrintEmpty = true; 102fe6060f1SDimitry Andric handleAllErrors(std::move(ResOrErr.takeError()), 103fe6060f1SDimitry Andric [&](const ErrorInfoBase &EI) { 10406c3fb27SDimitry Andric PrintEmpty = Printer.printError(Request, EI); 105fe6060f1SDimitry Andric }); 106fe6060f1SDimitry Andric 107fe6060f1SDimitry Andric if (PrintEmpty) 108fe6060f1SDimitry Andric Printer.print(Request, T()); 109fe6060f1SDimitry Andric } 110fe6060f1SDimitry Andric 111fe6060f1SDimitry Andric enum class OutputStyle { LLVM, GNU, JSON }; 112fe6060f1SDimitry Andric 1130b57cec5SDimitry Andric enum class Command { 1140b57cec5SDimitry Andric Code, 1150b57cec5SDimitry Andric Data, 1160b57cec5SDimitry Andric Frame, 1170b57cec5SDimitry Andric }; 1180b57cec5SDimitry Andric 119bdd1243dSDimitry Andric static void enableDebuginfod(LLVMSymbolizer &Symbolizer, 120bdd1243dSDimitry Andric const opt::ArgList &Args) { 12181ad6265SDimitry Andric static bool IsEnabled = false; 12281ad6265SDimitry Andric if (IsEnabled) 12381ad6265SDimitry Andric return; 12481ad6265SDimitry Andric IsEnabled = true; 12581ad6265SDimitry Andric // Look up symbols using the debuginfod client. 126bdd1243dSDimitry Andric Symbolizer.setBuildIDFetcher(std::make_unique<DebuginfodFetcher>( 127bdd1243dSDimitry Andric Args.getAllArgValues(OPT_debug_file_directory_EQ))); 12881ad6265SDimitry Andric // The HTTPClient must be initialized for use by the debuginfod client. 12981ad6265SDimitry Andric HTTPClient::initialize(); 13081ad6265SDimitry Andric } 13181ad6265SDimitry Andric 1325f757f3fSDimitry Andric static StringRef getSpaceDelimitedWord(StringRef &Source) { 1335f757f3fSDimitry Andric const char kDelimiters[] = " \n\r"; 1345f757f3fSDimitry Andric const char *Pos = Source.data(); 1355f757f3fSDimitry Andric StringRef Result; 1365f757f3fSDimitry Andric Pos += strspn(Pos, kDelimiters); 1375f757f3fSDimitry Andric if (*Pos == '"' || *Pos == '\'') { 1385f757f3fSDimitry Andric char Quote = *Pos; 1395f757f3fSDimitry Andric Pos++; 1405f757f3fSDimitry Andric const char *End = strchr(Pos, Quote); 1415f757f3fSDimitry Andric if (!End) 1425f757f3fSDimitry Andric return StringRef(); 1435f757f3fSDimitry Andric Result = StringRef(Pos, End - Pos); 1445f757f3fSDimitry Andric Pos = End + 1; 1455f757f3fSDimitry Andric } else { 1465f757f3fSDimitry Andric int NameLength = strcspn(Pos, kDelimiters); 1475f757f3fSDimitry Andric Result = StringRef(Pos, NameLength); 1485f757f3fSDimitry Andric Pos += NameLength; 1495f757f3fSDimitry Andric } 1505f757f3fSDimitry Andric Source = StringRef(Pos, Source.end() - Pos); 1515f757f3fSDimitry Andric return Result; 1525f757f3fSDimitry Andric } 1535f757f3fSDimitry Andric 1545f757f3fSDimitry Andric static Error makeStringError(StringRef Msg) { 1555f757f3fSDimitry Andric return make_error<StringError>(Msg, inconvertibleErrorCode()); 1565f757f3fSDimitry Andric } 1575f757f3fSDimitry Andric 1585f757f3fSDimitry Andric static Error parseCommand(StringRef BinaryName, bool IsAddr2Line, 159e8d8bef9SDimitry Andric StringRef InputString, Command &Cmd, 160bdd1243dSDimitry Andric std::string &ModuleName, object::BuildID &BuildID, 1615f757f3fSDimitry Andric StringRef &Symbol, uint64_t &Offset) { 1625f757f3fSDimitry Andric ModuleName = BinaryName; 1630b57cec5SDimitry Andric if (InputString.consume_front("CODE ")) { 1640b57cec5SDimitry Andric Cmd = Command::Code; 1650b57cec5SDimitry Andric } else if (InputString.consume_front("DATA ")) { 1660b57cec5SDimitry Andric Cmd = Command::Data; 1670b57cec5SDimitry Andric } else if (InputString.consume_front("FRAME ")) { 1680b57cec5SDimitry Andric Cmd = Command::Frame; 1690b57cec5SDimitry Andric } else { 1700b57cec5SDimitry Andric // If no cmd, assume it's CODE. 1710b57cec5SDimitry Andric Cmd = Command::Code; 1720b57cec5SDimitry Andric } 17381ad6265SDimitry Andric 1745f757f3fSDimitry Andric // Parse optional input file specification. 17581ad6265SDimitry Andric bool HasFilePrefix = false; 17681ad6265SDimitry Andric bool HasBuildIDPrefix = false; 1775f757f3fSDimitry Andric while (!InputString.empty()) { 1785f757f3fSDimitry Andric InputString = InputString.ltrim(); 17981ad6265SDimitry Andric if (InputString.consume_front("FILE:")) { 1805f757f3fSDimitry Andric if (HasFilePrefix || HasBuildIDPrefix) 1815f757f3fSDimitry Andric return makeStringError("duplicate input file specification prefix"); 18281ad6265SDimitry Andric HasFilePrefix = true; 18381ad6265SDimitry Andric continue; 18481ad6265SDimitry Andric } 18581ad6265SDimitry Andric if (InputString.consume_front("BUILDID:")) { 1865f757f3fSDimitry Andric if (HasBuildIDPrefix || HasFilePrefix) 1875f757f3fSDimitry Andric return makeStringError("duplicate input file specification prefix"); 18881ad6265SDimitry Andric HasBuildIDPrefix = true; 18981ad6265SDimitry Andric continue; 19081ad6265SDimitry Andric } 19181ad6265SDimitry Andric break; 19281ad6265SDimitry Andric } 19381ad6265SDimitry Andric 1945f757f3fSDimitry Andric // If an input file is not specified on the command line, try to extract it 1955f757f3fSDimitry Andric // from the command. 1965f757f3fSDimitry Andric if (HasBuildIDPrefix || HasFilePrefix) { 1975f757f3fSDimitry Andric InputString = InputString.ltrim(); 1985f757f3fSDimitry Andric if (InputString.empty()) { 1995f757f3fSDimitry Andric if (HasFilePrefix) 2005f757f3fSDimitry Andric return makeStringError("must be followed by an input file"); 2015f757f3fSDimitry Andric else 2025f757f3fSDimitry Andric return makeStringError("must be followed by a hash"); 2030b57cec5SDimitry Andric } 2045f757f3fSDimitry Andric 2055f757f3fSDimitry Andric if (!BinaryName.empty() || !BuildID.empty()) 2065f757f3fSDimitry Andric return makeStringError("input file has already been specified"); 2075f757f3fSDimitry Andric 2085f757f3fSDimitry Andric StringRef Name = getSpaceDelimitedWord(InputString); 2095f757f3fSDimitry Andric if (Name.empty()) 2105f757f3fSDimitry Andric return makeStringError("unbalanced quotes in input file name"); 21181ad6265SDimitry Andric if (HasBuildIDPrefix) { 2125f757f3fSDimitry Andric BuildID = parseBuildID(Name); 21381ad6265SDimitry Andric if (BuildID.empty()) 2145f757f3fSDimitry Andric return makeStringError("wrong format of build-id"); 2150b57cec5SDimitry Andric } else { 2165f757f3fSDimitry Andric ModuleName = Name; 2170b57cec5SDimitry Andric } 2185f757f3fSDimitry Andric } else if (BinaryName.empty() && BuildID.empty()) { 2195f757f3fSDimitry Andric // No input file has been specified. If the input string contains at least 2205f757f3fSDimitry Andric // two items, assume that the first item is a file name. 2215f757f3fSDimitry Andric ModuleName = getSpaceDelimitedWord(InputString); 2225f757f3fSDimitry Andric if (ModuleName.empty()) 2235f757f3fSDimitry Andric return makeStringError("no input filename has been specified"); 2245f757f3fSDimitry Andric } 2255f757f3fSDimitry Andric 2265f757f3fSDimitry Andric // Parse address specification, which can be an offset in module or a 2275f757f3fSDimitry Andric // symbol with optional offset. 2285f757f3fSDimitry Andric InputString = InputString.trim(); 2295f757f3fSDimitry Andric if (InputString.empty()) 2305f757f3fSDimitry Andric return makeStringError("no module offset has been specified"); 2315f757f3fSDimitry Andric 2325f757f3fSDimitry Andric // If input string contains a space, ignore everything after it. This behavior 2335f757f3fSDimitry Andric // is consistent with GNU addr2line. 2345f757f3fSDimitry Andric int AddrSpecLength = InputString.find_first_of(" \n\r"); 2355f757f3fSDimitry Andric StringRef AddrSpec = InputString.substr(0, AddrSpecLength); 2365f757f3fSDimitry Andric bool StartsWithDigit = std::isdigit(AddrSpec.front()); 2375f757f3fSDimitry Andric 2385f757f3fSDimitry Andric // GNU addr2line assumes the address is hexadecimal and allows a redundant 2395ffd83dbSDimitry Andric // "0x" or "0X" prefix; do the same for compatibility. 2405ffd83dbSDimitry Andric if (IsAddr2Line) 2415f757f3fSDimitry Andric AddrSpec.consume_front("0x") || AddrSpec.consume_front("0X"); 2425f757f3fSDimitry Andric 2435f757f3fSDimitry Andric // If address specification is a number, treat it as a module offset. 2445f757f3fSDimitry Andric if (!AddrSpec.getAsInteger(IsAddr2Line ? 16 : 0, Offset)) { 2455f757f3fSDimitry Andric // Module offset is an address. 2465f757f3fSDimitry Andric Symbol = StringRef(); 2475f757f3fSDimitry Andric return Error::success(); 2485f757f3fSDimitry Andric } 2495f757f3fSDimitry Andric 2505f757f3fSDimitry Andric // If address specification starts with a digit, but is not a number, consider 2515f757f3fSDimitry Andric // it as invalid. 2525f757f3fSDimitry Andric if (StartsWithDigit || AddrSpec.empty()) 2535f757f3fSDimitry Andric return makeStringError("expected a number as module offset"); 2545f757f3fSDimitry Andric 2555f757f3fSDimitry Andric // Otherwise it is a symbol name, potentially with an offset. 2565f757f3fSDimitry Andric Symbol = AddrSpec; 2575f757f3fSDimitry Andric Offset = 0; 2585f757f3fSDimitry Andric 2595f757f3fSDimitry Andric // If the address specification contains '+', try treating it as 2605f757f3fSDimitry Andric // "symbol + offset". 2615f757f3fSDimitry Andric size_t Plus = AddrSpec.rfind('+'); 2625f757f3fSDimitry Andric if (Plus != StringRef::npos) { 2635f757f3fSDimitry Andric StringRef SymbolStr = AddrSpec.take_front(Plus); 2645f757f3fSDimitry Andric StringRef OffsetStr = AddrSpec.substr(Plus + 1); 2655f757f3fSDimitry Andric if (!SymbolStr.empty() && !OffsetStr.empty() && 2665f757f3fSDimitry Andric !OffsetStr.getAsInteger(0, Offset)) { 2675f757f3fSDimitry Andric Symbol = SymbolStr; 2685f757f3fSDimitry Andric return Error::success(); 2695f757f3fSDimitry Andric } 2705f757f3fSDimitry Andric // The found '+' is not an offset delimiter. 2715f757f3fSDimitry Andric } 2725f757f3fSDimitry Andric 2735f757f3fSDimitry Andric return Error::success(); 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric 27681ad6265SDimitry Andric template <typename T> 27781ad6265SDimitry Andric void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd, 2785f757f3fSDimitry Andric StringRef Symbol, uint64_t Offset, uint64_t AdjustVMA, 2795f757f3fSDimitry Andric bool ShouldInline, OutputStyle Style, 2805f757f3fSDimitry Andric LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { 281fe6060f1SDimitry Andric uint64_t AdjustedOffset = Offset - AdjustVMA; 28281ad6265SDimitry Andric object::SectionedAddress Address = {AdjustedOffset, 28381ad6265SDimitry Andric object::SectionedAddress::UndefSection}; 2845f757f3fSDimitry Andric Request SymRequest = { 2855f757f3fSDimitry Andric ModuleName, Symbol.empty() ? std::make_optional(Offset) : std::nullopt, 2865f757f3fSDimitry Andric Symbol}; 2870b57cec5SDimitry Andric if (Cmd == Command::Data) { 28881ad6265SDimitry Andric Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(ModuleSpec, Address); 28906c3fb27SDimitry Andric print(SymRequest, ResOrErr, Printer); 2900b57cec5SDimitry Andric } else if (Cmd == Command::Frame) { 29181ad6265SDimitry Andric Expected<std::vector<DILocal>> ResOrErr = 29281ad6265SDimitry Andric Symbolizer.symbolizeFrame(ModuleSpec, Address); 29306c3fb27SDimitry Andric print(SymRequest, ResOrErr, Printer); 2945f757f3fSDimitry Andric } else if (!Symbol.empty()) { 2955f757f3fSDimitry Andric Expected<std::vector<DILineInfo>> ResOrErr = 2965f757f3fSDimitry Andric Symbolizer.findSymbol(ModuleSpec, Symbol, Offset); 2975f757f3fSDimitry Andric print(SymRequest, ResOrErr, Printer); 29881ad6265SDimitry Andric } else if (ShouldInline) { 29981ad6265SDimitry Andric Expected<DIInliningInfo> ResOrErr = 30081ad6265SDimitry Andric Symbolizer.symbolizeInlinedCode(ModuleSpec, Address); 30106c3fb27SDimitry Andric print(SymRequest, ResOrErr, Printer); 302fe6060f1SDimitry Andric } else if (Style == OutputStyle::GNU) { 303e8d8bef9SDimitry Andric // With PrintFunctions == FunctionNameKind::LinkageName (default) 304e8d8bef9SDimitry Andric // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() 3050b57cec5SDimitry Andric // may override the name of an inlined function with the name of the topmost 3060b57cec5SDimitry Andric // caller function in the inlining chain. This contradicts the existing 3070b57cec5SDimitry Andric // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only 3080b57cec5SDimitry Andric // the topmost function, which suits our needs better. 30981ad6265SDimitry Andric Expected<DIInliningInfo> ResOrErr = 31081ad6265SDimitry Andric Symbolizer.symbolizeInlinedCode(ModuleSpec, Address); 311fe6060f1SDimitry Andric Expected<DILineInfo> Res0OrErr = 312fe6060f1SDimitry Andric !ResOrErr 313fe6060f1SDimitry Andric ? Expected<DILineInfo>(ResOrErr.takeError()) 314fe6060f1SDimitry Andric : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo() 315fe6060f1SDimitry Andric : ResOrErr->getFrame(0)); 31606c3fb27SDimitry Andric print(SymRequest, Res0OrErr, Printer); 317d409305fSDimitry Andric } else { 31881ad6265SDimitry Andric Expected<DILineInfo> ResOrErr = 31981ad6265SDimitry Andric Symbolizer.symbolizeCode(ModuleSpec, Address); 32006c3fb27SDimitry Andric print(SymRequest, ResOrErr, Printer); 321d409305fSDimitry Andric } 32281ad6265SDimitry Andric Symbolizer.pruneCache(); 32381ad6265SDimitry Andric } 32481ad6265SDimitry Andric 3255f757f3fSDimitry Andric static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) { 3265f757f3fSDimitry Andric Request SymRequest = {ModuleName, std::nullopt, StringRef()}; 3275f757f3fSDimitry Andric Printer.print(SymRequest, DILineInfo()); 3285f757f3fSDimitry Andric } 3295f757f3fSDimitry Andric 33081ad6265SDimitry Andric static void symbolizeInput(const opt::InputArgList &Args, 331bdd1243dSDimitry Andric object::BuildIDRef IncomingBuildID, 33281ad6265SDimitry Andric uint64_t AdjustVMA, bool IsAddr2Line, 33381ad6265SDimitry Andric OutputStyle Style, StringRef InputString, 33481ad6265SDimitry Andric LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { 33581ad6265SDimitry Andric Command Cmd; 33681ad6265SDimitry Andric std::string ModuleName; 337bdd1243dSDimitry Andric object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end()); 33881ad6265SDimitry Andric uint64_t Offset = 0; 3395f757f3fSDimitry Andric StringRef Symbol; 340*0fca6ea1SDimitry Andric 341*0fca6ea1SDimitry Andric // An empty input string may be used to check if the process is alive and 342*0fca6ea1SDimitry Andric // responding to input. Do not emit a message on stderr in this case but 343*0fca6ea1SDimitry Andric // respond on stdout. 344*0fca6ea1SDimitry Andric if (InputString.empty()) { 345*0fca6ea1SDimitry Andric printUnknownLineInfo(ModuleName, Printer); 346*0fca6ea1SDimitry Andric return; 347*0fca6ea1SDimitry Andric } 3485f757f3fSDimitry Andric if (Error E = parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, 3495f757f3fSDimitry Andric StringRef(InputString), Cmd, ModuleName, BuildID, 3505f757f3fSDimitry Andric Symbol, Offset)) { 3515f757f3fSDimitry Andric handleAllErrors(std::move(E), [&](const StringError &EI) { 3525f757f3fSDimitry Andric printError(EI, InputString); 3535f757f3fSDimitry Andric printUnknownLineInfo(ModuleName, Printer); 3545f757f3fSDimitry Andric }); 35581ad6265SDimitry Andric return; 35681ad6265SDimitry Andric } 35781ad6265SDimitry Andric bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line); 35881ad6265SDimitry Andric if (!BuildID.empty()) { 35981ad6265SDimitry Andric assert(ModuleName.empty()); 36081ad6265SDimitry Andric if (!Args.hasArg(OPT_no_debuginfod)) 361bdd1243dSDimitry Andric enableDebuginfod(Symbolizer, Args); 36281ad6265SDimitry Andric std::string BuildIDStr = toHex(BuildID); 3635f757f3fSDimitry Andric executeCommand(BuildIDStr, BuildID, Cmd, Symbol, Offset, AdjustVMA, 3645f757f3fSDimitry Andric ShouldInline, Style, Symbolizer, Printer); 36581ad6265SDimitry Andric } else { 3665f757f3fSDimitry Andric executeCommand(ModuleName, ModuleName, Cmd, Symbol, Offset, AdjustVMA, 3675f757f3fSDimitry Andric ShouldInline, Style, Symbolizer, Printer); 36881ad6265SDimitry Andric } 3690b57cec5SDimitry Andric } 3700b57cec5SDimitry Andric 371e8d8bef9SDimitry Andric static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, 372e8d8bef9SDimitry Andric raw_ostream &OS) { 373e8d8bef9SDimitry Andric const char HelpText[] = " [options] addresses..."; 374fe6060f1SDimitry Andric Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(), 375e8d8bef9SDimitry Andric ToolName.str().c_str()); 376e8d8bef9SDimitry Andric // TODO Replace this with OptTable API once it adds extrahelp support. 377e8d8bef9SDimitry Andric OS << "\nPass @FILE as argument to read options from FILE.\n"; 378e8d8bef9SDimitry Andric } 379e8d8bef9SDimitry Andric 380e8d8bef9SDimitry Andric static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line, 381e8d8bef9SDimitry Andric StringSaver &Saver, 382e8d8bef9SDimitry Andric SymbolizerOptTable &Tbl) { 383e8d8bef9SDimitry Andric StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer"; 384e8d8bef9SDimitry Andric // The environment variable specifies initial options which can be overridden 385e8d8bef9SDimitry Andric // by commnad line options. 386e8d8bef9SDimitry Andric Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" 387e8d8bef9SDimitry Andric : "LLVM_SYMBOLIZER_OPTS"); 388e8d8bef9SDimitry Andric bool HasError = false; 389e8d8bef9SDimitry Andric opt::InputArgList Args = 390e8d8bef9SDimitry Andric Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { 391e8d8bef9SDimitry Andric errs() << ("error: " + Msg + "\n"); 392e8d8bef9SDimitry Andric HasError = true; 393e8d8bef9SDimitry Andric }); 394e8d8bef9SDimitry Andric if (HasError) 395e8d8bef9SDimitry Andric exit(1); 396e8d8bef9SDimitry Andric if (Args.hasArg(OPT_help)) { 397e8d8bef9SDimitry Andric printHelp(ToolName, Tbl, outs()); 398e8d8bef9SDimitry Andric exit(0); 399e8d8bef9SDimitry Andric } 400e8d8bef9SDimitry Andric if (Args.hasArg(OPT_version)) { 401e8d8bef9SDimitry Andric outs() << ToolName << '\n'; 402e8d8bef9SDimitry Andric cl::PrintVersionMessage(); 403e8d8bef9SDimitry Andric exit(0); 404e8d8bef9SDimitry Andric } 405e8d8bef9SDimitry Andric 406e8d8bef9SDimitry Andric return Args; 407e8d8bef9SDimitry Andric } 408e8d8bef9SDimitry Andric 409e8d8bef9SDimitry Andric template <typename T> 410e8d8bef9SDimitry Andric static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { 411e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(ID)) { 412e8d8bef9SDimitry Andric StringRef V(A->getValue()); 413e8d8bef9SDimitry Andric if (!llvm::to_integer(V, Value, 0)) { 414e8d8bef9SDimitry Andric errs() << A->getSpelling() + 415e8d8bef9SDimitry Andric ": expected a non-negative integer, but got '" + V + "'"; 416e8d8bef9SDimitry Andric exit(1); 417e8d8bef9SDimitry Andric } 418e8d8bef9SDimitry Andric } else { 419e8d8bef9SDimitry Andric Value = 0; 420e8d8bef9SDimitry Andric } 421e8d8bef9SDimitry Andric } 422e8d8bef9SDimitry Andric 423e8d8bef9SDimitry Andric static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args, 424e8d8bef9SDimitry Andric bool IsAddr2Line) { 425e8d8bef9SDimitry Andric if (Args.hasArg(OPT_functions)) 426e8d8bef9SDimitry Andric return FunctionNameKind::LinkageName; 427e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ)) 428e8d8bef9SDimitry Andric return StringSwitch<FunctionNameKind>(A->getValue()) 429e8d8bef9SDimitry Andric .Case("none", FunctionNameKind::None) 430e8d8bef9SDimitry Andric .Case("short", FunctionNameKind::ShortName) 431e8d8bef9SDimitry Andric .Default(FunctionNameKind::LinkageName); 432e8d8bef9SDimitry Andric return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName; 433e8d8bef9SDimitry Andric } 434e8d8bef9SDimitry Andric 435bdd1243dSDimitry Andric static std::optional<bool> parseColorArg(const opt::InputArgList &Args) { 43681ad6265SDimitry Andric if (Args.hasArg(OPT_color)) 43781ad6265SDimitry Andric return true; 43881ad6265SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_color_EQ)) 439bdd1243dSDimitry Andric return StringSwitch<std::optional<bool>>(A->getValue()) 44081ad6265SDimitry Andric .Case("always", true) 44181ad6265SDimitry Andric .Case("never", false) 442bdd1243dSDimitry Andric .Case("auto", std::nullopt); 443bdd1243dSDimitry Andric return std::nullopt; 44481ad6265SDimitry Andric } 44581ad6265SDimitry Andric 446bdd1243dSDimitry Andric static object::BuildID parseBuildIDArg(const opt::InputArgList &Args, int ID) { 44781ad6265SDimitry Andric const opt::Arg *A = Args.getLastArg(ID); 44881ad6265SDimitry Andric if (!A) 44981ad6265SDimitry Andric return {}; 45081ad6265SDimitry Andric 45181ad6265SDimitry Andric StringRef V(A->getValue()); 452bdd1243dSDimitry Andric object::BuildID BuildID = parseBuildID(V); 45381ad6265SDimitry Andric if (BuildID.empty()) { 45481ad6265SDimitry Andric errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n"; 45581ad6265SDimitry Andric exit(1); 45681ad6265SDimitry Andric } 45781ad6265SDimitry Andric return BuildID; 45881ad6265SDimitry Andric } 45981ad6265SDimitry Andric 460fcaf7f86SDimitry Andric // Symbolize markup from stdin and write the result to stdout. 461a4a491e2SDimitry Andric static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) { 462a4a491e2SDimitry Andric MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args)); 463fcaf7f86SDimitry Andric std::string InputString; 464fcaf7f86SDimitry Andric while (std::getline(std::cin, InputString)) { 46581ad6265SDimitry Andric InputString += '\n'; 4665f757f3fSDimitry Andric Filter.filter(std::move(InputString)); 46781ad6265SDimitry Andric } 468fcaf7f86SDimitry Andric Filter.finish(); 46981ad6265SDimitry Andric } 47081ad6265SDimitry Andric 4715f757f3fSDimitry Andric int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) { 472e8d8bef9SDimitry Andric sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded); 4730b57cec5SDimitry Andric 47406c3fb27SDimitry Andric ToolName = argv[0]; 47506c3fb27SDimitry Andric bool IsAddr2Line = sys::path::stem(ToolName).contains("addr2line"); 476e8d8bef9SDimitry Andric BumpPtrAllocator A; 477e8d8bef9SDimitry Andric StringSaver Saver(A); 478e8d8bef9SDimitry Andric SymbolizerOptTable Tbl; 479e8d8bef9SDimitry Andric opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl); 4800b57cec5SDimitry Andric 4810b57cec5SDimitry Andric LLVMSymbolizer::Options Opts; 482e8d8bef9SDimitry Andric uint64_t AdjustVMA; 483fe6060f1SDimitry Andric PrinterConfig Config; 484e8d8bef9SDimitry Andric parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); 485e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { 486e8d8bef9SDimitry Andric Opts.PathStyle = 487e8d8bef9SDimitry Andric A->getOption().matches(OPT_basenames) 488e8d8bef9SDimitry Andric ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly 489e8d8bef9SDimitry Andric : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; 4900b57cec5SDimitry Andric } else { 491e8d8bef9SDimitry Andric Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; 492e8d8bef9SDimitry Andric } 493e8d8bef9SDimitry Andric Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ); 494e8d8bef9SDimitry Andric Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str(); 495e8d8bef9SDimitry Andric Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line); 496e8d8bef9SDimitry Andric Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str(); 497e8d8bef9SDimitry Andric Opts.FallbackDebugPath = 498e8d8bef9SDimitry Andric Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); 499e8d8bef9SDimitry Andric Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line); 500fe6060f1SDimitry Andric parseIntArg(Args, OPT_print_source_context_lines_EQ, 501fe6060f1SDimitry Andric Config.SourceContextLines); 502e8d8bef9SDimitry Andric Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); 503e8d8bef9SDimitry Andric Opts.UntagAddresses = 504e8d8bef9SDimitry Andric Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); 505e8d8bef9SDimitry Andric Opts.UseDIA = Args.hasArg(OPT_use_dia); 506e8d8bef9SDimitry Andric #if !defined(LLVM_ENABLE_DIA_SDK) 507e8d8bef9SDimitry Andric if (Opts.UseDIA) { 508e8d8bef9SDimitry Andric WithColor::warning() << "DIA not available; using native PDB reader\n"; 509e8d8bef9SDimitry Andric Opts.UseDIA = false; 510e8d8bef9SDimitry Andric } 511e8d8bef9SDimitry Andric #endif 512e8d8bef9SDimitry Andric Opts.UseSymbolTable = true; 51381ad6265SDimitry Andric if (Args.hasArg(OPT_cache_size_EQ)) 51481ad6265SDimitry Andric parseIntArg(Args, OPT_cache_size_EQ, Opts.MaxCacheSize); 515fe6060f1SDimitry Andric Config.PrintAddress = Args.hasArg(OPT_addresses); 516fe6060f1SDimitry Andric Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None; 517fe6060f1SDimitry Andric Config.Pretty = Args.hasArg(OPT_pretty_print); 518fe6060f1SDimitry Andric Config.Verbose = Args.hasArg(OPT_verbose); 519e8d8bef9SDimitry Andric 520e8d8bef9SDimitry Andric for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { 521e8d8bef9SDimitry Andric StringRef Hint(A->getValue()); 522e8d8bef9SDimitry Andric if (sys::path::extension(Hint) == ".dSYM") { 523e8d8bef9SDimitry Andric Opts.DsymHints.emplace_back(Hint); 524e8d8bef9SDimitry Andric } else { 525e8d8bef9SDimitry Andric errs() << "Warning: invalid dSYM hint: \"" << Hint 526e8d8bef9SDimitry Andric << "\" (must have the '.dSYM' extension).\n"; 5270b57cec5SDimitry Andric } 5280b57cec5SDimitry Andric } 529e8d8bef9SDimitry Andric 530a4a491e2SDimitry Andric LLVMSymbolizer Symbolizer(Opts); 531a4a491e2SDimitry Andric 5321ac55f4cSDimitry Andric if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod())) 533bdd1243dSDimitry Andric enableDebuginfod(Symbolizer, Args); 534a4a491e2SDimitry Andric 53581ad6265SDimitry Andric if (Args.hasArg(OPT_filter_markup)) { 536a4a491e2SDimitry Andric filterMarkup(Args, Symbolizer); 53781ad6265SDimitry Andric return 0; 53881ad6265SDimitry Andric } 53981ad6265SDimitry Andric 540fe6060f1SDimitry Andric auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM; 541e8d8bef9SDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) { 542fe6060f1SDimitry Andric if (strcmp(A->getValue(), "GNU") == 0) 543fe6060f1SDimitry Andric Style = OutputStyle::GNU; 544fe6060f1SDimitry Andric else if (strcmp(A->getValue(), "JSON") == 0) 545fe6060f1SDimitry Andric Style = OutputStyle::JSON; 546fe6060f1SDimitry Andric else 547fe6060f1SDimitry Andric Style = OutputStyle::LLVM; 548e8d8bef9SDimitry Andric } 549e8d8bef9SDimitry Andric 55081ad6265SDimitry Andric if (Args.hasArg(OPT_build_id_EQ) && Args.hasArg(OPT_obj_EQ)) { 55181ad6265SDimitry Andric errs() << "error: cannot specify both --build-id and --obj\n"; 55281ad6265SDimitry Andric return EXIT_FAILURE; 55381ad6265SDimitry Andric } 554bdd1243dSDimitry Andric object::BuildID BuildID = parseBuildIDArg(Args, OPT_build_id_EQ); 55581ad6265SDimitry Andric 556fe6060f1SDimitry Andric std::unique_ptr<DIPrinter> Printer; 557fe6060f1SDimitry Andric if (Style == OutputStyle::GNU) 55806c3fb27SDimitry Andric Printer = std::make_unique<GNUPrinter>(outs(), printError, Config); 559fe6060f1SDimitry Andric else if (Style == OutputStyle::JSON) 560fe6060f1SDimitry Andric Printer = std::make_unique<JSONPrinter>(outs(), Config); 561fe6060f1SDimitry Andric else 56206c3fb27SDimitry Andric Printer = std::make_unique<LLVMPrinter>(outs(), printError, Config); 56306c3fb27SDimitry Andric 56406c3fb27SDimitry Andric // When an input file is specified, exit immediately if the file cannot be 56506c3fb27SDimitry Andric // read. If getOrCreateModuleInfo succeeds, symbolizeInput will reuse the 56606c3fb27SDimitry Andric // cached file handle. 56706c3fb27SDimitry Andric if (auto *Arg = Args.getLastArg(OPT_obj_EQ); Arg) { 56806c3fb27SDimitry Andric auto Status = Symbolizer.getOrCreateModuleInfo(Arg->getValue()); 56906c3fb27SDimitry Andric if (!Status) { 5705f757f3fSDimitry Andric Request SymRequest = {Arg->getValue(), 0, StringRef()}; 57106c3fb27SDimitry Andric handleAllErrors(Status.takeError(), [&](const ErrorInfoBase &EI) { 57206c3fb27SDimitry Andric Printer->printError(SymRequest, EI); 57306c3fb27SDimitry Andric }); 57406c3fb27SDimitry Andric return EXIT_FAILURE; 57506c3fb27SDimitry Andric } 57606c3fb27SDimitry Andric } 5770b57cec5SDimitry Andric 578e8d8bef9SDimitry Andric std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT); 579e8d8bef9SDimitry Andric if (InputAddresses.empty()) { 5800b57cec5SDimitry Andric const int kMaxInputStringLength = 1024; 5810b57cec5SDimitry Andric char InputString[kMaxInputStringLength]; 5820b57cec5SDimitry Andric 5830b57cec5SDimitry Andric while (fgets(InputString, sizeof(InputString), stdin)) { 584480093f4SDimitry Andric // Strip newline characters. 585480093f4SDimitry Andric std::string StrippedInputString(InputString); 586e8d8bef9SDimitry Andric llvm::erase_if(StrippedInputString, 587e8d8bef9SDimitry Andric [](char c) { return c == '\r' || c == '\n'; }); 58881ad6265SDimitry Andric symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, 58981ad6265SDimitry Andric StrippedInputString, Symbolizer, *Printer); 5900b57cec5SDimitry Andric outs().flush(); 5910b57cec5SDimitry Andric } 5920b57cec5SDimitry Andric } else { 593fe6060f1SDimitry Andric Printer->listBegin(); 594e8d8bef9SDimitry Andric for (StringRef Address : InputAddresses) 59581ad6265SDimitry Andric symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, Address, 59681ad6265SDimitry Andric Symbolizer, *Printer); 597fe6060f1SDimitry Andric Printer->listEnd(); 5980b57cec5SDimitry Andric } 5990b57cec5SDimitry Andric 6000b57cec5SDimitry Andric return 0; 6010b57cec5SDimitry Andric } 602