1*0eae32dcSDimitry Andric //===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===// 2*0eae32dcSDimitry Andric // 3*0eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0eae32dcSDimitry Andric // 7*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 8*0eae32dcSDimitry Andric /// 9*0eae32dcSDimitry Andric /// \file 10*0eae32dcSDimitry Andric /// 11*0eae32dcSDimitry Andric /// This file defines the fetchInfo function, which retrieves 12*0eae32dcSDimitry Andric /// any of the three supported artifact types: (executable, debuginfo, source 13*0eae32dcSDimitry Andric /// file) associated with a build-id from debuginfod servers. If a source file 14*0eae32dcSDimitry Andric /// is to be fetched, its absolute path must be specified in the Description 15*0eae32dcSDimitry Andric /// argument to fetchInfo. 16*0eae32dcSDimitry Andric /// 17*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 18*0eae32dcSDimitry Andric 19*0eae32dcSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h" 20*0eae32dcSDimitry Andric #include "llvm/ADT/StringRef.h" 21*0eae32dcSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h" 22*0eae32dcSDimitry Andric #include "llvm/Support/CachePruning.h" 23*0eae32dcSDimitry Andric #include "llvm/Support/Caching.h" 24*0eae32dcSDimitry Andric #include "llvm/Support/Error.h" 25*0eae32dcSDimitry Andric #include "llvm/Support/FileUtilities.h" 26*0eae32dcSDimitry Andric #include "llvm/Support/xxhash.h" 27*0eae32dcSDimitry Andric 28*0eae32dcSDimitry Andric namespace llvm { 29*0eae32dcSDimitry Andric static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); } 30*0eae32dcSDimitry Andric 31*0eae32dcSDimitry Andric // Returns a binary BuildID as a normalized hex string. 32*0eae32dcSDimitry Andric // Uses lowercase for compatibility with common debuginfod servers. 33*0eae32dcSDimitry Andric static std::string buildIDToString(BuildIDRef ID) { 34*0eae32dcSDimitry Andric return llvm::toHex(ID, /*LowerCase=*/true); 35*0eae32dcSDimitry Andric } 36*0eae32dcSDimitry Andric 37*0eae32dcSDimitry Andric Expected<SmallVector<StringRef>> getDefaultDebuginfodUrls() { 38*0eae32dcSDimitry Andric const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS"); 39*0eae32dcSDimitry Andric if (DebuginfodUrlsEnv == NULL) 40*0eae32dcSDimitry Andric return SmallVector<StringRef>(); 41*0eae32dcSDimitry Andric 42*0eae32dcSDimitry Andric SmallVector<StringRef> DebuginfodUrls; 43*0eae32dcSDimitry Andric StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " "); 44*0eae32dcSDimitry Andric return DebuginfodUrls; 45*0eae32dcSDimitry Andric } 46*0eae32dcSDimitry Andric 47*0eae32dcSDimitry Andric Expected<std::string> getDefaultDebuginfodCacheDirectory() { 48*0eae32dcSDimitry Andric if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH")) 49*0eae32dcSDimitry Andric return CacheDirectoryEnv; 50*0eae32dcSDimitry Andric 51*0eae32dcSDimitry Andric SmallString<64> CacheDirectory; 52*0eae32dcSDimitry Andric if (!sys::path::cache_directory(CacheDirectory)) 53*0eae32dcSDimitry Andric return createStringError( 54*0eae32dcSDimitry Andric errc::io_error, "Unable to determine appropriate cache directory."); 55*0eae32dcSDimitry Andric return std::string(CacheDirectory); 56*0eae32dcSDimitry Andric } 57*0eae32dcSDimitry Andric 58*0eae32dcSDimitry Andric std::chrono::milliseconds getDefaultDebuginfodTimeout() { 59*0eae32dcSDimitry Andric long Timeout; 60*0eae32dcSDimitry Andric const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT"); 61*0eae32dcSDimitry Andric if (DebuginfodTimeoutEnv && 62*0eae32dcSDimitry Andric to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10)) 63*0eae32dcSDimitry Andric return std::chrono::milliseconds(Timeout * 1000); 64*0eae32dcSDimitry Andric 65*0eae32dcSDimitry Andric return std::chrono::milliseconds(90 * 1000); 66*0eae32dcSDimitry Andric } 67*0eae32dcSDimitry Andric 68*0eae32dcSDimitry Andric /// The following functions fetch a debuginfod artifact to a file in a local 69*0eae32dcSDimitry Andric /// cache and return the cached file path. They first search the local cache, 70*0eae32dcSDimitry Andric /// followed by the debuginfod servers. 71*0eae32dcSDimitry Andric 72*0eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID, 73*0eae32dcSDimitry Andric StringRef SourceFilePath) { 74*0eae32dcSDimitry Andric SmallString<64> UrlPath; 75*0eae32dcSDimitry Andric sys::path::append(UrlPath, sys::path::Style::posix, "buildid", 76*0eae32dcSDimitry Andric buildIDToString(ID), "source", 77*0eae32dcSDimitry Andric sys::path::convert_to_slash(SourceFilePath)); 78*0eae32dcSDimitry Andric return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); 79*0eae32dcSDimitry Andric } 80*0eae32dcSDimitry Andric 81*0eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) { 82*0eae32dcSDimitry Andric SmallString<64> UrlPath; 83*0eae32dcSDimitry Andric sys::path::append(UrlPath, sys::path::Style::posix, "buildid", 84*0eae32dcSDimitry Andric buildIDToString(ID), "executable"); 85*0eae32dcSDimitry Andric return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); 86*0eae32dcSDimitry Andric } 87*0eae32dcSDimitry Andric 88*0eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) { 89*0eae32dcSDimitry Andric SmallString<64> UrlPath; 90*0eae32dcSDimitry Andric sys::path::append(UrlPath, sys::path::Style::posix, "buildid", 91*0eae32dcSDimitry Andric buildIDToString(ID), "debuginfo"); 92*0eae32dcSDimitry Andric return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); 93*0eae32dcSDimitry Andric } 94*0eae32dcSDimitry Andric 95*0eae32dcSDimitry Andric // General fetching function. 96*0eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey, 97*0eae32dcSDimitry Andric StringRef UrlPath) { 98*0eae32dcSDimitry Andric SmallString<10> CacheDir; 99*0eae32dcSDimitry Andric 100*0eae32dcSDimitry Andric Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory(); 101*0eae32dcSDimitry Andric if (!CacheDirOrErr) 102*0eae32dcSDimitry Andric return CacheDirOrErr.takeError(); 103*0eae32dcSDimitry Andric CacheDir = *CacheDirOrErr; 104*0eae32dcSDimitry Andric 105*0eae32dcSDimitry Andric Expected<SmallVector<StringRef>> DebuginfodUrlsOrErr = 106*0eae32dcSDimitry Andric getDefaultDebuginfodUrls(); 107*0eae32dcSDimitry Andric if (!DebuginfodUrlsOrErr) 108*0eae32dcSDimitry Andric return DebuginfodUrlsOrErr.takeError(); 109*0eae32dcSDimitry Andric SmallVector<StringRef> &DebuginfodUrls = *DebuginfodUrlsOrErr; 110*0eae32dcSDimitry Andric return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir, 111*0eae32dcSDimitry Andric DebuginfodUrls, 112*0eae32dcSDimitry Andric getDefaultDebuginfodTimeout()); 113*0eae32dcSDimitry Andric } 114*0eae32dcSDimitry Andric 115*0eae32dcSDimitry Andric Expected<std::string> getCachedOrDownloadArtifact( 116*0eae32dcSDimitry Andric StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath, 117*0eae32dcSDimitry Andric ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) { 118*0eae32dcSDimitry Andric SmallString<64> AbsCachedArtifactPath; 119*0eae32dcSDimitry Andric sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath, 120*0eae32dcSDimitry Andric "llvmcache-" + UniqueKey); 121*0eae32dcSDimitry Andric 122*0eae32dcSDimitry Andric Expected<FileCache> CacheOrErr = 123*0eae32dcSDimitry Andric localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath); 124*0eae32dcSDimitry Andric if (!CacheOrErr) 125*0eae32dcSDimitry Andric return CacheOrErr.takeError(); 126*0eae32dcSDimitry Andric 127*0eae32dcSDimitry Andric FileCache Cache = *CacheOrErr; 128*0eae32dcSDimitry Andric // We choose an arbitrary Task parameter as we do not make use of it. 129*0eae32dcSDimitry Andric unsigned Task = 0; 130*0eae32dcSDimitry Andric Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey); 131*0eae32dcSDimitry Andric if (!CacheAddStreamOrErr) 132*0eae32dcSDimitry Andric return CacheAddStreamOrErr.takeError(); 133*0eae32dcSDimitry Andric AddStreamFn &CacheAddStream = *CacheAddStreamOrErr; 134*0eae32dcSDimitry Andric if (!CacheAddStream) 135*0eae32dcSDimitry Andric return std::string(AbsCachedArtifactPath); 136*0eae32dcSDimitry Andric // The artifact was not found in the local cache, query the debuginfod 137*0eae32dcSDimitry Andric // servers. 138*0eae32dcSDimitry Andric if (!HTTPClient::isAvailable()) 139*0eae32dcSDimitry Andric return createStringError(errc::io_error, 140*0eae32dcSDimitry Andric "No working HTTP client is available."); 141*0eae32dcSDimitry Andric 142*0eae32dcSDimitry Andric if (!HTTPClient::IsInitialized) 143*0eae32dcSDimitry Andric return createStringError( 144*0eae32dcSDimitry Andric errc::io_error, 145*0eae32dcSDimitry Andric "A working HTTP client is available, but it is not initialized. To " 146*0eae32dcSDimitry Andric "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() " 147*0eae32dcSDimitry Andric "at the beginning of main."); 148*0eae32dcSDimitry Andric 149*0eae32dcSDimitry Andric HTTPClient Client; 150*0eae32dcSDimitry Andric Client.setTimeout(Timeout); 151*0eae32dcSDimitry Andric for (StringRef ServerUrl : DebuginfodUrls) { 152*0eae32dcSDimitry Andric SmallString<64> ArtifactUrl; 153*0eae32dcSDimitry Andric sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath); 154*0eae32dcSDimitry Andric 155*0eae32dcSDimitry Andric Expected<HTTPResponseBuffer> ResponseOrErr = Client.get(ArtifactUrl); 156*0eae32dcSDimitry Andric if (!ResponseOrErr) 157*0eae32dcSDimitry Andric return ResponseOrErr.takeError(); 158*0eae32dcSDimitry Andric 159*0eae32dcSDimitry Andric HTTPResponseBuffer &Response = *ResponseOrErr; 160*0eae32dcSDimitry Andric if (Response.Code != 200) 161*0eae32dcSDimitry Andric continue; 162*0eae32dcSDimitry Andric 163*0eae32dcSDimitry Andric // We have retrieved the artifact from this server, and now add it to the 164*0eae32dcSDimitry Andric // file cache. 165*0eae32dcSDimitry Andric Expected<std::unique_ptr<CachedFileStream>> FileStreamOrErr = 166*0eae32dcSDimitry Andric CacheAddStream(Task); 167*0eae32dcSDimitry Andric if (!FileStreamOrErr) 168*0eae32dcSDimitry Andric return FileStreamOrErr.takeError(); 169*0eae32dcSDimitry Andric std::unique_ptr<CachedFileStream> &FileStream = *FileStreamOrErr; 170*0eae32dcSDimitry Andric if (!Response.Body) 171*0eae32dcSDimitry Andric return createStringError( 172*0eae32dcSDimitry Andric errc::io_error, "Unallocated MemoryBuffer in HTTPResponseBuffer."); 173*0eae32dcSDimitry Andric 174*0eae32dcSDimitry Andric *FileStream->OS << StringRef(Response.Body->getBufferStart(), 175*0eae32dcSDimitry Andric Response.Body->getBufferSize()); 176*0eae32dcSDimitry Andric 177*0eae32dcSDimitry Andric // Return the path to the artifact on disk. 178*0eae32dcSDimitry Andric return std::string(AbsCachedArtifactPath); 179*0eae32dcSDimitry Andric } 180*0eae32dcSDimitry Andric 181*0eae32dcSDimitry Andric return createStringError(errc::argument_out_of_domain, "build id not found"); 182*0eae32dcSDimitry Andric } 183*0eae32dcSDimitry Andric } // namespace llvm 184