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