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