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