xref: /llvm-project/llvm/lib/Debuginfod/Debuginfod.cpp (revision 6594f428de91e333c1cbea4f55e79b18d31024c4)
10e0f1b28SNoah Shutty //===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===//
20e0f1b28SNoah Shutty //
30e0f1b28SNoah Shutty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40e0f1b28SNoah Shutty // See https://llvm.org/LICENSE.txt for license information.
50e0f1b28SNoah Shutty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60e0f1b28SNoah Shutty //
70e0f1b28SNoah Shutty //===----------------------------------------------------------------------===//
80e0f1b28SNoah Shutty ///
90e0f1b28SNoah Shutty /// \file
100e0f1b28SNoah Shutty ///
11babef908SNoah Shutty /// This file contains several definitions for the debuginfod client and server.
12babef908SNoah Shutty /// For the client, this file defines the fetchInfo function. For the server,
13babef908SNoah Shutty /// this file defines the DebuginfodLogEntry and DebuginfodServer structs, as
14babef908SNoah Shutty /// well as the DebuginfodLog, DebuginfodCollection classes. The fetchInfo
15babef908SNoah Shutty /// function retrieves any of the three supported artifact types: (executable,
16babef908SNoah Shutty /// debuginfo, source file) associated with a build-id from debuginfod servers.
17babef908SNoah Shutty /// If a source file is to be fetched, its absolute path must be specified in
18babef908SNoah Shutty /// the Description argument to fetchInfo. The DebuginfodLogEntry,
19babef908SNoah Shutty /// DebuginfodLog, and DebuginfodCollection are used by the DebuginfodServer to
20babef908SNoah Shutty /// scan the local filesystem for binaries and serve the debuginfod protocol.
210e0f1b28SNoah Shutty ///
220e0f1b28SNoah Shutty //===----------------------------------------------------------------------===//
230e0f1b28SNoah Shutty 
240e0f1b28SNoah Shutty #include "llvm/Debuginfod/Debuginfod.h"
2532a02a9cSDaniel Thornburgh #include "llvm/ADT/StringExtras.h"
260e0f1b28SNoah Shutty #include "llvm/ADT/StringRef.h"
27babef908SNoah Shutty #include "llvm/BinaryFormat/Magic.h"
28babef908SNoah Shutty #include "llvm/DebugInfo/DWARF/DWARFContext.h"
29babef908SNoah Shutty #include "llvm/DebugInfo/Symbolize/Symbolize.h"
30d9941f74SNoah Shutty #include "llvm/Debuginfod/HTTPClient.h"
31e61d89efSDaniel Thornburgh #include "llvm/Object/BuildID.h"
32babef908SNoah Shutty #include "llvm/Object/ELFObjectFile.h"
330e0f1b28SNoah Shutty #include "llvm/Support/CachePruning.h"
340e0f1b28SNoah Shutty #include "llvm/Support/Caching.h"
3575e164f6Sserge-sans-paille #include "llvm/Support/Errc.h"
360e0f1b28SNoah Shutty #include "llvm/Support/Error.h"
370e0f1b28SNoah Shutty #include "llvm/Support/FileUtilities.h"
3832a02a9cSDaniel Thornburgh #include "llvm/Support/MemoryBuffer.h"
3975e164f6Sserge-sans-paille #include "llvm/Support/Path.h"
40babef908SNoah Shutty #include "llvm/Support/ThreadPool.h"
410e0f1b28SNoah Shutty #include "llvm/Support/xxhash.h"
420e0f1b28SNoah Shutty 
43babef908SNoah Shutty #include <atomic>
44c43c86c2SKevin Frei #include <optional>
45b812db14SDavid Spickett #include <thread>
46babef908SNoah Shutty 
470e0f1b28SNoah Shutty namespace llvm {
48e61d89efSDaniel Thornburgh 
49e61d89efSDaniel Thornburgh using llvm::object::BuildIDRef;
50e61d89efSDaniel Thornburgh 
51c43c86c2SKevin Frei namespace {
52c43c86c2SKevin Frei std::optional<SmallVector<StringRef>> DebuginfodUrls;
53c43c86c2SKevin Frei // Many Readers/Single Writer lock protecting the global debuginfod URL list.
5489e775acSKevin Frei llvm::sys::RWMutex UrlsMutex;
55c43c86c2SKevin Frei } // namespace
56c43c86c2SKevin Frei 
getDebuginfodCacheKey(llvm::StringRef S)576d5f8d3eSKevin Frei std::string getDebuginfodCacheKey(llvm::StringRef S) {
58099f4e23SFangrui Song   return utostr(xxh3_64bits(S));
59099f4e23SFangrui Song }
600e0f1b28SNoah Shutty 
610e0f1b28SNoah Shutty // Returns a binary BuildID as a normalized hex string.
620e0f1b28SNoah Shutty // Uses lowercase for compatibility with common debuginfod servers.
buildIDToString(BuildIDRef ID)630e0f1b28SNoah Shutty static std::string buildIDToString(BuildIDRef ID) {
640e0f1b28SNoah Shutty   return llvm::toHex(ID, /*LowerCase=*/true);
650e0f1b28SNoah Shutty }
660e0f1b28SNoah Shutty 
canUseDebuginfod()67a3b0dde4SDaniel Thornburgh bool canUseDebuginfod() {
68a3b0dde4SDaniel Thornburgh   return HTTPClient::isAvailable() && !getDefaultDebuginfodUrls().empty();
69a3b0dde4SDaniel Thornburgh }
70a3b0dde4SDaniel Thornburgh 
getDefaultDebuginfodUrls()71a3b0dde4SDaniel Thornburgh SmallVector<StringRef> getDefaultDebuginfodUrls() {
7289e775acSKevin Frei   std::shared_lock<llvm::sys::RWMutex> ReadGuard(UrlsMutex);
73c43c86c2SKevin Frei   if (!DebuginfodUrls) {
74c43c86c2SKevin Frei     // Only read from the environment variable if the user hasn't already
756d5f8d3eSKevin Frei     // set the value.
76c43c86c2SKevin Frei     ReadGuard.unlock();
7789e775acSKevin Frei     std::unique_lock<llvm::sys::RWMutex> WriteGuard(UrlsMutex);
78c43c86c2SKevin Frei     DebuginfodUrls = SmallVector<StringRef>();
79c43c86c2SKevin Frei     if (const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS")) {
80c43c86c2SKevin Frei       StringRef(DebuginfodUrlsEnv)
81c43c86c2SKevin Frei           .split(DebuginfodUrls.value(), " ", -1, false);
82c43c86c2SKevin Frei     }
83c43c86c2SKevin Frei     WriteGuard.unlock();
84c43c86c2SKevin Frei     ReadGuard.lock();
85c43c86c2SKevin Frei   }
86c43c86c2SKevin Frei   return DebuginfodUrls.value();
87c43c86c2SKevin Frei }
880e0f1b28SNoah Shutty 
896d5f8d3eSKevin Frei // Set the default debuginfod URL list, override the environment variable.
setDefaultDebuginfodUrls(const SmallVector<StringRef> & URLs)90c43c86c2SKevin Frei void setDefaultDebuginfodUrls(const SmallVector<StringRef> &URLs) {
9189e775acSKevin Frei   std::unique_lock<llvm::sys::RWMutex> WriteGuard(UrlsMutex);
92c43c86c2SKevin Frei   DebuginfodUrls = URLs;
930e0f1b28SNoah Shutty }
940e0f1b28SNoah Shutty 
95babef908SNoah Shutty /// Finds a default local file caching directory for the debuginfod client,
96babef908SNoah Shutty /// first checking DEBUGINFOD_CACHE_PATH.
getDefaultDebuginfodCacheDirectory()970e0f1b28SNoah Shutty Expected<std::string> getDefaultDebuginfodCacheDirectory() {
980e0f1b28SNoah Shutty   if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH"))
990e0f1b28SNoah Shutty     return CacheDirectoryEnv;
1000e0f1b28SNoah Shutty 
1010e0f1b28SNoah Shutty   SmallString<64> CacheDirectory;
1020e0f1b28SNoah Shutty   if (!sys::path::cache_directory(CacheDirectory))
1030e0f1b28SNoah Shutty     return createStringError(
1040e0f1b28SNoah Shutty         errc::io_error, "Unable to determine appropriate cache directory.");
105fd0782a3SPetr Hosek   sys::path::append(CacheDirectory, "llvm-debuginfod", "client");
1060e0f1b28SNoah Shutty   return std::string(CacheDirectory);
1070e0f1b28SNoah Shutty }
1080e0f1b28SNoah Shutty 
getDefaultDebuginfodTimeout()1090e0f1b28SNoah Shutty std::chrono::milliseconds getDefaultDebuginfodTimeout() {
1100e0f1b28SNoah Shutty   long Timeout;
1110e0f1b28SNoah Shutty   const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT");
1120e0f1b28SNoah Shutty   if (DebuginfodTimeoutEnv &&
1130e0f1b28SNoah Shutty       to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10))
1140e0f1b28SNoah Shutty     return std::chrono::milliseconds(Timeout * 1000);
1150e0f1b28SNoah Shutty 
1160e0f1b28SNoah Shutty   return std::chrono::milliseconds(90 * 1000);
1170e0f1b28SNoah Shutty }
1180e0f1b28SNoah Shutty 
1190e0f1b28SNoah Shutty /// The following functions fetch a debuginfod artifact to a file in a local
1200e0f1b28SNoah Shutty /// cache and return the cached file path. They first search the local cache,
1210e0f1b28SNoah Shutty /// followed by the debuginfod servers.
1220e0f1b28SNoah Shutty 
getDebuginfodSourceUrlPath(BuildIDRef ID,StringRef SourceFilePath)1236d5f8d3eSKevin Frei std::string getDebuginfodSourceUrlPath(BuildIDRef ID,
1240e0f1b28SNoah Shutty                                        StringRef SourceFilePath) {
1250e0f1b28SNoah Shutty   SmallString<64> UrlPath;
1260e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
1270e0f1b28SNoah Shutty                     buildIDToString(ID), "source",
1280e0f1b28SNoah Shutty                     sys::path::convert_to_slash(SourceFilePath));
1296d5f8d3eSKevin Frei   return std::string(UrlPath);
1300e0f1b28SNoah Shutty }
1310e0f1b28SNoah Shutty 
getCachedOrDownloadSource(BuildIDRef ID,StringRef SourceFilePath)1326d5f8d3eSKevin Frei Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID,
1336d5f8d3eSKevin Frei                                                 StringRef SourceFilePath) {
1346d5f8d3eSKevin Frei   std::string UrlPath = getDebuginfodSourceUrlPath(ID, SourceFilePath);
1356d5f8d3eSKevin Frei   return getCachedOrDownloadArtifact(getDebuginfodCacheKey(UrlPath), UrlPath);
1366d5f8d3eSKevin Frei }
1376d5f8d3eSKevin Frei 
getDebuginfodExecutableUrlPath(BuildIDRef ID)1386d5f8d3eSKevin Frei std::string getDebuginfodExecutableUrlPath(BuildIDRef ID) {
1390e0f1b28SNoah Shutty   SmallString<64> UrlPath;
1400e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
1410e0f1b28SNoah Shutty                     buildIDToString(ID), "executable");
1426d5f8d3eSKevin Frei   return std::string(UrlPath);
1430e0f1b28SNoah Shutty }
1440e0f1b28SNoah Shutty 
getCachedOrDownloadExecutable(BuildIDRef ID)1456d5f8d3eSKevin Frei Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) {
1466d5f8d3eSKevin Frei   std::string UrlPath = getDebuginfodExecutableUrlPath(ID);
1476d5f8d3eSKevin Frei   return getCachedOrDownloadArtifact(getDebuginfodCacheKey(UrlPath), UrlPath);
1486d5f8d3eSKevin Frei }
1496d5f8d3eSKevin Frei 
getDebuginfodDebuginfoUrlPath(BuildIDRef ID)1506d5f8d3eSKevin Frei std::string getDebuginfodDebuginfoUrlPath(BuildIDRef ID) {
1510e0f1b28SNoah Shutty   SmallString<64> UrlPath;
1520e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
1530e0f1b28SNoah Shutty                     buildIDToString(ID), "debuginfo");
1546d5f8d3eSKevin Frei   return std::string(UrlPath);
1556d5f8d3eSKevin Frei }
1566d5f8d3eSKevin Frei 
getCachedOrDownloadDebuginfo(BuildIDRef ID)1576d5f8d3eSKevin Frei Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) {
1586d5f8d3eSKevin Frei   std::string UrlPath = getDebuginfodDebuginfoUrlPath(ID);
1596d5f8d3eSKevin Frei   return getCachedOrDownloadArtifact(getDebuginfodCacheKey(UrlPath), UrlPath);
1600e0f1b28SNoah Shutty }
1610e0f1b28SNoah Shutty 
1620e0f1b28SNoah Shutty // General fetching function.
getCachedOrDownloadArtifact(StringRef UniqueKey,StringRef UrlPath)1630e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
1640e0f1b28SNoah Shutty                                                   StringRef UrlPath) {
1650e0f1b28SNoah Shutty   SmallString<10> CacheDir;
1660e0f1b28SNoah Shutty 
1670e0f1b28SNoah Shutty   Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory();
1680e0f1b28SNoah Shutty   if (!CacheDirOrErr)
1690e0f1b28SNoah Shutty     return CacheDirOrErr.takeError();
1700e0f1b28SNoah Shutty   CacheDir = *CacheDirOrErr;
1710e0f1b28SNoah Shutty 
1720e0f1b28SNoah Shutty   return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir,
173a3b0dde4SDaniel Thornburgh                                      getDefaultDebuginfodUrls(),
1740e0f1b28SNoah Shutty                                      getDefaultDebuginfodTimeout());
1750e0f1b28SNoah Shutty }
1760e0f1b28SNoah Shutty 
1777917b3c6SDaniel Thornburgh namespace {
1787917b3c6SDaniel Thornburgh 
1797917b3c6SDaniel Thornburgh /// A simple handler which streams the returned data to a cache file. The cache
1807917b3c6SDaniel Thornburgh /// file is only created if a 200 OK status is observed.
1817917b3c6SDaniel Thornburgh class StreamedHTTPResponseHandler : public HTTPResponseHandler {
1827917b3c6SDaniel Thornburgh   using CreateStreamFn =
1837917b3c6SDaniel Thornburgh       std::function<Expected<std::unique_ptr<CachedFileStream>>()>;
1847917b3c6SDaniel Thornburgh   CreateStreamFn CreateStream;
1857917b3c6SDaniel Thornburgh   HTTPClient &Client;
1867917b3c6SDaniel Thornburgh   std::unique_ptr<CachedFileStream> FileStream;
1877917b3c6SDaniel Thornburgh 
1887917b3c6SDaniel Thornburgh public:
StreamedHTTPResponseHandler(CreateStreamFn CreateStream,HTTPClient & Client)1897917b3c6SDaniel Thornburgh   StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
1907917b3c6SDaniel Thornburgh       : CreateStream(CreateStream), Client(Client) {}
191ace2a6c1SDaniel Thornburgh   virtual ~StreamedHTTPResponseHandler() = default;
1927917b3c6SDaniel Thornburgh 
1937917b3c6SDaniel Thornburgh   Error handleBodyChunk(StringRef BodyChunk) override;
1947917b3c6SDaniel Thornburgh };
1957917b3c6SDaniel Thornburgh 
1967917b3c6SDaniel Thornburgh } // namespace
1977917b3c6SDaniel Thornburgh 
handleBodyChunk(StringRef BodyChunk)1987917b3c6SDaniel Thornburgh Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
1997917b3c6SDaniel Thornburgh   if (!FileStream) {
200a3b0dde4SDaniel Thornburgh     unsigned Code = Client.responseCode();
201a3b0dde4SDaniel Thornburgh     if (Code && Code != 200)
2027917b3c6SDaniel Thornburgh       return Error::success();
2037917b3c6SDaniel Thornburgh     Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError =
2047917b3c6SDaniel Thornburgh         CreateStream();
2057917b3c6SDaniel Thornburgh     if (!FileStreamOrError)
2067917b3c6SDaniel Thornburgh       return FileStreamOrError.takeError();
2077917b3c6SDaniel Thornburgh     FileStream = std::move(*FileStreamOrError);
2087917b3c6SDaniel Thornburgh   }
2097917b3c6SDaniel Thornburgh   *FileStream->OS << BodyChunk;
2107917b3c6SDaniel Thornburgh   return Error::success();
2117917b3c6SDaniel Thornburgh }
2127917b3c6SDaniel Thornburgh 
21332a02a9cSDaniel Thornburgh // An over-accepting simplification of the HTTP RFC 7230 spec.
isHeader(StringRef S)21432a02a9cSDaniel Thornburgh static bool isHeader(StringRef S) {
21532a02a9cSDaniel Thornburgh   StringRef Name;
21632a02a9cSDaniel Thornburgh   StringRef Value;
21732a02a9cSDaniel Thornburgh   std::tie(Name, Value) = S.split(':');
21832a02a9cSDaniel Thornburgh   if (Name.empty() || Value.empty())
21932a02a9cSDaniel Thornburgh     return false;
22032a02a9cSDaniel Thornburgh   return all_of(Name, [](char C) { return llvm::isPrint(C) && C != ' '; }) &&
22132a02a9cSDaniel Thornburgh          all_of(Value, [](char C) { return llvm::isPrint(C) || C == '\t'; });
22232a02a9cSDaniel Thornburgh }
22332a02a9cSDaniel Thornburgh 
getHeaders()22432a02a9cSDaniel Thornburgh static SmallVector<std::string, 0> getHeaders() {
22532a02a9cSDaniel Thornburgh   const char *Filename = getenv("DEBUGINFOD_HEADERS_FILE");
22632a02a9cSDaniel Thornburgh   if (!Filename)
22732a02a9cSDaniel Thornburgh     return {};
22832a02a9cSDaniel Thornburgh   ErrorOr<std::unique_ptr<MemoryBuffer>> HeadersFile =
22932a02a9cSDaniel Thornburgh       MemoryBuffer::getFile(Filename, /*IsText=*/true);
23032a02a9cSDaniel Thornburgh   if (!HeadersFile)
23132a02a9cSDaniel Thornburgh     return {};
23232a02a9cSDaniel Thornburgh 
23332a02a9cSDaniel Thornburgh   SmallVector<std::string, 0> Headers;
23432a02a9cSDaniel Thornburgh   uint64_t LineNumber = 0;
23532a02a9cSDaniel Thornburgh   for (StringRef Line : llvm::split((*HeadersFile)->getBuffer(), '\n')) {
23632a02a9cSDaniel Thornburgh     LineNumber++;
23720d6f630SDaniel Thornburgh     if (!Line.empty() && Line.back() == '\r')
23820d6f630SDaniel Thornburgh       Line = Line.drop_back();
23932a02a9cSDaniel Thornburgh     if (!isHeader(Line)) {
24032a02a9cSDaniel Thornburgh       if (!all_of(Line, llvm::isSpace))
24132a02a9cSDaniel Thornburgh         WithColor::warning()
24232a02a9cSDaniel Thornburgh             << "could not parse debuginfod header: " << Filename << ':'
24332a02a9cSDaniel Thornburgh             << LineNumber << '\n';
24432a02a9cSDaniel Thornburgh       continue;
24532a02a9cSDaniel Thornburgh     }
24632a02a9cSDaniel Thornburgh     Headers.emplace_back(Line);
24732a02a9cSDaniel Thornburgh   }
24832a02a9cSDaniel Thornburgh   return Headers;
24932a02a9cSDaniel Thornburgh }
25032a02a9cSDaniel Thornburgh 
getCachedOrDownloadArtifact(StringRef UniqueKey,StringRef UrlPath,StringRef CacheDirectoryPath,ArrayRef<StringRef> DebuginfodUrls,std::chrono::milliseconds Timeout)2510e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadArtifact(
2520e0f1b28SNoah Shutty     StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
2530e0f1b28SNoah Shutty     ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) {
2540e0f1b28SNoah Shutty   SmallString<64> AbsCachedArtifactPath;
2550e0f1b28SNoah Shutty   sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath,
2560e0f1b28SNoah Shutty                     "llvmcache-" + UniqueKey);
2570e0f1b28SNoah Shutty 
2580e0f1b28SNoah Shutty   Expected<FileCache> CacheOrErr =
2590e0f1b28SNoah Shutty       localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath);
2600e0f1b28SNoah Shutty   if (!CacheOrErr)
2610e0f1b28SNoah Shutty     return CacheOrErr.takeError();
2620e0f1b28SNoah Shutty 
2630e0f1b28SNoah Shutty   FileCache Cache = *CacheOrErr;
2640e0f1b28SNoah Shutty   // We choose an arbitrary Task parameter as we do not make use of it.
2650e0f1b28SNoah Shutty   unsigned Task = 0;
26684be92d2SZequan Wu   Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey, "");
2670e0f1b28SNoah Shutty   if (!CacheAddStreamOrErr)
2680e0f1b28SNoah Shutty     return CacheAddStreamOrErr.takeError();
2690e0f1b28SNoah Shutty   AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
2700e0f1b28SNoah Shutty   if (!CacheAddStream)
2710e0f1b28SNoah Shutty     return std::string(AbsCachedArtifactPath);
2720e0f1b28SNoah Shutty   // The artifact was not found in the local cache, query the debuginfod
2730e0f1b28SNoah Shutty   // servers.
2740e0f1b28SNoah Shutty   if (!HTTPClient::isAvailable())
2750e0f1b28SNoah Shutty     return createStringError(errc::io_error,
2760e0f1b28SNoah Shutty                              "No working HTTP client is available.");
2770e0f1b28SNoah Shutty 
27834491ca7SNoah Shutty   if (!HTTPClient::IsInitialized)
27934491ca7SNoah Shutty     return createStringError(
28034491ca7SNoah Shutty         errc::io_error,
28134491ca7SNoah Shutty         "A working HTTP client is available, but it is not initialized. To "
28234491ca7SNoah Shutty         "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() "
28334491ca7SNoah Shutty         "at the beginning of main.");
28434491ca7SNoah Shutty 
2850e0f1b28SNoah Shutty   HTTPClient Client;
2860e0f1b28SNoah Shutty   Client.setTimeout(Timeout);
2870e0f1b28SNoah Shutty   for (StringRef ServerUrl : DebuginfodUrls) {
2880e0f1b28SNoah Shutty     SmallString<64> ArtifactUrl;
2890e0f1b28SNoah Shutty     sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
2900e0f1b28SNoah Shutty 
2917917b3c6SDaniel Thornburgh     // Perform the HTTP request and if successful, write the response body to
2927917b3c6SDaniel Thornburgh     // the cache.
293cd935224SDaniel Thornburgh     {
29484be92d2SZequan Wu       StreamedHTTPResponseHandler Handler(
29584be92d2SZequan Wu           [&]() { return CacheAddStream(Task, ""); }, Client);
2967917b3c6SDaniel Thornburgh       HTTPRequest Request(ArtifactUrl);
29732a02a9cSDaniel Thornburgh       Request.Headers = getHeaders();
2987917b3c6SDaniel Thornburgh       Error Err = Client.perform(Request, Handler);
2997917b3c6SDaniel Thornburgh       if (Err)
300ace2a6c1SDaniel Thornburgh         return std::move(Err);
3010e0f1b28SNoah Shutty 
302a3b0dde4SDaniel Thornburgh       unsigned Code = Client.responseCode();
303a3b0dde4SDaniel Thornburgh       if (Code && Code != 200)
3040e0f1b28SNoah Shutty         continue;
305cd935224SDaniel Thornburgh     }
306cd935224SDaniel Thornburgh 
307cd935224SDaniel Thornburgh     Expected<CachePruningPolicy> PruningPolicyOrErr =
308cd935224SDaniel Thornburgh         parseCachePruningPolicy(std::getenv("DEBUGINFOD_CACHE_POLICY"));
309cd935224SDaniel Thornburgh     if (!PruningPolicyOrErr)
310cd935224SDaniel Thornburgh       return PruningPolicyOrErr.takeError();
311cd935224SDaniel Thornburgh     pruneCache(CacheDirectoryPath, *PruningPolicyOrErr);
3120e0f1b28SNoah Shutty 
3130e0f1b28SNoah Shutty     // Return the path to the artifact on disk.
3140e0f1b28SNoah Shutty     return std::string(AbsCachedArtifactPath);
3150e0f1b28SNoah Shutty   }
3160e0f1b28SNoah Shutty 
3170e0f1b28SNoah Shutty   return createStringError(errc::argument_out_of_domain, "build id not found");
3180e0f1b28SNoah Shutty }
319babef908SNoah Shutty 
DebuginfodLogEntry(const Twine & Message)320babef908SNoah Shutty DebuginfodLogEntry::DebuginfodLogEntry(const Twine &Message)
321babef908SNoah Shutty     : Message(Message.str()) {}
322babef908SNoah Shutty 
push(const Twine & Message)323babef908SNoah Shutty void DebuginfodLog::push(const Twine &Message) {
324babef908SNoah Shutty   push(DebuginfodLogEntry(Message));
325babef908SNoah Shutty }
326babef908SNoah Shutty 
push(DebuginfodLogEntry Entry)327babef908SNoah Shutty void DebuginfodLog::push(DebuginfodLogEntry Entry) {
328babef908SNoah Shutty   {
329babef908SNoah Shutty     std::lock_guard<std::mutex> Guard(QueueMutex);
330babef908SNoah Shutty     LogEntryQueue.push(Entry);
331babef908SNoah Shutty   }
332babef908SNoah Shutty   QueueCondition.notify_one();
333babef908SNoah Shutty }
334babef908SNoah Shutty 
pop()335babef908SNoah Shutty DebuginfodLogEntry DebuginfodLog::pop() {
336babef908SNoah Shutty   {
337babef908SNoah Shutty     std::unique_lock<std::mutex> Guard(QueueMutex);
338babef908SNoah Shutty     // Wait for messages to be pushed into the queue.
339babef908SNoah Shutty     QueueCondition.wait(Guard, [&] { return !LogEntryQueue.empty(); });
340babef908SNoah Shutty   }
341babef908SNoah Shutty   std::lock_guard<std::mutex> Guard(QueueMutex);
342babef908SNoah Shutty   if (!LogEntryQueue.size())
343babef908SNoah Shutty     llvm_unreachable("Expected message in the queue.");
344babef908SNoah Shutty 
345babef908SNoah Shutty   DebuginfodLogEntry Entry = LogEntryQueue.front();
346babef908SNoah Shutty   LogEntryQueue.pop();
347babef908SNoah Shutty   return Entry;
348babef908SNoah Shutty }
349babef908SNoah Shutty 
DebuginfodCollection(ArrayRef<StringRef> PathsRef,DebuginfodLog & Log,ThreadPoolInterface & Pool,double MinInterval)350babef908SNoah Shutty DebuginfodCollection::DebuginfodCollection(ArrayRef<StringRef> PathsRef,
351*6594f428SMehdi Amini                                            DebuginfodLog &Log,
352*6594f428SMehdi Amini                                            ThreadPoolInterface &Pool,
353babef908SNoah Shutty                                            double MinInterval)
354babef908SNoah Shutty     : Log(Log), Pool(Pool), MinInterval(MinInterval) {
355babef908SNoah Shutty   for (StringRef Path : PathsRef)
356babef908SNoah Shutty     Paths.push_back(Path.str());
357babef908SNoah Shutty }
358babef908SNoah Shutty 
update()359babef908SNoah Shutty Error DebuginfodCollection::update() {
360babef908SNoah Shutty   std::lock_guard<sys::Mutex> Guard(UpdateMutex);
361babef908SNoah Shutty   if (UpdateTimer.isRunning())
362babef908SNoah Shutty     UpdateTimer.stopTimer();
363babef908SNoah Shutty   UpdateTimer.clear();
364babef908SNoah Shutty   for (const std::string &Path : Paths) {
365babef908SNoah Shutty     Log.push("Updating binaries at path " + Path);
366babef908SNoah Shutty     if (Error Err = findBinaries(Path))
367babef908SNoah Shutty       return Err;
368babef908SNoah Shutty   }
369babef908SNoah Shutty   Log.push("Updated collection");
370babef908SNoah Shutty   UpdateTimer.startTimer();
371babef908SNoah Shutty   return Error::success();
372babef908SNoah Shutty }
373babef908SNoah Shutty 
updateIfStale()374babef908SNoah Shutty Expected<bool> DebuginfodCollection::updateIfStale() {
375babef908SNoah Shutty   if (!UpdateTimer.isRunning())
376babef908SNoah Shutty     return false;
377babef908SNoah Shutty   UpdateTimer.stopTimer();
378babef908SNoah Shutty   double Time = UpdateTimer.getTotalTime().getWallTime();
379babef908SNoah Shutty   UpdateTimer.startTimer();
380babef908SNoah Shutty   if (Time < MinInterval)
381babef908SNoah Shutty     return false;
382babef908SNoah Shutty   if (Error Err = update())
383babef908SNoah Shutty     return std::move(Err);
384babef908SNoah Shutty   return true;
385babef908SNoah Shutty }
386babef908SNoah Shutty 
updateForever(std::chrono::milliseconds Interval)387babef908SNoah Shutty Error DebuginfodCollection::updateForever(std::chrono::milliseconds Interval) {
388babef908SNoah Shutty   while (true) {
389babef908SNoah Shutty     if (Error Err = update())
390babef908SNoah Shutty       return Err;
391babef908SNoah Shutty     std::this_thread::sleep_for(Interval);
392babef908SNoah Shutty   }
393babef908SNoah Shutty   llvm_unreachable("updateForever loop should never end");
394babef908SNoah Shutty }
395babef908SNoah Shutty 
hasELFMagic(StringRef FilePath)396babef908SNoah Shutty static bool hasELFMagic(StringRef FilePath) {
397babef908SNoah Shutty   file_magic Type;
398babef908SNoah Shutty   std::error_code EC = identify_magic(FilePath, Type);
399babef908SNoah Shutty   if (EC)
400babef908SNoah Shutty     return false;
401babef908SNoah Shutty   switch (Type) {
402babef908SNoah Shutty   case file_magic::elf:
403babef908SNoah Shutty   case file_magic::elf_relocatable:
404babef908SNoah Shutty   case file_magic::elf_executable:
405babef908SNoah Shutty   case file_magic::elf_shared_object:
406babef908SNoah Shutty   case file_magic::elf_core:
407babef908SNoah Shutty     return true;
408babef908SNoah Shutty   default:
409babef908SNoah Shutty     return false;
410babef908SNoah Shutty   }
411babef908SNoah Shutty }
412babef908SNoah Shutty 
findBinaries(StringRef Path)413babef908SNoah Shutty Error DebuginfodCollection::findBinaries(StringRef Path) {
414babef908SNoah Shutty   std::error_code EC;
415babef908SNoah Shutty   sys::fs::recursive_directory_iterator I(Twine(Path), EC), E;
416babef908SNoah Shutty   std::mutex IteratorMutex;
417babef908SNoah Shutty   ThreadPoolTaskGroup IteratorGroup(Pool);
418744616b3SMehdi Amini   for (unsigned WorkerIndex = 0; WorkerIndex < Pool.getMaxConcurrency();
419babef908SNoah Shutty        WorkerIndex++) {
420babef908SNoah Shutty     IteratorGroup.async([&, this]() -> void {
421babef908SNoah Shutty       std::string FilePath;
422babef908SNoah Shutty       while (true) {
423babef908SNoah Shutty         {
424babef908SNoah Shutty           // Check if iteration is over or there is an error during iteration
425babef908SNoah Shutty           std::lock_guard<std::mutex> Guard(IteratorMutex);
426babef908SNoah Shutty           if (I == E || EC)
427babef908SNoah Shutty             return;
428babef908SNoah Shutty           // Grab a file path from the directory iterator and advance the
429babef908SNoah Shutty           // iterator.
430babef908SNoah Shutty           FilePath = I->path();
431babef908SNoah Shutty           I.increment(EC);
432babef908SNoah Shutty         }
433babef908SNoah Shutty 
434babef908SNoah Shutty         // Inspect the file at this path to determine if it is debuginfo.
435babef908SNoah Shutty         if (!hasELFMagic(FilePath))
436babef908SNoah Shutty           continue;
437babef908SNoah Shutty 
438babef908SNoah Shutty         Expected<object::OwningBinary<object::Binary>> BinOrErr =
439babef908SNoah Shutty             object::createBinary(FilePath);
440babef908SNoah Shutty 
441babef908SNoah Shutty         if (!BinOrErr) {
442babef908SNoah Shutty           consumeError(BinOrErr.takeError());
443babef908SNoah Shutty           continue;
444babef908SNoah Shutty         }
445babef908SNoah Shutty         object::Binary *Bin = std::move(BinOrErr.get().getBinary());
446babef908SNoah Shutty         if (!Bin->isObject())
447babef908SNoah Shutty           continue;
448babef908SNoah Shutty 
449babef908SNoah Shutty         // TODO: Support non-ELF binaries
450babef908SNoah Shutty         object::ELFObjectFileBase *Object =
451babef908SNoah Shutty             dyn_cast<object::ELFObjectFileBase>(Bin);
452babef908SNoah Shutty         if (!Object)
453babef908SNoah Shutty           continue;
454babef908SNoah Shutty 
4559812948dSDaniel Thornburgh         BuildIDRef ID = getBuildID(Object);
4569812948dSDaniel Thornburgh         if (ID.empty())
457babef908SNoah Shutty           continue;
458babef908SNoah Shutty 
4599812948dSDaniel Thornburgh         std::string IDString = buildIDToString(ID);
460e61d89efSDaniel Thornburgh         if (Object->hasDebugInfo()) {
461babef908SNoah Shutty           std::lock_guard<sys::RWMutex> DebugBinariesGuard(DebugBinariesMutex);
46267ba5c50SFangrui Song           (void)DebugBinaries.try_emplace(IDString, std::move(FilePath));
463babef908SNoah Shutty         } else {
464babef908SNoah Shutty           std::lock_guard<sys::RWMutex> BinariesGuard(BinariesMutex);
46567ba5c50SFangrui Song           (void)Binaries.try_emplace(IDString, std::move(FilePath));
466babef908SNoah Shutty         }
467babef908SNoah Shutty       }
468babef908SNoah Shutty     });
469babef908SNoah Shutty   }
470babef908SNoah Shutty   IteratorGroup.wait();
471babef908SNoah Shutty   std::unique_lock<std::mutex> Guard(IteratorMutex);
472babef908SNoah Shutty   if (EC)
473babef908SNoah Shutty     return errorCodeToError(EC);
474babef908SNoah Shutty   return Error::success();
475babef908SNoah Shutty }
476babef908SNoah Shutty 
47777c90c8cSKazu Hirata Expected<std::optional<std::string>>
getBinaryPath(BuildIDRef ID)478babef908SNoah Shutty DebuginfodCollection::getBinaryPath(BuildIDRef ID) {
479babef908SNoah Shutty   Log.push("getting binary path of ID " + buildIDToString(ID));
480babef908SNoah Shutty   std::shared_lock<sys::RWMutex> Guard(BinariesMutex);
481babef908SNoah Shutty   auto Loc = Binaries.find(buildIDToString(ID));
482babef908SNoah Shutty   if (Loc != Binaries.end()) {
483babef908SNoah Shutty     std::string Path = Loc->getValue();
484babef908SNoah Shutty     return Path;
485babef908SNoah Shutty   }
486aadaafacSKazu Hirata   return std::nullopt;
487babef908SNoah Shutty }
488babef908SNoah Shutty 
48977c90c8cSKazu Hirata Expected<std::optional<std::string>>
getDebugBinaryPath(BuildIDRef ID)490babef908SNoah Shutty DebuginfodCollection::getDebugBinaryPath(BuildIDRef ID) {
491babef908SNoah Shutty   Log.push("getting debug binary path of ID " + buildIDToString(ID));
492babef908SNoah Shutty   std::shared_lock<sys::RWMutex> Guard(DebugBinariesMutex);
493babef908SNoah Shutty   auto Loc = DebugBinaries.find(buildIDToString(ID));
494babef908SNoah Shutty   if (Loc != DebugBinaries.end()) {
495babef908SNoah Shutty     std::string Path = Loc->getValue();
496babef908SNoah Shutty     return Path;
497babef908SNoah Shutty   }
498aadaafacSKazu Hirata   return std::nullopt;
499babef908SNoah Shutty }
500babef908SNoah Shutty 
findBinaryPath(BuildIDRef ID)501babef908SNoah Shutty Expected<std::string> DebuginfodCollection::findBinaryPath(BuildIDRef ID) {
502babef908SNoah Shutty   {
503babef908SNoah Shutty     // Check collection; perform on-demand update if stale.
50477c90c8cSKazu Hirata     Expected<std::optional<std::string>> PathOrErr = getBinaryPath(ID);
505babef908SNoah Shutty     if (!PathOrErr)
506babef908SNoah Shutty       return PathOrErr.takeError();
50777c90c8cSKazu Hirata     std::optional<std::string> Path = *PathOrErr;
508babef908SNoah Shutty     if (!Path) {
509babef908SNoah Shutty       Expected<bool> UpdatedOrErr = updateIfStale();
510babef908SNoah Shutty       if (!UpdatedOrErr)
511babef908SNoah Shutty         return UpdatedOrErr.takeError();
512babef908SNoah Shutty       if (*UpdatedOrErr) {
513babef908SNoah Shutty         // Try once more.
514babef908SNoah Shutty         PathOrErr = getBinaryPath(ID);
515babef908SNoah Shutty         if (!PathOrErr)
516babef908SNoah Shutty           return PathOrErr.takeError();
517babef908SNoah Shutty         Path = *PathOrErr;
518babef908SNoah Shutty       }
519babef908SNoah Shutty     }
520babef908SNoah Shutty     if (Path)
521a3209b0fSFangrui Song       return *Path;
522babef908SNoah Shutty   }
523babef908SNoah Shutty 
524babef908SNoah Shutty   // Try federation.
525babef908SNoah Shutty   Expected<std::string> PathOrErr = getCachedOrDownloadExecutable(ID);
526babef908SNoah Shutty   if (!PathOrErr)
527babef908SNoah Shutty     consumeError(PathOrErr.takeError());
528babef908SNoah Shutty 
529babef908SNoah Shutty   // Fall back to debug binary.
530babef908SNoah Shutty   return findDebugBinaryPath(ID);
531babef908SNoah Shutty }
532babef908SNoah Shutty 
findDebugBinaryPath(BuildIDRef ID)533babef908SNoah Shutty Expected<std::string> DebuginfodCollection::findDebugBinaryPath(BuildIDRef ID) {
534babef908SNoah Shutty   // Check collection; perform on-demand update if stale.
53577c90c8cSKazu Hirata   Expected<std::optional<std::string>> PathOrErr = getDebugBinaryPath(ID);
536babef908SNoah Shutty   if (!PathOrErr)
537babef908SNoah Shutty     return PathOrErr.takeError();
53877c90c8cSKazu Hirata   std::optional<std::string> Path = *PathOrErr;
539babef908SNoah Shutty   if (!Path) {
540babef908SNoah Shutty     Expected<bool> UpdatedOrErr = updateIfStale();
541babef908SNoah Shutty     if (!UpdatedOrErr)
542babef908SNoah Shutty       return UpdatedOrErr.takeError();
543babef908SNoah Shutty     if (*UpdatedOrErr) {
544babef908SNoah Shutty       // Try once more.
545babef908SNoah Shutty       PathOrErr = getBinaryPath(ID);
546babef908SNoah Shutty       if (!PathOrErr)
547babef908SNoah Shutty         return PathOrErr.takeError();
548babef908SNoah Shutty       Path = *PathOrErr;
549babef908SNoah Shutty     }
550babef908SNoah Shutty   }
551babef908SNoah Shutty   if (Path)
552a3209b0fSFangrui Song     return *Path;
553babef908SNoah Shutty 
554babef908SNoah Shutty   // Try federation.
555babef908SNoah Shutty   return getCachedOrDownloadDebuginfo(ID);
556babef908SNoah Shutty }
557babef908SNoah Shutty 
DebuginfodServer(DebuginfodLog & Log,DebuginfodCollection & Collection)558babef908SNoah Shutty DebuginfodServer::DebuginfodServer(DebuginfodLog &Log,
559babef908SNoah Shutty                                    DebuginfodCollection &Collection)
560babef908SNoah Shutty     : Log(Log), Collection(Collection) {
561babef908SNoah Shutty   cantFail(
562babef908SNoah Shutty       Server.get(R"(/buildid/(.*)/debuginfo)", [&](HTTPServerRequest Request) {
563babef908SNoah Shutty         Log.push("GET " + Request.UrlPath);
564babef908SNoah Shutty         std::string IDString;
565babef908SNoah Shutty         if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
566babef908SNoah Shutty           Request.setResponse(
567babef908SNoah Shutty               {404, "text/plain", "Build ID is not a hex string\n"});
568babef908SNoah Shutty           return;
569babef908SNoah Shutty         }
570e61d89efSDaniel Thornburgh         object::BuildID ID(IDString.begin(), IDString.end());
571babef908SNoah Shutty         Expected<std::string> PathOrErr = Collection.findDebugBinaryPath(ID);
572babef908SNoah Shutty         if (Error Err = PathOrErr.takeError()) {
573babef908SNoah Shutty           consumeError(std::move(Err));
574babef908SNoah Shutty           Request.setResponse({404, "text/plain", "Build ID not found\n"});
575babef908SNoah Shutty           return;
576babef908SNoah Shutty         }
577babef908SNoah Shutty         streamFile(Request, *PathOrErr);
578babef908SNoah Shutty       }));
579babef908SNoah Shutty   cantFail(
580babef908SNoah Shutty       Server.get(R"(/buildid/(.*)/executable)", [&](HTTPServerRequest Request) {
581babef908SNoah Shutty         Log.push("GET " + Request.UrlPath);
582babef908SNoah Shutty         std::string IDString;
583babef908SNoah Shutty         if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
584babef908SNoah Shutty           Request.setResponse(
585babef908SNoah Shutty               {404, "text/plain", "Build ID is not a hex string\n"});
586babef908SNoah Shutty           return;
587babef908SNoah Shutty         }
588e61d89efSDaniel Thornburgh         object::BuildID ID(IDString.begin(), IDString.end());
589babef908SNoah Shutty         Expected<std::string> PathOrErr = Collection.findBinaryPath(ID);
590babef908SNoah Shutty         if (Error Err = PathOrErr.takeError()) {
591babef908SNoah Shutty           consumeError(std::move(Err));
592babef908SNoah Shutty           Request.setResponse({404, "text/plain", "Build ID not found\n"});
593babef908SNoah Shutty           return;
594babef908SNoah Shutty         }
595babef908SNoah Shutty         streamFile(Request, *PathOrErr);
596babef908SNoah Shutty       }));
597babef908SNoah Shutty }
598babef908SNoah Shutty 
5990e0f1b28SNoah Shutty } // namespace llvm
600