//===-- llvm-debuginfod-find.cpp - Simple CLI for libdebuginfod-client ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file contains the llvm-debuginfod-find tool. This tool /// queries the debuginfod servers in the DEBUGINFOD_URLS environment /// variable (delimited by space (" ")) for the executable, /// debuginfo, or specified source file of the binary matching the /// given build-id. /// //===----------------------------------------------------------------------===// #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Debuginfod/BuildIDFetcher.h" #include "llvm/Debuginfod/Debuginfod.h" #include "llvm/Debuginfod/HTTPClient.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LLVMDriver.h" using namespace llvm; // Command-line option boilerplate. namespace { enum ID { OPT_INVALID = 0, // This is not an option ID. #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), #include "Opts.inc" #undef OPTION }; #define OPTTABLE_STR_TABLE_CODE #include "Opts.inc" #undef OPTTABLE_STR_TABLE_CODE #define OPTTABLE_PREFIXES_TABLE_CODE #include "Opts.inc" #undef OPTTABLE_PREFIXES_TABLE_CODE using namespace llvm::opt; static constexpr opt::OptTable::Info InfoTable[] = { #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), #include "Opts.inc" #undef OPTION }; class DebuginfodFindOptTable : public opt::GenericOptTable { public: DebuginfodFindOptTable() : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} }; } // end anonymous namespace static std::string InputBuildID; static bool FetchExecutable; static bool FetchDebuginfo; static std::string FetchSource; static bool DumpToStdout; static std::vector DebugFileDirectory; static void parseArgs(int argc, char **argv) { DebuginfodFindOptTable Tbl; llvm::BumpPtrAllocator A; llvm::StringSaver Saver{A}; opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { llvm::errs() << Msg << '\n'; std::exit(1); }); if (Args.hasArg(OPT_help)) { Tbl.printHelp( llvm::outs(), "llvm-debuginfod-find [options] ", "llvm-debuginfod-find: Fetch debuginfod artifacts\n\n" "This program is a frontend to the debuginfod client library. The " "cache directory, request timeout (in seconds), and debuginfod server " "urls are set by these environment variables:\n" "DEBUGINFOD_CACHE_PATH (default set by sys::path::cache_directory)\n" "DEBUGINFOD_TIMEOUT (defaults to 90s)\n" "DEBUGINFOD_URLS=[comma separated URLs] (defaults to empty)"); std::exit(0); } InputBuildID = Args.getLastArgValue(OPT_INPUT); FetchExecutable = Args.hasArg(OPT_fetch_executable); FetchDebuginfo = Args.hasArg(OPT_fetch_debuginfo); DumpToStdout = Args.hasArg(OPT_dump_to_stdout); FetchSource = Args.getLastArgValue(OPT_fetch_source, ""); DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory); } [[noreturn]] static void helpExit() { errs() << "Must specify exactly one of --executable, " "--source=/path/to/file, or --debuginfo.\n"; exit(1); } ExitOnError ExitOnDebuginfodFindError; static std::string fetchDebugInfo(object::BuildIDRef BuildID); int llvm_debuginfod_find_main(int argc, char **argv, const llvm::ToolContext &) { // InitLLVM X(argc, argv); HTTPClient::initialize(); parseArgs(argc, argv); if (FetchExecutable + FetchDebuginfo + (FetchSource != "") != 1) helpExit(); std::string IDString; if (!tryGetFromHex(InputBuildID, IDString)) { errs() << "Build ID " << InputBuildID << " is not a hex string.\n"; exit(1); } object::BuildID ID(IDString.begin(), IDString.end()); std::string Path; if (FetchSource != "") Path = ExitOnDebuginfodFindError(getCachedOrDownloadSource(ID, FetchSource)); else if (FetchExecutable) Path = ExitOnDebuginfodFindError(getCachedOrDownloadExecutable(ID)); else if (FetchDebuginfo) Path = fetchDebugInfo(ID); else llvm_unreachable("We have already checked that exactly one of the above " "conditions is true."); if (DumpToStdout) { // Print the contents of the artifact. ErrorOr> Buf = MemoryBuffer::getFile( Path, /*IsText=*/false, /*RequiresNullTerminator=*/false); ExitOnDebuginfodFindError(errorCodeToError(Buf.getError())); outs() << Buf.get()->getBuffer(); } else // Print the path to the cached artifact file. outs() << Path << "\n"; return 0; } // Find a debug file in local build ID directories and via debuginfod. std::string fetchDebugInfo(object::BuildIDRef BuildID) { if (std::optional Path = DebuginfodFetcher(DebugFileDirectory).fetch(BuildID)) return *Path; errs() << "Build ID " << llvm::toHex(BuildID, /*Lowercase=*/true) << " could not be found.\n"; exit(1); }