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