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