Index: llvm/tools/llvm-debuginfod/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfod/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Debuginfod + Support + ) +add_llvm_tool(llvm-debuginfod + llvm-debuginfod.cpp + ) +if(LLVM_INSTALL_BINUTILS_SYMLINKS) + add_llvm_tool_symlink(debuginfod llvm-debuginfod) +endif() Index: llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfod/llvm-debuginfod.cpp @@ -0,0 +1,127 @@ +//===-- llvm-debuginfod-find.cpp - Simple CLI for libdebuginfod-client ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the llvm-debuginfod-find tool. This tool +/// queries the debuginfod servers in the DEBUGINFOD_URLS environment +/// variable (delimited by space (" ")) for the executable, +/// debuginfo, or specified source file of the binary matching the +/// given build-id. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/HTTPServer.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" +#include +#include +#include +#include + +using namespace llvm; + +static cl::list + ScanPaths(cl::Positional, cl::desc(""), cl::OneOrMore); + +static cl::opt Port( + "p", cl::init(0), + cl::desc("Port to listen on. Set to 0 to bind to any available port.")); + +ExitOnError ExitOnErr; + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv); + SmallVector Paths; + for (const std::string &Path : ScanPaths) + Paths.push_back(Path); + DebuginfodCollection Collection(Paths); + + HTTPServer Server; + // TODO: Add streamStringResponse or StreamingHTTPResponse(HTTPResponse) ctor + // to simplify this bit. Deduplicate buildID parsing by factoring out into + // common parent handler. + ExitOnErr(Server.get( + R"(/buildid/(.*)/debuginfo)", + [&](HTTPServerRequest Request) -> StreamingHTTPResponse { + std::string IDString; + if (!tryGetFromHex(Request.UrlPathMatches[1], IDString)) { + std::string Body = "Build ID is not a hex string\n"; + return {404, "text/plain", Body.size(), + [=](size_t Offset, size_t Length) -> std::string { + return Body.substr(Offset, Length); + }}; + } + BuildID ID(IDString.begin(), IDString.end()); + Expected PathOrErr = Collection.getDebugBinaryPath(ID); + if (Error Err = PathOrErr.takeError()) { + consumeError(std::move(Err)); + std::string Body = "Build ID not found\n"; + return {404, "text/plain", Body.size(), + [=](size_t Offset, size_t Length) -> std::string { + return Body.substr(Offset, Length); + }}; + } + return streamFileResponse(*PathOrErr); + })); + ExitOnErr(Server.get( + R"(/buildid/(.*)/executable)", + [&](HTTPServerRequest Request) -> StreamingHTTPResponse { + std::string IDString; + if (!tryGetFromHex(Request.UrlPathMatches[1], IDString)) { + std::string Body = "Build ID is not a hex string\n"; + return {404, "text/plain", Body.size(), + [=](size_t Offset, size_t Length) -> std::string { + return Body.substr(Offset, Length); + }}; + } + BuildID ID(IDString.begin(), IDString.end()); + Expected PathOrErr = Collection.getBinaryPath(ID); + if (Error Err = PathOrErr.takeError()) { + consumeError(std::move(Err)); + std::string Body = "Build ID not found\n"; + return {404, "text/plain", Body.size(), + [=](size_t Offset, size_t Length) -> std::string { + return Body.substr(Offset, Length); + }}; + } + return streamFileResponse(*PathOrErr); + })); + + if (!Port) + Port = ExitOnErr(Server.bindAny()); + else + ExitOnErr(Server.bind(Port)); + outs() << "Listening on port " << Port << "\n"; + ThreadPool Pool(hardware_concurrency(3)); + Pool.async([&]() { ExitOnErr(Server.listen()); }); + Pool.async([&]() { + while (1) { + outs() << "updating collection\n"; + ExitOnErr(Collection.update()); + outs() << "updated collection\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + } + return Error::success(); + }); + + Pool.wait(); +}