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