1 //===-- llvm-debuginfod.cpp - federating debuginfod server ----------------===//
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
7 //===----------------------------------------------------------------------===//
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
16 //===----------------------------------------------------------------------===//
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/LLVMDriver.h"
27 #include "llvm/Support/ThreadPool.h"
31 // Command-line option boilerplate.
34 OPT_INVALID
= 0, // This is not an option ID.
35 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
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);
47 using namespace llvm::opt
;
48 static constexpr opt::OptTable::Info InfoTable
[] = {
49 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
54 class DebuginfodOptTable
: public opt::GenericOptTable
{
56 DebuginfodOptTable() : GenericOptTable(InfoTable
) {}
58 } // end anonymous namespace
62 static std::string HostInterface
;
63 static int ScanInterval
;
64 static double MinInterval
;
65 static size_t MaxConcurrency
;
66 static bool VerboseLogging
;
67 static std::vector
<std::string
> ScanPaths
;
69 ExitOnError ExitOnErr
;
72 static void parseIntArg(const opt::InputArgList
&Args
, int ID
, T
&Value
,
74 if (const opt::Arg
*A
= Args
.getLastArg(ID
)) {
75 StringRef
V(A
->getValue());
76 if (!llvm::to_integer(V
, Value
, 0)) {
77 errs() << A
->getSpelling() + ": expected an integer, but got '" + V
+ "'";
85 static void parseArgs(int argc
, char **argv
) {
86 DebuginfodOptTable Tbl
;
87 llvm::StringRef ToolName
= argv
[0];
88 llvm::BumpPtrAllocator A
;
89 llvm::StringSaver Saver
{A
};
90 opt::InputArgList Args
=
91 Tbl
.parseArgs(argc
, argv
, OPT_UNKNOWN
, Saver
, [&](StringRef Msg
) {
92 llvm::errs() << Msg
<< '\n';
96 if (Args
.hasArg(OPT_help
)) {
97 Tbl
.printHelp(llvm::outs(),
98 "llvm-debuginfod [options] <Directories to scan>",
99 ToolName
.str().c_str());
103 VerboseLogging
= Args
.hasArg(OPT_verbose_logging
);
104 ScanPaths
= Args
.getAllArgValues(OPT_INPUT
);
106 parseIntArg(Args
, OPT_port
, Port
, 0u);
107 parseIntArg(Args
, OPT_scan_interval
, ScanInterval
, 300);
108 parseIntArg(Args
, OPT_max_concurrency
, MaxConcurrency
, size_t(0));
110 if (const opt::Arg
*A
= Args
.getLastArg(OPT_min_interval
)) {
111 StringRef
V(A
->getValue());
112 if (!llvm::to_float(V
, MinInterval
)) {
113 errs() << A
->getSpelling() + ": expected a number, but got '" + V
+ "'";
120 HostInterface
= Args
.getLastArgValue(OPT_host_interface
, "0.0.0.0");
123 int llvm_debuginfod_main(int argc
, char **argv
, const llvm::ToolContext
&) {
124 InitLLVM
X(argc
, argv
);
125 HTTPClient::initialize();
126 parseArgs(argc
, argv
);
128 SmallVector
<StringRef
, 1> Paths
;
129 for (const std::string
&Path
: ScanPaths
)
130 Paths
.push_back(Path
);
132 ThreadPool
Pool(hardware_concurrency(MaxConcurrency
));
134 DebuginfodCollection
Collection(Paths
, Log
, Pool
, MinInterval
);
135 DebuginfodServer
Server(Log
, Collection
);
138 Port
= ExitOnErr(Server
.Server
.bind(HostInterface
.c_str()));
140 ExitOnErr(Server
.Server
.bind(Port
, HostInterface
.c_str()));
142 Log
.push("Listening on port " + Twine(Port
).str());
144 Pool
.async([&]() { ExitOnErr(Server
.Server
.listen()); });
147 DebuginfodLogEntry Entry
= Log
.pop();
148 if (VerboseLogging
) {
149 outs() << Entry
.Message
<< "\n";
155 ExitOnErr(Collection
.updateForever(std::chrono::seconds(ScanInterval
)));
157 llvm_unreachable("The ThreadPool should never finish running its tasks.");