xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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