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 static constexpr opt::OptTable::Info InfoTable[] = { 47 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 48 #include "Opts.inc" 49 #undef OPTION 50 }; 51 52 class DebuginfodOptTable : public opt::GenericOptTable { 53 public: 54 DebuginfodOptTable() : GenericOptTable(InfoTable) {} 55 }; 56 } // end anonymous namespace 57 58 // Options 59 static unsigned Port; 60 static std::string HostInterface; 61 static int ScanInterval; 62 static double MinInterval; 63 static size_t MaxConcurrency; 64 static bool VerboseLogging; 65 static std::vector<std::string> ScanPaths; 66 67 ExitOnError ExitOnErr; 68 69 template <typename T> 70 static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value, 71 T Default) { 72 if (const opt::Arg *A = Args.getLastArg(ID)) { 73 StringRef V(A->getValue()); 74 if (!llvm::to_integer(V, Value, 0)) { 75 errs() << A->getSpelling() + ": expected an integer, but got '" + V + "'"; 76 exit(1); 77 } 78 } else { 79 Value = Default; 80 } 81 } 82 83 static void parseArgs(int argc, char **argv) { 84 DebuginfodOptTable Tbl; 85 llvm::StringRef ToolName = argv[0]; 86 llvm::BumpPtrAllocator A; 87 llvm::StringSaver Saver{A}; 88 opt::InputArgList Args = 89 Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { 90 llvm::errs() << Msg << '\n'; 91 std::exit(1); 92 }); 93 94 if (Args.hasArg(OPT_help)) { 95 Tbl.printHelp(llvm::outs(), 96 "llvm-debuginfod [options] <Directories to scan>", 97 ToolName.str().c_str()); 98 std::exit(0); 99 } 100 101 VerboseLogging = Args.hasArg(OPT_verbose_logging); 102 ScanPaths = Args.getAllArgValues(OPT_INPUT); 103 104 parseIntArg(Args, OPT_port, Port, 0u); 105 parseIntArg(Args, OPT_scan_interval, ScanInterval, 300); 106 parseIntArg(Args, OPT_max_concurrency, MaxConcurrency, size_t(0)); 107 108 if (const opt::Arg *A = Args.getLastArg(OPT_min_interval)) { 109 StringRef V(A->getValue()); 110 if (!llvm::to_float(V, MinInterval)) { 111 errs() << A->getSpelling() + ": expected a number, but got '" + V + "'"; 112 exit(1); 113 } 114 } else { 115 MinInterval = 10.0; 116 } 117 118 HostInterface = Args.getLastArgValue(OPT_host_interface, "0.0.0.0"); 119 } 120 121 int main(int argc, char **argv) { 122 InitLLVM X(argc, argv); 123 HTTPClient::initialize(); 124 parseArgs(argc, argv); 125 126 SmallVector<StringRef, 1> Paths; 127 for (const std::string &Path : ScanPaths) 128 Paths.push_back(Path); 129 130 ThreadPool Pool(hardware_concurrency(MaxConcurrency)); 131 DebuginfodLog Log; 132 DebuginfodCollection Collection(Paths, Log, Pool, MinInterval); 133 DebuginfodServer Server(Log, Collection); 134 135 if (!Port) 136 Port = ExitOnErr(Server.Server.bind(HostInterface.c_str())); 137 else 138 ExitOnErr(Server.Server.bind(Port, HostInterface.c_str())); 139 140 Log.push("Listening on port " + Twine(Port).str()); 141 142 Pool.async([&]() { ExitOnErr(Server.Server.listen()); }); 143 Pool.async([&]() { 144 while (true) { 145 DebuginfodLogEntry Entry = Log.pop(); 146 if (VerboseLogging) { 147 outs() << Entry.Message << "\n"; 148 outs().flush(); 149 } 150 } 151 }); 152 if (Paths.size()) 153 ExitOnErr(Collection.updateForever(std::chrono::seconds(ScanInterval))); 154 Pool.wait(); 155 llvm_unreachable("The ThreadPool should never finish running its tasks."); 156 } 157