Debuginfod.cpp 7.1 KB

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