xref: /freebsd-src/contrib/llvm-project/llvm/lib/Debuginfod/Debuginfod.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
10eae32dcSDimitry Andric //===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===//
20eae32dcSDimitry Andric //
30eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60eae32dcSDimitry Andric //
70eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
80eae32dcSDimitry Andric ///
90eae32dcSDimitry Andric /// \file
100eae32dcSDimitry Andric ///
110eae32dcSDimitry Andric /// This file defines the fetchInfo function, which retrieves
120eae32dcSDimitry Andric /// any of the three supported artifact types: (executable, debuginfo, source
130eae32dcSDimitry Andric /// file) associated with a build-id from debuginfod servers. If a source file
140eae32dcSDimitry Andric /// is to be fetched, its absolute path must be specified in the Description
150eae32dcSDimitry Andric /// argument to fetchInfo.
160eae32dcSDimitry Andric ///
170eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
180eae32dcSDimitry Andric 
190eae32dcSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h"
200eae32dcSDimitry Andric #include "llvm/ADT/StringRef.h"
210eae32dcSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
220eae32dcSDimitry Andric #include "llvm/Support/CachePruning.h"
230eae32dcSDimitry Andric #include "llvm/Support/Caching.h"
2404eeddc0SDimitry Andric #include "llvm/Support/Errc.h"
250eae32dcSDimitry Andric #include "llvm/Support/Error.h"
260eae32dcSDimitry Andric #include "llvm/Support/FileUtilities.h"
2704eeddc0SDimitry Andric #include "llvm/Support/Path.h"
280eae32dcSDimitry Andric #include "llvm/Support/xxhash.h"
290eae32dcSDimitry Andric 
300eae32dcSDimitry Andric namespace llvm {
310eae32dcSDimitry Andric static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); }
320eae32dcSDimitry Andric 
330eae32dcSDimitry Andric // Returns a binary BuildID as a normalized hex string.
340eae32dcSDimitry Andric // Uses lowercase for compatibility with common debuginfod servers.
350eae32dcSDimitry Andric static std::string buildIDToString(BuildIDRef ID) {
360eae32dcSDimitry Andric   return llvm::toHex(ID, /*LowerCase=*/true);
370eae32dcSDimitry Andric }
380eae32dcSDimitry Andric 
390eae32dcSDimitry Andric Expected<SmallVector<StringRef>> getDefaultDebuginfodUrls() {
400eae32dcSDimitry Andric   const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS");
4104eeddc0SDimitry Andric   if (DebuginfodUrlsEnv == nullptr)
420eae32dcSDimitry Andric     return SmallVector<StringRef>();
430eae32dcSDimitry Andric 
440eae32dcSDimitry Andric   SmallVector<StringRef> DebuginfodUrls;
450eae32dcSDimitry Andric   StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " ");
460eae32dcSDimitry Andric   return DebuginfodUrls;
470eae32dcSDimitry Andric }
480eae32dcSDimitry Andric 
490eae32dcSDimitry Andric Expected<std::string> getDefaultDebuginfodCacheDirectory() {
500eae32dcSDimitry Andric   if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH"))
510eae32dcSDimitry Andric     return CacheDirectoryEnv;
520eae32dcSDimitry Andric 
530eae32dcSDimitry Andric   SmallString<64> CacheDirectory;
540eae32dcSDimitry Andric   if (!sys::path::cache_directory(CacheDirectory))
550eae32dcSDimitry Andric     return createStringError(
560eae32dcSDimitry Andric         errc::io_error, "Unable to determine appropriate cache directory.");
5704eeddc0SDimitry Andric   sys::path::append(CacheDirectory, "llvm-debuginfod", "client");
580eae32dcSDimitry Andric   return std::string(CacheDirectory);
590eae32dcSDimitry Andric }
600eae32dcSDimitry Andric 
610eae32dcSDimitry Andric std::chrono::milliseconds getDefaultDebuginfodTimeout() {
620eae32dcSDimitry Andric   long Timeout;
630eae32dcSDimitry Andric   const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT");
640eae32dcSDimitry Andric   if (DebuginfodTimeoutEnv &&
650eae32dcSDimitry Andric       to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10))
660eae32dcSDimitry Andric     return std::chrono::milliseconds(Timeout * 1000);
670eae32dcSDimitry Andric 
680eae32dcSDimitry Andric   return std::chrono::milliseconds(90 * 1000);
690eae32dcSDimitry Andric }
700eae32dcSDimitry Andric 
710eae32dcSDimitry Andric /// The following functions fetch a debuginfod artifact to a file in a local
720eae32dcSDimitry Andric /// cache and return the cached file path. They first search the local cache,
730eae32dcSDimitry Andric /// followed by the debuginfod servers.
740eae32dcSDimitry Andric 
750eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID,
760eae32dcSDimitry Andric                                                 StringRef SourceFilePath) {
770eae32dcSDimitry Andric   SmallString<64> UrlPath;
780eae32dcSDimitry Andric   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
790eae32dcSDimitry Andric                     buildIDToString(ID), "source",
800eae32dcSDimitry Andric                     sys::path::convert_to_slash(SourceFilePath));
810eae32dcSDimitry Andric   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
820eae32dcSDimitry Andric }
830eae32dcSDimitry Andric 
840eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) {
850eae32dcSDimitry Andric   SmallString<64> UrlPath;
860eae32dcSDimitry Andric   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
870eae32dcSDimitry Andric                     buildIDToString(ID), "executable");
880eae32dcSDimitry Andric   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
890eae32dcSDimitry Andric }
900eae32dcSDimitry Andric 
910eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) {
920eae32dcSDimitry Andric   SmallString<64> UrlPath;
930eae32dcSDimitry Andric   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
940eae32dcSDimitry Andric                     buildIDToString(ID), "debuginfo");
950eae32dcSDimitry Andric   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
960eae32dcSDimitry Andric }
970eae32dcSDimitry Andric 
980eae32dcSDimitry Andric // General fetching function.
990eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
1000eae32dcSDimitry Andric                                                   StringRef UrlPath) {
1010eae32dcSDimitry Andric   SmallString<10> CacheDir;
1020eae32dcSDimitry Andric 
1030eae32dcSDimitry Andric   Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory();
1040eae32dcSDimitry Andric   if (!CacheDirOrErr)
1050eae32dcSDimitry Andric     return CacheDirOrErr.takeError();
1060eae32dcSDimitry Andric   CacheDir = *CacheDirOrErr;
1070eae32dcSDimitry Andric 
1080eae32dcSDimitry Andric   Expected<SmallVector<StringRef>> DebuginfodUrlsOrErr =
1090eae32dcSDimitry Andric       getDefaultDebuginfodUrls();
1100eae32dcSDimitry Andric   if (!DebuginfodUrlsOrErr)
1110eae32dcSDimitry Andric     return DebuginfodUrlsOrErr.takeError();
1120eae32dcSDimitry Andric   SmallVector<StringRef> &DebuginfodUrls = *DebuginfodUrlsOrErr;
1130eae32dcSDimitry Andric   return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir,
1140eae32dcSDimitry Andric                                      DebuginfodUrls,
1150eae32dcSDimitry Andric                                      getDefaultDebuginfodTimeout());
1160eae32dcSDimitry Andric }
1170eae32dcSDimitry Andric 
118*81ad6265SDimitry Andric namespace {
119*81ad6265SDimitry Andric 
120*81ad6265SDimitry Andric /// A simple handler which streams the returned data to a cache file. The cache
121*81ad6265SDimitry Andric /// file is only created if a 200 OK status is observed.
122*81ad6265SDimitry Andric class StreamedHTTPResponseHandler : public HTTPResponseHandler {
123*81ad6265SDimitry Andric   using CreateStreamFn =
124*81ad6265SDimitry Andric       std::function<Expected<std::unique_ptr<CachedFileStream>>()>;
125*81ad6265SDimitry Andric   CreateStreamFn CreateStream;
126*81ad6265SDimitry Andric   HTTPClient &Client;
127*81ad6265SDimitry Andric   std::unique_ptr<CachedFileStream> FileStream;
128*81ad6265SDimitry Andric 
129*81ad6265SDimitry Andric public:
130*81ad6265SDimitry Andric   StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
131*81ad6265SDimitry Andric       : CreateStream(CreateStream), Client(Client) {}
132*81ad6265SDimitry Andric   virtual ~StreamedHTTPResponseHandler() = default;
133*81ad6265SDimitry Andric 
134*81ad6265SDimitry Andric   Error handleBodyChunk(StringRef BodyChunk) override;
135*81ad6265SDimitry Andric };
136*81ad6265SDimitry Andric 
137*81ad6265SDimitry Andric } // namespace
138*81ad6265SDimitry Andric 
139*81ad6265SDimitry Andric Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
140*81ad6265SDimitry Andric   if (!FileStream) {
141*81ad6265SDimitry Andric     if (Client.responseCode() != 200)
142*81ad6265SDimitry Andric       return Error::success();
143*81ad6265SDimitry Andric     Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError =
144*81ad6265SDimitry Andric         CreateStream();
145*81ad6265SDimitry Andric     if (!FileStreamOrError)
146*81ad6265SDimitry Andric       return FileStreamOrError.takeError();
147*81ad6265SDimitry Andric     FileStream = std::move(*FileStreamOrError);
148*81ad6265SDimitry Andric   }
149*81ad6265SDimitry Andric   *FileStream->OS << BodyChunk;
150*81ad6265SDimitry Andric   return Error::success();
151*81ad6265SDimitry Andric }
152*81ad6265SDimitry Andric 
1530eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadArtifact(
1540eae32dcSDimitry Andric     StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
1550eae32dcSDimitry Andric     ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) {
1560eae32dcSDimitry Andric   SmallString<64> AbsCachedArtifactPath;
1570eae32dcSDimitry Andric   sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath,
1580eae32dcSDimitry Andric                     "llvmcache-" + UniqueKey);
1590eae32dcSDimitry Andric 
1600eae32dcSDimitry Andric   Expected<FileCache> CacheOrErr =
1610eae32dcSDimitry Andric       localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath);
1620eae32dcSDimitry Andric   if (!CacheOrErr)
1630eae32dcSDimitry Andric     return CacheOrErr.takeError();
1640eae32dcSDimitry Andric 
1650eae32dcSDimitry Andric   FileCache Cache = *CacheOrErr;
1660eae32dcSDimitry Andric   // We choose an arbitrary Task parameter as we do not make use of it.
1670eae32dcSDimitry Andric   unsigned Task = 0;
1680eae32dcSDimitry Andric   Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey);
1690eae32dcSDimitry Andric   if (!CacheAddStreamOrErr)
1700eae32dcSDimitry Andric     return CacheAddStreamOrErr.takeError();
1710eae32dcSDimitry Andric   AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
1720eae32dcSDimitry Andric   if (!CacheAddStream)
1730eae32dcSDimitry Andric     return std::string(AbsCachedArtifactPath);
1740eae32dcSDimitry Andric   // The artifact was not found in the local cache, query the debuginfod
1750eae32dcSDimitry Andric   // servers.
1760eae32dcSDimitry Andric   if (!HTTPClient::isAvailable())
1770eae32dcSDimitry Andric     return createStringError(errc::io_error,
1780eae32dcSDimitry Andric                              "No working HTTP client is available.");
1790eae32dcSDimitry Andric 
1800eae32dcSDimitry Andric   if (!HTTPClient::IsInitialized)
1810eae32dcSDimitry Andric     return createStringError(
1820eae32dcSDimitry Andric         errc::io_error,
1830eae32dcSDimitry Andric         "A working HTTP client is available, but it is not initialized. To "
1840eae32dcSDimitry Andric         "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() "
1850eae32dcSDimitry Andric         "at the beginning of main.");
1860eae32dcSDimitry Andric 
1870eae32dcSDimitry Andric   HTTPClient Client;
1880eae32dcSDimitry Andric   Client.setTimeout(Timeout);
1890eae32dcSDimitry Andric   for (StringRef ServerUrl : DebuginfodUrls) {
1900eae32dcSDimitry Andric     SmallString<64> ArtifactUrl;
1910eae32dcSDimitry Andric     sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
1920eae32dcSDimitry Andric 
193*81ad6265SDimitry Andric     // Perform the HTTP request and if successful, write the response body to
194*81ad6265SDimitry Andric     // the cache.
195*81ad6265SDimitry Andric     StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); },
196*81ad6265SDimitry Andric                                         Client);
197*81ad6265SDimitry Andric     HTTPRequest Request(ArtifactUrl);
198*81ad6265SDimitry Andric     Error Err = Client.perform(Request, Handler);
199*81ad6265SDimitry Andric     if (Err)
200*81ad6265SDimitry Andric       return std::move(Err);
2010eae32dcSDimitry Andric 
202*81ad6265SDimitry Andric     if (Client.responseCode() != 200)
2030eae32dcSDimitry Andric       continue;
2040eae32dcSDimitry Andric 
2050eae32dcSDimitry Andric     // Return the path to the artifact on disk.
2060eae32dcSDimitry Andric     return std::string(AbsCachedArtifactPath);
2070eae32dcSDimitry Andric   }
2080eae32dcSDimitry Andric 
2090eae32dcSDimitry Andric   return createStringError(errc::argument_out_of_domain, "build id not found");
2100eae32dcSDimitry Andric }
2110eae32dcSDimitry Andric } // namespace llvm
212