xref: /llvm-project/llvm/tools/llvm-debuginfod-find/llvm-debuginfod-find.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
1 //===-- llvm-debuginfod-find.cpp - Simple CLI for libdebuginfod-client ----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file contains the llvm-debuginfod-find tool. This tool
11 /// queries the debuginfod servers in the DEBUGINFOD_URLS environment
12 /// variable (delimited by space (" ")) for the executable,
13 /// debuginfo, or specified source file of the binary matching the
14 /// given build-id.
15 ///
16 //===----------------------------------------------------------------------===//
17 
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Debuginfod/BuildIDFetcher.h"
21 #include "llvm/Debuginfod/Debuginfod.h"
22 #include "llvm/Debuginfod/HTTPClient.h"
23 #include "llvm/Option/ArgList.h"
24 #include "llvm/Option/Option.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/InitLLVM.h"
27 #include "llvm/Support/LLVMDriver.h"
28 
29 using namespace llvm;
30 
31 // Command-line option boilerplate.
32 namespace {
33 enum ID {
34   OPT_INVALID = 0, // This is not an option ID.
35 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
36 #include "Opts.inc"
37 #undef OPTION
38 };
39 
40 #define OPTTABLE_STR_TABLE_CODE
41 #include "Opts.inc"
42 #undef OPTTABLE_STR_TABLE_CODE
43 
44 #define OPTTABLE_PREFIXES_TABLE_CODE
45 #include "Opts.inc"
46 #undef OPTTABLE_PREFIXES_TABLE_CODE
47 
48 using namespace llvm::opt;
49 static constexpr opt::OptTable::Info InfoTable[] = {
50 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
51 #include "Opts.inc"
52 #undef OPTION
53 };
54 
55 class DebuginfodFindOptTable : public opt::GenericOptTable {
56 public:
57   DebuginfodFindOptTable()
58       : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
59 };
60 
61 } // end anonymous namespace
62 
63 static std::string InputBuildID;
64 static bool FetchExecutable;
65 static bool FetchDebuginfo;
66 static std::string FetchSource;
67 static bool DumpToStdout;
68 static std::vector<std::string> DebugFileDirectory;
69 
70 static void parseArgs(int argc, char **argv) {
71   DebuginfodFindOptTable Tbl;
72   llvm::BumpPtrAllocator A;
73   llvm::StringSaver Saver{A};
74   opt::InputArgList Args =
75       Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
76         llvm::errs() << Msg << '\n';
77         std::exit(1);
78       });
79 
80   if (Args.hasArg(OPT_help)) {
81     Tbl.printHelp(
82         llvm::outs(), "llvm-debuginfod-find [options] <input build_id>",
83         "llvm-debuginfod-find: Fetch debuginfod artifacts\n\n"
84         "This program is a frontend to the debuginfod client library. The "
85         "cache directory, request timeout (in seconds), and debuginfod server "
86         "urls are set by these environment variables:\n"
87         "DEBUGINFOD_CACHE_PATH (default set by sys::path::cache_directory)\n"
88         "DEBUGINFOD_TIMEOUT (defaults to 90s)\n"
89         "DEBUGINFOD_URLS=[comma separated URLs] (defaults to empty)");
90     std::exit(0);
91   }
92 
93   InputBuildID = Args.getLastArgValue(OPT_INPUT);
94 
95   FetchExecutable = Args.hasArg(OPT_fetch_executable);
96   FetchDebuginfo = Args.hasArg(OPT_fetch_debuginfo);
97   DumpToStdout = Args.hasArg(OPT_dump_to_stdout);
98   FetchSource = Args.getLastArgValue(OPT_fetch_source, "");
99   DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory);
100 }
101 
102 [[noreturn]] static void helpExit() {
103   errs() << "Must specify exactly one of --executable, "
104             "--source=/path/to/file, or --debuginfo.\n";
105   exit(1);
106 }
107 
108 ExitOnError ExitOnDebuginfodFindError;
109 
110 static std::string fetchDebugInfo(object::BuildIDRef BuildID);
111 
112 int llvm_debuginfod_find_main(int argc, char **argv,
113                               const llvm::ToolContext &) {
114   // InitLLVM X(argc, argv);
115   HTTPClient::initialize();
116   parseArgs(argc, argv);
117 
118   if (FetchExecutable + FetchDebuginfo + (FetchSource != "") != 1)
119     helpExit();
120 
121   std::string IDString;
122   if (!tryGetFromHex(InputBuildID, IDString)) {
123     errs() << "Build ID " << InputBuildID << " is not a hex string.\n";
124     exit(1);
125   }
126   object::BuildID ID(IDString.begin(), IDString.end());
127 
128   std::string Path;
129   if (FetchSource != "")
130     Path =
131         ExitOnDebuginfodFindError(getCachedOrDownloadSource(ID, FetchSource));
132   else if (FetchExecutable)
133     Path = ExitOnDebuginfodFindError(getCachedOrDownloadExecutable(ID));
134   else if (FetchDebuginfo)
135     Path = fetchDebugInfo(ID);
136   else
137     llvm_unreachable("We have already checked that exactly one of the above "
138                      "conditions is true.");
139 
140   if (DumpToStdout) {
141     // Print the contents of the artifact.
142     ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
143         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
144     ExitOnDebuginfodFindError(errorCodeToError(Buf.getError()));
145     outs() << Buf.get()->getBuffer();
146   } else
147     // Print the path to the cached artifact file.
148     outs() << Path << "\n";
149 
150   return 0;
151 }
152 
153 // Find a debug file in local build ID directories and via debuginfod.
154 std::string fetchDebugInfo(object::BuildIDRef BuildID) {
155   if (std::optional<std::string> Path =
156           DebuginfodFetcher(DebugFileDirectory).fetch(BuildID))
157     return *Path;
158   errs() << "Build ID " << llvm::toHex(BuildID, /*Lowercase=*/true)
159          << " could not be found.\n";
160   exit(1);
161 }
162