xref: /llvm-project/llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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