158 lines · cpp
1//===-- llvm-debuginfod.cpp - federating debuginfod server ----------------===//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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains the llvm-debuginfod tool, which serves the debuginfod11/// protocol over HTTP. The tool periodically scans zero or more filesystem12/// directories for ELF binaries to serve, and federates requests for unknown13/// build IDs to the debuginfod servers set in the DEBUGINFOD_URLS environment14/// variable.15///16//===----------------------------------------------------------------------===//17 18#include "llvm/ADT/StringExtras.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/Debuginfod/Debuginfod.h"21#include "llvm/Debuginfod/HTTPClient.h"22#include "llvm/Option/ArgList.h"23#include "llvm/Option/Option.h"24#include "llvm/Support/CommandLine.h"25#include "llvm/Support/LLVMDriver.h"26#include "llvm/Support/ThreadPool.h"27 28using namespace llvm;29 30// Command-line option boilerplate.31namespace {32enum ID {33 OPT_INVALID = 0, // This is not an option ID.34#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),35#include "Opts.inc"36#undef OPTION37};38 39#define OPTTABLE_STR_TABLE_CODE40#include "Opts.inc"41#undef OPTTABLE_STR_TABLE_CODE42 43#define OPTTABLE_PREFIXES_TABLE_CODE44#include "Opts.inc"45#undef OPTTABLE_PREFIXES_TABLE_CODE46 47using namespace llvm::opt;48static constexpr opt::OptTable::Info InfoTable[] = {49#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),50#include "Opts.inc"51#undef OPTION52};53 54class DebuginfodOptTable : public opt::GenericOptTable {55public:56 DebuginfodOptTable()57 : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}58};59} // end anonymous namespace60 61// Options62static unsigned Port;63static std::string HostInterface;64static int ScanInterval;65static double MinInterval;66static size_t MaxConcurrency;67static bool VerboseLogging;68static std::vector<std::string> ScanPaths;69 70ExitOnError ExitOnErr;71 72template <typename T>73static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value,74 T Default) {75 if (const opt::Arg *A = Args.getLastArg(ID)) {76 StringRef V(A->getValue());77 if (!llvm::to_integer(V, Value, 0)) {78 errs() << A->getSpelling() + ": expected an integer, but got '" + V + "'";79 exit(1);80 }81 } else {82 Value = Default;83 }84}85 86static void parseArgs(int argc, char **argv) {87 DebuginfodOptTable Tbl;88 llvm::StringRef ToolName = argv[0];89 llvm::BumpPtrAllocator A;90 llvm::StringSaver Saver{A};91 opt::InputArgList Args =92 Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {93 llvm::errs() << Msg << '\n';94 std::exit(1);95 });96 97 if (Args.hasArg(OPT_help)) {98 Tbl.printHelp(llvm::outs(),99 "llvm-debuginfod [options] <Directories to scan>",100 ToolName.str().c_str());101 std::exit(0);102 }103 104 VerboseLogging = Args.hasArg(OPT_verbose_logging);105 ScanPaths = Args.getAllArgValues(OPT_INPUT);106 107 parseIntArg(Args, OPT_port, Port, 0u);108 parseIntArg(Args, OPT_scan_interval, ScanInterval, 300);109 parseIntArg(Args, OPT_max_concurrency, MaxConcurrency, size_t(0));110 111 if (const opt::Arg *A = Args.getLastArg(OPT_min_interval)) {112 StringRef V(A->getValue());113 if (!llvm::to_float(V, MinInterval)) {114 errs() << A->getSpelling() + ": expected a number, but got '" + V + "'";115 exit(1);116 }117 } else {118 MinInterval = 10.0;119 }120 121 HostInterface = Args.getLastArgValue(OPT_host_interface, "0.0.0.0");122}123 124int llvm_debuginfod_main(int argc, char **argv, const llvm::ToolContext &) {125 HTTPClient::initialize();126 parseArgs(argc, argv);127 128 SmallVector<StringRef, 1> Paths;129 llvm::append_range(Paths, ScanPaths);130 131 DefaultThreadPool Pool(hardware_concurrency(MaxConcurrency));132 DebuginfodLog Log;133 DebuginfodCollection Collection(Paths, Log, Pool, MinInterval);134 DebuginfodServer Server(Log, Collection);135 136 if (!Port)137 Port = ExitOnErr(Server.Server.bind(HostInterface.c_str()));138 else139 ExitOnErr(Server.Server.bind(Port, HostInterface.c_str()));140 141 Log.push("Listening on port " + Twine(Port).str());142 143 Pool.async([&]() { ExitOnErr(Server.Server.listen()); });144 Pool.async([&]() {145 while (true) {146 DebuginfodLogEntry Entry = Log.pop();147 if (VerboseLogging) {148 outs() << Entry.Message << "\n";149 outs().flush();150 }151 }152 });153 if (Paths.size())154 ExitOnErr(Collection.updateForever(std::chrono::seconds(ScanInterval)));155 Pool.wait();156 llvm_unreachable("The ThreadPool should never finish running its tasks.");157}158