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:
SymbolizerOptTable()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
printError(const ErrorInfoBase & EI,StringRef AuxInfo)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>
print(const Request & Request,Expected<T> & ResOrErr,DIPrinter & Printer)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
enableDebuginfod(LLVMSymbolizer & Symbolizer,const opt::ArgList & Args)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
getSpaceDelimitedWord(StringRef & Source)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
makeStringError(StringRef Msg)1545f757f3fSDimitry Andric static Error makeStringError(StringRef Msg) {
1555f757f3fSDimitry Andric return make_error<StringError>(Msg, inconvertibleErrorCode());
1565f757f3fSDimitry Andric }
1575f757f3fSDimitry Andric
parseCommand(StringRef BinaryName,bool IsAddr2Line,StringRef InputString,Command & Cmd,std::string & ModuleName,object::BuildID & BuildID,StringRef & Symbol,uint64_t & Offset)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>
executeCommand(StringRef ModuleName,const T & ModuleSpec,Command Cmd,StringRef Symbol,uint64_t Offset,uint64_t AdjustVMA,bool ShouldInline,OutputStyle Style,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)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
printUnknownLineInfo(std::string ModuleName,DIPrinter & Printer)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
symbolizeInput(const opt::InputArgList & Args,object::BuildIDRef IncomingBuildID,uint64_t AdjustVMA,bool IsAddr2Line,OutputStyle Style,StringRef InputString,LLVMSymbolizer & Symbolizer,DIPrinter & Printer)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
printHelp(StringRef ToolName,const SymbolizerOptTable & Tbl,raw_ostream & OS)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
parseOptions(int Argc,char * Argv[],bool IsAddr2Line,StringSaver & Saver,SymbolizerOptTable & Tbl)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>
parseIntArg(const opt::InputArgList & Args,int ID,T & Value)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
decideHowToPrintFunctions(const opt::InputArgList & Args,bool IsAddr2Line)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
parseColorArg(const opt::InputArgList & Args)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
parseBuildIDArg(const opt::InputArgList & Args,int ID)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.
filterMarkup(const opt::InputArgList & Args,LLVMSymbolizer & Symbolizer)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
llvm_symbolizer_main(int argc,char ** argv,const llvm::ToolContext &)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