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