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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file contains the llvm-debuginfod tool, which serves the debuginfod 11 /// protocol over HTTP. The tool periodically scans zero or more filesystem 12 /// directories for ELF binaries to serve, and federates requests for unknown 13 /// build IDs to the debuginfod servers set in the DEBUGINFOD_URLS environment 14 /// 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/InitLLVM.h" 26 #include "llvm/Support/ThreadPool.h" 27 28 using namespace llvm; 29 30 // Command-line option boilerplate. 31 namespace { 32 enum 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 OPTION 37 }; 38 39 #define PREFIX(NAME, VALUE) \ 40 static constexpr StringLiteral NAME##_init[] = VALUE; \ 41 static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ 42 std::size(NAME##_init) - 1); 43 #include "Opts.inc" 44 #undef PREFIX 45 46 using namespace llvm::opt; 47 static constexpr opt::OptTable::Info InfoTable[] = { 48 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 49 #include "Opts.inc" 50 #undef OPTION 51 }; 52 53 class DebuginfodOptTable : public opt::GenericOptTable { 54 public: 55 DebuginfodOptTable() : GenericOptTable(InfoTable) {} 56 }; 57 } // end anonymous namespace 58 59 // Options 60 static unsigned Port; 61 static std::string HostInterface; 62 static int ScanInterval; 63 static double MinInterval; 64 static size_t MaxConcurrency; 65 static bool VerboseLogging; 66 static std::vector<std::string> ScanPaths; 67 68 ExitOnError ExitOnErr; 69 70 template <typename T> 71 static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value, 72 T Default) { 73 if (const opt::Arg *A = Args.getLastArg(ID)) { 74 StringRef V(A->getValue()); 75 if (!llvm::to_integer(V, Value, 0)) { 76 errs() << A->getSpelling() + ": expected an integer, but got '" + V + "'"; 77 exit(1); 78 } 79 } else { 80 Value = Default; 81 } 82 } 83 84 static void parseArgs(int argc, char **argv) { 85 DebuginfodOptTable Tbl; 86 llvm::StringRef ToolName = argv[0]; 87 llvm::BumpPtrAllocator A; 88 llvm::StringSaver Saver{A}; 89 opt::InputArgList Args = 90 Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { 91 llvm::errs() << Msg << '\n'; 92 std::exit(1); 93 }); 94 95 if (Args.hasArg(OPT_help)) { 96 Tbl.printHelp(llvm::outs(), 97 "llvm-debuginfod [options] <Directories to scan>", 98 ToolName.str().c_str()); 99 std::exit(0); 100 } 101 102 VerboseLogging = Args.hasArg(OPT_verbose_logging); 103 ScanPaths = Args.getAllArgValues(OPT_INPUT); 104 105 parseIntArg(Args, OPT_port, Port, 0u); 106 parseIntArg(Args, OPT_scan_interval, ScanInterval, 300); 107 parseIntArg(Args, OPT_max_concurrency, MaxConcurrency, size_t(0)); 108 109 if (const opt::Arg *A = Args.getLastArg(OPT_min_interval)) { 110 StringRef V(A->getValue()); 111 if (!llvm::to_float(V, MinInterval)) { 112 errs() << A->getSpelling() + ": expected a number, but got '" + V + "'"; 113 exit(1); 114 } 115 } else { 116 MinInterval = 10.0; 117 } 118 119 HostInterface = Args.getLastArgValue(OPT_host_interface, "0.0.0.0"); 120 } 121 122 int main(int argc, char **argv) { 123 InitLLVM X(argc, argv); 124 HTTPClient::initialize(); 125 parseArgs(argc, argv); 126 127 SmallVector<StringRef, 1> Paths; 128 for (const std::string &Path : ScanPaths) 129 Paths.push_back(Path); 130 131 ThreadPool 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 else 139 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