xref: /freebsd-src/contrib/llvm-project/llvm/lib/Debuginfod/Debuginfod.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
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