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