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/Debuginfod/BuildIDFetcher.h" |
20 | #include "llvm/Debuginfod/Debuginfod.h" |
21 | #include "llvm/Debuginfod/HTTPClient.h" |
22 | #include "llvm/Support/CommandLine.h" |
23 | #include "llvm/Support/InitLLVM.h" |
24 | |
25 | using namespace llvm; |
26 | |
27 | cl::OptionCategory DebuginfodFindCategory("llvm-debuginfod-find Options" ); |
28 | |
29 | cl::opt<std::string> InputBuildID(cl::Positional, cl::Required, |
30 | cl::desc("<input build_id>" ), cl::init(Val: "-" ), |
31 | cl::cat(DebuginfodFindCategory)); |
32 | |
33 | static cl::opt<bool> |
34 | FetchExecutable("executable" , cl::init(Val: false), |
35 | cl::desc("If set, fetch a binary file associated with this " |
36 | "build id, containing the executable sections." ), |
37 | cl::cat(DebuginfodFindCategory)); |
38 | |
39 | static cl::opt<bool> |
40 | FetchDebuginfo("debuginfo" , cl::init(Val: false), |
41 | cl::desc("If set, fetch a binary file associated with this " |
42 | "build id, containing the debuginfo sections." ), |
43 | cl::cat(DebuginfodFindCategory)); |
44 | |
45 | static cl::opt<std::string> FetchSource( |
46 | "source" , cl::init(Val: "" ), |
47 | cl::desc("Fetch a source file associated with this build id, which is at " |
48 | "this relative path relative to the compilation directory." ), |
49 | cl::cat(DebuginfodFindCategory)); |
50 | |
51 | static cl::opt<bool> |
52 | DumpToStdout("dump" , cl::init(Val: false), |
53 | cl::desc("If set, dumps the contents of the fetched artifact " |
54 | "to standard output. Otherwise, dumps the absolute " |
55 | "path to the cached artifact on disk." ), |
56 | cl::cat(DebuginfodFindCategory)); |
57 | |
58 | static cl::list<std::string> DebugFileDirectory( |
59 | "debug-file-directory" , |
60 | cl::desc("Path to directory where to look for debug files." ), |
61 | cl::cat(DebuginfodFindCategory)); |
62 | |
63 | [[noreturn]] static void helpExit() { |
64 | errs() << "Must specify exactly one of --executable, " |
65 | "--source=/path/to/file, or --debuginfo." ; |
66 | exit(status: 1); |
67 | } |
68 | |
69 | ExitOnError ExitOnErr; |
70 | |
71 | static std::string fetchDebugInfo(object::BuildIDRef BuildID); |
72 | |
73 | int main(int argc, char **argv) { |
74 | InitLLVM X(argc, argv); |
75 | HTTPClient::initialize(); |
76 | |
77 | cl::HideUnrelatedOptions(Categories: {&DebuginfodFindCategory}); |
78 | cl::ParseCommandLineOptions( |
79 | argc, argv, |
80 | Overview: "llvm-debuginfod-find: Fetch debuginfod artifacts\n\n" |
81 | "This program is a frontend to the debuginfod client library. The cache " |
82 | "directory, request timeout (in seconds), and debuginfod server urls are " |
83 | "set by these environment variables:\n" |
84 | "DEBUGINFOD_CACHE_PATH (default set by sys::path::cache_directory)\n" |
85 | "DEBUGINFOD_TIMEOUT (defaults to 90s)\n" |
86 | "DEBUGINFOD_URLS=[comma separated URLs] (defaults to empty)\n" ); |
87 | |
88 | if (FetchExecutable + FetchDebuginfo + (FetchSource != "" ) != 1) |
89 | helpExit(); |
90 | |
91 | std::string IDString; |
92 | if (!tryGetFromHex(Input: InputBuildID, Output&: IDString)) { |
93 | errs() << "Build ID " << InputBuildID << " is not a hex string.\n" ; |
94 | exit(status: 1); |
95 | } |
96 | object::BuildID ID(IDString.begin(), IDString.end()); |
97 | |
98 | std::string Path; |
99 | if (FetchSource != "" ) |
100 | Path = ExitOnErr(getCachedOrDownloadSource(ID, SourceFilePath: FetchSource)); |
101 | else if (FetchExecutable) |
102 | Path = ExitOnErr(getCachedOrDownloadExecutable(ID)); |
103 | else if (FetchDebuginfo) |
104 | Path = fetchDebugInfo(BuildID: ID); |
105 | else |
106 | llvm_unreachable("We have already checked that exactly one of the above " |
107 | "conditions is true." ); |
108 | |
109 | if (DumpToStdout) { |
110 | // Print the contents of the artifact. |
111 | ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile( |
112 | Filename: Path, /*IsText=*/false, /*RequiresNullTerminator=*/false); |
113 | ExitOnErr(errorCodeToError(EC: Buf.getError())); |
114 | outs() << Buf.get()->getBuffer(); |
115 | } else |
116 | // Print the path to the cached artifact file. |
117 | outs() << Path << "\n" ; |
118 | } |
119 | |
120 | // Find a debug file in local build ID directories and via debuginfod. |
121 | std::string fetchDebugInfo(object::BuildIDRef BuildID) { |
122 | if (std::optional<std::string> Path = |
123 | DebuginfodFetcher(DebugFileDirectory).fetch(BuildID)) |
124 | return *Path; |
125 | errs() << "Build ID " << llvm::toHex(Input: BuildID, /*Lowercase=*/LowerCase: true) |
126 | << " could not be found.\n" ; |
127 | exit(status: 1); |
128 | } |
129 | |