136f01909SNoah Shutty //===-- llvm-debuginfod.cpp - federating debuginfod server ----------------===// 236f01909SNoah Shutty // 336f01909SNoah Shutty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 436f01909SNoah Shutty // See https://llvm.org/LICENSE.txt for license information. 536f01909SNoah Shutty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 636f01909SNoah Shutty // 736f01909SNoah Shutty //===----------------------------------------------------------------------===// 836f01909SNoah Shutty /// 936f01909SNoah Shutty /// \file 1036f01909SNoah Shutty /// This file contains the llvm-debuginfod tool, which serves the debuginfod 1136f01909SNoah Shutty /// protocol over HTTP. The tool periodically scans zero or more filesystem 1236f01909SNoah Shutty /// directories for ELF binaries to serve, and federates requests for unknown 1336f01909SNoah Shutty /// build IDs to the debuginfod servers set in the DEBUGINFOD_URLS environment 1436f01909SNoah Shutty /// variable. 1536f01909SNoah Shutty /// 1636f01909SNoah Shutty //===----------------------------------------------------------------------===// 1736f01909SNoah Shutty 18816141ceSFangrui Song #include "llvm/ADT/StringExtras.h" 198148fc57SAndrés Villegas #include "llvm/ADT/StringRef.h" 2036f01909SNoah Shutty #include "llvm/Debuginfod/Debuginfod.h" 2136f01909SNoah Shutty #include "llvm/Debuginfod/HTTPClient.h" 228148fc57SAndrés Villegas #include "llvm/Option/ArgList.h" 238148fc57SAndrés Villegas #include "llvm/Option/Option.h" 2436f01909SNoah Shutty #include "llvm/Support/CommandLine.h" 25d5ca9004SAndrés Villegas #include "llvm/Support/LLVMDriver.h" 2636f01909SNoah Shutty #include "llvm/Support/ThreadPool.h" 2736f01909SNoah Shutty 2836f01909SNoah Shutty using namespace llvm; 2936f01909SNoah Shutty 308148fc57SAndrés Villegas // Command-line option boilerplate. 318148fc57SAndrés Villegas namespace { 328148fc57SAndrés Villegas enum ID { 338148fc57SAndrés Villegas OPT_INVALID = 0, // This is not an option ID. 343f092f37SJan Svoboda #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 358148fc57SAndrés Villegas #include "Opts.inc" 368148fc57SAndrés Villegas #undef OPTION 378148fc57SAndrés Villegas }; 3836f01909SNoah Shutty 39*dd647e3eSChandler Carruth #define OPTTABLE_STR_TABLE_CODE 408148fc57SAndrés Villegas #include "Opts.inc" 41*dd647e3eSChandler Carruth #undef OPTTABLE_STR_TABLE_CODE 42*dd647e3eSChandler Carruth 43*dd647e3eSChandler Carruth #define OPTTABLE_PREFIXES_TABLE_CODE 44*dd647e3eSChandler Carruth #include "Opts.inc" 45*dd647e3eSChandler Carruth #undef OPTTABLE_PREFIXES_TABLE_CODE 4636f01909SNoah Shutty 47dcb6d212SJustin Bogner using namespace llvm::opt; 488148fc57SAndrés Villegas static constexpr opt::OptTable::Info InfoTable[] = { 493f092f37SJan Svoboda #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 508148fc57SAndrés Villegas #include "Opts.inc" 518148fc57SAndrés Villegas #undef OPTION 528148fc57SAndrés Villegas }; 5336f01909SNoah Shutty 548148fc57SAndrés Villegas class DebuginfodOptTable : public opt::GenericOptTable { 558148fc57SAndrés Villegas public: 56*dd647e3eSChandler Carruth DebuginfodOptTable() 57*dd647e3eSChandler Carruth : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} 588148fc57SAndrés Villegas }; 598148fc57SAndrés Villegas } // end anonymous namespace 6036f01909SNoah Shutty 618148fc57SAndrés Villegas // Options 628148fc57SAndrés Villegas static unsigned Port; 638148fc57SAndrés Villegas static std::string HostInterface; 648148fc57SAndrés Villegas static int ScanInterval; 658148fc57SAndrés Villegas static double MinInterval; 668148fc57SAndrés Villegas static size_t MaxConcurrency; 678148fc57SAndrés Villegas static bool VerboseLogging; 688148fc57SAndrés Villegas static std::vector<std::string> ScanPaths; 6936f01909SNoah Shutty 7036f01909SNoah Shutty ExitOnError ExitOnErr; 7136f01909SNoah Shutty 728148fc57SAndrés Villegas template <typename T> 738148fc57SAndrés Villegas static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value, 748148fc57SAndrés Villegas T Default) { 758148fc57SAndrés Villegas if (const opt::Arg *A = Args.getLastArg(ID)) { 768148fc57SAndrés Villegas StringRef V(A->getValue()); 778148fc57SAndrés Villegas if (!llvm::to_integer(V, Value, 0)) { 788148fc57SAndrés Villegas errs() << A->getSpelling() + ": expected an integer, but got '" + V + "'"; 798148fc57SAndrés Villegas exit(1); 808148fc57SAndrés Villegas } 818148fc57SAndrés Villegas } else { 828148fc57SAndrés Villegas Value = Default; 838148fc57SAndrés Villegas } 848148fc57SAndrés Villegas } 858148fc57SAndrés Villegas 868148fc57SAndrés Villegas static void parseArgs(int argc, char **argv) { 878148fc57SAndrés Villegas DebuginfodOptTable Tbl; 888148fc57SAndrés Villegas llvm::StringRef ToolName = argv[0]; 898148fc57SAndrés Villegas llvm::BumpPtrAllocator A; 908148fc57SAndrés Villegas llvm::StringSaver Saver{A}; 918148fc57SAndrés Villegas opt::InputArgList Args = 928148fc57SAndrés Villegas Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { 938148fc57SAndrés Villegas llvm::errs() << Msg << '\n'; 948148fc57SAndrés Villegas std::exit(1); 958148fc57SAndrés Villegas }); 968148fc57SAndrés Villegas 978148fc57SAndrés Villegas if (Args.hasArg(OPT_help)) { 988148fc57SAndrés Villegas Tbl.printHelp(llvm::outs(), 998148fc57SAndrés Villegas "llvm-debuginfod [options] <Directories to scan>", 1008148fc57SAndrés Villegas ToolName.str().c_str()); 1018148fc57SAndrés Villegas std::exit(0); 1028148fc57SAndrés Villegas } 1038148fc57SAndrés Villegas 1048148fc57SAndrés Villegas VerboseLogging = Args.hasArg(OPT_verbose_logging); 1058148fc57SAndrés Villegas ScanPaths = Args.getAllArgValues(OPT_INPUT); 1068148fc57SAndrés Villegas 1078148fc57SAndrés Villegas parseIntArg(Args, OPT_port, Port, 0u); 1088148fc57SAndrés Villegas parseIntArg(Args, OPT_scan_interval, ScanInterval, 300); 1098148fc57SAndrés Villegas parseIntArg(Args, OPT_max_concurrency, MaxConcurrency, size_t(0)); 1108148fc57SAndrés Villegas 1118148fc57SAndrés Villegas if (const opt::Arg *A = Args.getLastArg(OPT_min_interval)) { 1128148fc57SAndrés Villegas StringRef V(A->getValue()); 1138148fc57SAndrés Villegas if (!llvm::to_float(V, MinInterval)) { 1148148fc57SAndrés Villegas errs() << A->getSpelling() + ": expected a number, but got '" + V + "'"; 1158148fc57SAndrés Villegas exit(1); 1168148fc57SAndrés Villegas } 1178148fc57SAndrés Villegas } else { 1188148fc57SAndrés Villegas MinInterval = 10.0; 1198148fc57SAndrés Villegas } 1208148fc57SAndrés Villegas 1218148fc57SAndrés Villegas HostInterface = Args.getLastArgValue(OPT_host_interface, "0.0.0.0"); 1228148fc57SAndrés Villegas } 1238148fc57SAndrés Villegas 124d5ca9004SAndrés Villegas int llvm_debuginfod_main(int argc, char **argv, const llvm::ToolContext &) { 12536f01909SNoah Shutty HTTPClient::initialize(); 1268148fc57SAndrés Villegas parseArgs(argc, argv); 12736f01909SNoah Shutty 12836f01909SNoah Shutty SmallVector<StringRef, 1> Paths; 12936f01909SNoah Shutty for (const std::string &Path : ScanPaths) 13036f01909SNoah Shutty Paths.push_back(Path); 13136f01909SNoah Shutty 132716042a6SMehdi Amini DefaultThreadPool Pool(hardware_concurrency(MaxConcurrency)); 13336f01909SNoah Shutty DebuginfodLog Log; 13436f01909SNoah Shutty DebuginfodCollection Collection(Paths, Log, Pool, MinInterval); 13536f01909SNoah Shutty DebuginfodServer Server(Log, Collection); 13636f01909SNoah Shutty 13736f01909SNoah Shutty if (!Port) 13836f01909SNoah Shutty Port = ExitOnErr(Server.Server.bind(HostInterface.c_str())); 13936f01909SNoah Shutty else 14036f01909SNoah Shutty ExitOnErr(Server.Server.bind(Port, HostInterface.c_str())); 14136f01909SNoah Shutty 14236f01909SNoah Shutty Log.push("Listening on port " + Twine(Port).str()); 14336f01909SNoah Shutty 14436f01909SNoah Shutty Pool.async([&]() { ExitOnErr(Server.Server.listen()); }); 14536f01909SNoah Shutty Pool.async([&]() { 1467094ab4eSKazu Hirata while (true) { 14736f01909SNoah Shutty DebuginfodLogEntry Entry = Log.pop(); 14836f01909SNoah Shutty if (VerboseLogging) { 14936f01909SNoah Shutty outs() << Entry.Message << "\n"; 15036f01909SNoah Shutty outs().flush(); 15136f01909SNoah Shutty } 15236f01909SNoah Shutty } 15336f01909SNoah Shutty }); 15436f01909SNoah Shutty if (Paths.size()) 15536f01909SNoah Shutty ExitOnErr(Collection.updateForever(std::chrono::seconds(ScanInterval))); 15636f01909SNoah Shutty Pool.wait(); 15736f01909SNoah Shutty llvm_unreachable("The ThreadPool should never finish running its tasks."); 15836f01909SNoah Shutty } 159