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