Debuginfod.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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 contains several definitions for the debuginfod client and server.
  12. /// For the client, this file defines the fetchInfo function. For the server,
  13. /// this file defines the DebuginfodLogEntry and DebuginfodServer structs, as
  14. /// well as the DebuginfodLog, DebuginfodCollection classes. The fetchInfo
  15. /// function retrieves any of the three supported artifact types: (executable,
  16. /// debuginfo, source file) associated with a build-id from debuginfod servers.
  17. /// If a source file is to be fetched, its absolute path must be specified in
  18. /// the Description argument to fetchInfo. The DebuginfodLogEntry,
  19. /// DebuginfodLog, and DebuginfodCollection are used by the DebuginfodServer to
  20. /// scan the local filesystem for binaries and serve the debuginfod protocol.
  21. ///
  22. //===----------------------------------------------------------------------===//
  23. #include "llvm/Debuginfod/Debuginfod.h"
  24. #include "llvm/ADT/StringExtras.h"
  25. #include "llvm/ADT/StringRef.h"
  26. #include "llvm/BinaryFormat/Magic.h"
  27. #include "llvm/DebugInfo/DWARF/DWARFContext.h"
  28. #include "llvm/DebugInfo/Symbolize/Symbolize.h"
  29. #include "llvm/Debuginfod/HTTPClient.h"
  30. #include "llvm/Object/BuildID.h"
  31. #include "llvm/Object/ELFObjectFile.h"
  32. #include "llvm/Support/CachePruning.h"
  33. #include "llvm/Support/Caching.h"
  34. #include "llvm/Support/Errc.h"
  35. #include "llvm/Support/Error.h"
  36. #include "llvm/Support/FileUtilities.h"
  37. #include "llvm/Support/MemoryBuffer.h"
  38. #include "llvm/Support/Path.h"
  39. #include "llvm/Support/ThreadPool.h"
  40. #include "llvm/Support/xxhash.h"
  41. #include <atomic>
  42. #include <thread>
  43. namespace llvm {
  44. using llvm::object::BuildIDRef;
  45. static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); }
  46. // Returns a binary BuildID as a normalized hex string.
  47. // Uses lowercase for compatibility with common debuginfod servers.
  48. static std::string buildIDToString(BuildIDRef ID) {
  49. return llvm::toHex(ID, /*LowerCase=*/true);
  50. }
  51. bool canUseDebuginfod() {
  52. return HTTPClient::isAvailable() && !getDefaultDebuginfodUrls().empty();
  53. }
  54. SmallVector<StringRef> getDefaultDebuginfodUrls() {
  55. const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS");
  56. if (DebuginfodUrlsEnv == nullptr)
  57. return SmallVector<StringRef>();
  58. SmallVector<StringRef> DebuginfodUrls;
  59. StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " ");
  60. return DebuginfodUrls;
  61. }
  62. /// Finds a default local file caching directory for the debuginfod client,
  63. /// first checking DEBUGINFOD_CACHE_PATH.
  64. Expected<std::string> getDefaultDebuginfodCacheDirectory() {
  65. if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH"))
  66. return CacheDirectoryEnv;
  67. SmallString<64> CacheDirectory;
  68. if (!sys::path::cache_directory(CacheDirectory))
  69. return createStringError(
  70. errc::io_error, "Unable to determine appropriate cache directory.");
  71. sys::path::append(CacheDirectory, "llvm-debuginfod", "client");
  72. return std::string(CacheDirectory);
  73. }
  74. std::chrono::milliseconds getDefaultDebuginfodTimeout() {
  75. long Timeout;
  76. const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT");
  77. if (DebuginfodTimeoutEnv &&
  78. to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10))
  79. return std::chrono::milliseconds(Timeout * 1000);
  80. return std::chrono::milliseconds(90 * 1000);
  81. }
  82. /// The following functions fetch a debuginfod artifact to a file in a local
  83. /// cache and return the cached file path. They first search the local cache,
  84. /// followed by the debuginfod servers.
  85. Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID,
  86. StringRef SourceFilePath) {
  87. SmallString<64> UrlPath;
  88. sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
  89. buildIDToString(ID), "source",
  90. sys::path::convert_to_slash(SourceFilePath));
  91. return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
  92. }
  93. Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) {
  94. SmallString<64> UrlPath;
  95. sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
  96. buildIDToString(ID), "executable");
  97. return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
  98. }
  99. Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) {
  100. SmallString<64> UrlPath;
  101. sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
  102. buildIDToString(ID), "debuginfo");
  103. return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
  104. }
  105. // General fetching function.
  106. Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
  107. StringRef UrlPath) {
  108. SmallString<10> CacheDir;
  109. Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory();
  110. if (!CacheDirOrErr)
  111. return CacheDirOrErr.takeError();
  112. CacheDir = *CacheDirOrErr;
  113. return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir,
  114. getDefaultDebuginfodUrls(),
  115. getDefaultDebuginfodTimeout());
  116. }
  117. namespace {
  118. /// A simple handler which streams the returned data to a cache file. The cache
  119. /// file is only created if a 200 OK status is observed.
  120. class StreamedHTTPResponseHandler : public HTTPResponseHandler {
  121. using CreateStreamFn =
  122. std::function<Expected<std::unique_ptr<CachedFileStream>>()>;
  123. CreateStreamFn CreateStream;
  124. HTTPClient &Client;
  125. std::unique_ptr<CachedFileStream> FileStream;
  126. public:
  127. StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
  128. : CreateStream(CreateStream), Client(Client) {}
  129. virtual ~StreamedHTTPResponseHandler() = default;
  130. Error handleBodyChunk(StringRef BodyChunk) override;
  131. };
  132. } // namespace
  133. Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
  134. if (!FileStream) {
  135. unsigned Code = Client.responseCode();
  136. if (Code && Code != 200)
  137. return Error::success();
  138. Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError =
  139. CreateStream();
  140. if (!FileStreamOrError)
  141. return FileStreamOrError.takeError();
  142. FileStream = std::move(*FileStreamOrError);
  143. }
  144. *FileStream->OS << BodyChunk;
  145. return Error::success();
  146. }
  147. // An over-accepting simplification of the HTTP RFC 7230 spec.
  148. static bool isHeader(StringRef S) {
  149. StringRef Name;
  150. StringRef Value;
  151. std::tie(Name, Value) = S.split(':');
  152. if (Name.empty() || Value.empty())
  153. return false;
  154. return all_of(Name, [](char C) { return llvm::isPrint(C) && C != ' '; }) &&
  155. all_of(Value, [](char C) { return llvm::isPrint(C) || C == '\t'; });
  156. }
  157. static SmallVector<std::string, 0> getHeaders() {
  158. const char *Filename = getenv("DEBUGINFOD_HEADERS_FILE");
  159. if (!Filename)
  160. return {};
  161. ErrorOr<std::unique_ptr<MemoryBuffer>> HeadersFile =
  162. MemoryBuffer::getFile(Filename, /*IsText=*/true);
  163. if (!HeadersFile)
  164. return {};
  165. SmallVector<std::string, 0> Headers;
  166. uint64_t LineNumber = 0;
  167. for (StringRef Line : llvm::split((*HeadersFile)->getBuffer(), '\n')) {
  168. LineNumber++;
  169. if (!Line.empty() && Line.back() == '\r')
  170. Line = Line.drop_back();
  171. if (!isHeader(Line)) {
  172. if (!all_of(Line, llvm::isSpace))
  173. WithColor::warning()
  174. << "could not parse debuginfod header: " << Filename << ':'
  175. << LineNumber << '\n';
  176. continue;
  177. }
  178. Headers.emplace_back(Line);
  179. }
  180. return Headers;
  181. }
  182. Expected<std::string> getCachedOrDownloadArtifact(
  183. StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
  184. ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) {
  185. SmallString<64> AbsCachedArtifactPath;
  186. sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath,
  187. "llvmcache-" + UniqueKey);
  188. Expected<FileCache> CacheOrErr =
  189. localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath);
  190. if (!CacheOrErr)
  191. return CacheOrErr.takeError();
  192. FileCache Cache = *CacheOrErr;
  193. // We choose an arbitrary Task parameter as we do not make use of it.
  194. unsigned Task = 0;
  195. Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey, "");
  196. if (!CacheAddStreamOrErr)
  197. return CacheAddStreamOrErr.takeError();
  198. AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
  199. if (!CacheAddStream)
  200. return std::string(AbsCachedArtifactPath);
  201. // The artifact was not found in the local cache, query the debuginfod
  202. // servers.
  203. if (!HTTPClient::isAvailable())
  204. return createStringError(errc::io_error,
  205. "No working HTTP client is available.");
  206. if (!HTTPClient::IsInitialized)
  207. return createStringError(
  208. errc::io_error,
  209. "A working HTTP client is available, but it is not initialized. To "
  210. "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() "
  211. "at the beginning of main.");
  212. HTTPClient Client;
  213. Client.setTimeout(Timeout);
  214. for (StringRef ServerUrl : DebuginfodUrls) {
  215. SmallString<64> ArtifactUrl;
  216. sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
  217. // Perform the HTTP request and if successful, write the response body to
  218. // the cache.
  219. StreamedHTTPResponseHandler Handler(
  220. [&]() { return CacheAddStream(Task, ""); }, Client);
  221. HTTPRequest Request(ArtifactUrl);
  222. Request.Headers = getHeaders();
  223. Error Err = Client.perform(Request, Handler);
  224. if (Err)
  225. return std::move(Err);
  226. unsigned Code = Client.responseCode();
  227. if (Code && Code != 200)
  228. continue;
  229. // Return the path to the artifact on disk.
  230. return std::string(AbsCachedArtifactPath);
  231. }
  232. return createStringError(errc::argument_out_of_domain, "build id not found");
  233. }
  234. DebuginfodLogEntry::DebuginfodLogEntry(const Twine &Message)
  235. : Message(Message.str()) {}
  236. void DebuginfodLog::push(const Twine &Message) {
  237. push(DebuginfodLogEntry(Message));
  238. }
  239. void DebuginfodLog::push(DebuginfodLogEntry Entry) {
  240. {
  241. std::lock_guard<std::mutex> Guard(QueueMutex);
  242. LogEntryQueue.push(Entry);
  243. }
  244. QueueCondition.notify_one();
  245. }
  246. DebuginfodLogEntry DebuginfodLog::pop() {
  247. {
  248. std::unique_lock<std::mutex> Guard(QueueMutex);
  249. // Wait for messages to be pushed into the queue.
  250. QueueCondition.wait(Guard, [&] { return !LogEntryQueue.empty(); });
  251. }
  252. std::lock_guard<std::mutex> Guard(QueueMutex);
  253. if (!LogEntryQueue.size())
  254. llvm_unreachable("Expected message in the queue.");
  255. DebuginfodLogEntry Entry = LogEntryQueue.front();
  256. LogEntryQueue.pop();
  257. return Entry;
  258. }
  259. DebuginfodCollection::DebuginfodCollection(ArrayRef<StringRef> PathsRef,
  260. DebuginfodLog &Log, ThreadPool &Pool,
  261. double MinInterval)
  262. : Log(Log), Pool(Pool), MinInterval(MinInterval) {
  263. for (StringRef Path : PathsRef)
  264. Paths.push_back(Path.str());
  265. }
  266. Error DebuginfodCollection::update() {
  267. std::lock_guard<sys::Mutex> Guard(UpdateMutex);
  268. if (UpdateTimer.isRunning())
  269. UpdateTimer.stopTimer();
  270. UpdateTimer.clear();
  271. for (const std::string &Path : Paths) {
  272. Log.push("Updating binaries at path " + Path);
  273. if (Error Err = findBinaries(Path))
  274. return Err;
  275. }
  276. Log.push("Updated collection");
  277. UpdateTimer.startTimer();
  278. return Error::success();
  279. }
  280. Expected<bool> DebuginfodCollection::updateIfStale() {
  281. if (!UpdateTimer.isRunning())
  282. return false;
  283. UpdateTimer.stopTimer();
  284. double Time = UpdateTimer.getTotalTime().getWallTime();
  285. UpdateTimer.startTimer();
  286. if (Time < MinInterval)
  287. return false;
  288. if (Error Err = update())
  289. return std::move(Err);
  290. return true;
  291. }
  292. Error DebuginfodCollection::updateForever(std::chrono::milliseconds Interval) {
  293. while (true) {
  294. if (Error Err = update())
  295. return Err;
  296. std::this_thread::sleep_for(Interval);
  297. }
  298. llvm_unreachable("updateForever loop should never end");
  299. }
  300. static bool hasELFMagic(StringRef FilePath) {
  301. file_magic Type;
  302. std::error_code EC = identify_magic(FilePath, Type);
  303. if (EC)
  304. return false;
  305. switch (Type) {
  306. case file_magic::elf:
  307. case file_magic::elf_relocatable:
  308. case file_magic::elf_executable:
  309. case file_magic::elf_shared_object:
  310. case file_magic::elf_core:
  311. return true;
  312. default:
  313. return false;
  314. }
  315. }
  316. Error DebuginfodCollection::findBinaries(StringRef Path) {
  317. std::error_code EC;
  318. sys::fs::recursive_directory_iterator I(Twine(Path), EC), E;
  319. std::mutex IteratorMutex;
  320. ThreadPoolTaskGroup IteratorGroup(Pool);
  321. for (unsigned WorkerIndex = 0; WorkerIndex < Pool.getThreadCount();
  322. WorkerIndex++) {
  323. IteratorGroup.async([&, this]() -> void {
  324. std::string FilePath;
  325. while (true) {
  326. {
  327. // Check if iteration is over or there is an error during iteration
  328. std::lock_guard<std::mutex> Guard(IteratorMutex);
  329. if (I == E || EC)
  330. return;
  331. // Grab a file path from the directory iterator and advance the
  332. // iterator.
  333. FilePath = I->path();
  334. I.increment(EC);
  335. }
  336. // Inspect the file at this path to determine if it is debuginfo.
  337. if (!hasELFMagic(FilePath))
  338. continue;
  339. Expected<object::OwningBinary<object::Binary>> BinOrErr =
  340. object::createBinary(FilePath);
  341. if (!BinOrErr) {
  342. consumeError(BinOrErr.takeError());
  343. continue;
  344. }
  345. object::Binary *Bin = std::move(BinOrErr.get().getBinary());
  346. if (!Bin->isObject())
  347. continue;
  348. // TODO: Support non-ELF binaries
  349. object::ELFObjectFileBase *Object =
  350. dyn_cast<object::ELFObjectFileBase>(Bin);
  351. if (!Object)
  352. continue;
  353. std::optional<BuildIDRef> ID = getBuildID(Object);
  354. if (!ID)
  355. continue;
  356. std::string IDString = buildIDToString(*ID);
  357. if (Object->hasDebugInfo()) {
  358. std::lock_guard<sys::RWMutex> DebugBinariesGuard(DebugBinariesMutex);
  359. (void)DebugBinaries.try_emplace(IDString, std::move(FilePath));
  360. } else {
  361. std::lock_guard<sys::RWMutex> BinariesGuard(BinariesMutex);
  362. (void)Binaries.try_emplace(IDString, std::move(FilePath));
  363. }
  364. }
  365. });
  366. }
  367. IteratorGroup.wait();
  368. std::unique_lock<std::mutex> Guard(IteratorMutex);
  369. if (EC)
  370. return errorCodeToError(EC);
  371. return Error::success();
  372. }
  373. Expected<std::optional<std::string>>
  374. DebuginfodCollection::getBinaryPath(BuildIDRef ID) {
  375. Log.push("getting binary path of ID " + buildIDToString(ID));
  376. std::shared_lock<sys::RWMutex> Guard(BinariesMutex);
  377. auto Loc = Binaries.find(buildIDToString(ID));
  378. if (Loc != Binaries.end()) {
  379. std::string Path = Loc->getValue();
  380. return Path;
  381. }
  382. return std::nullopt;
  383. }
  384. Expected<std::optional<std::string>>
  385. DebuginfodCollection::getDebugBinaryPath(BuildIDRef ID) {
  386. Log.push("getting debug binary path of ID " + buildIDToString(ID));
  387. std::shared_lock<sys::RWMutex> Guard(DebugBinariesMutex);
  388. auto Loc = DebugBinaries.find(buildIDToString(ID));
  389. if (Loc != DebugBinaries.end()) {
  390. std::string Path = Loc->getValue();
  391. return Path;
  392. }
  393. return std::nullopt;
  394. }
  395. Expected<std::string> DebuginfodCollection::findBinaryPath(BuildIDRef ID) {
  396. {
  397. // Check collection; perform on-demand update if stale.
  398. Expected<std::optional<std::string>> PathOrErr = getBinaryPath(ID);
  399. if (!PathOrErr)
  400. return PathOrErr.takeError();
  401. std::optional<std::string> Path = *PathOrErr;
  402. if (!Path) {
  403. Expected<bool> UpdatedOrErr = updateIfStale();
  404. if (!UpdatedOrErr)
  405. return UpdatedOrErr.takeError();
  406. if (*UpdatedOrErr) {
  407. // Try once more.
  408. PathOrErr = getBinaryPath(ID);
  409. if (!PathOrErr)
  410. return PathOrErr.takeError();
  411. Path = *PathOrErr;
  412. }
  413. }
  414. if (Path)
  415. return *Path;
  416. }
  417. // Try federation.
  418. Expected<std::string> PathOrErr = getCachedOrDownloadExecutable(ID);
  419. if (!PathOrErr)
  420. consumeError(PathOrErr.takeError());
  421. // Fall back to debug binary.
  422. return findDebugBinaryPath(ID);
  423. }
  424. Expected<std::string> DebuginfodCollection::findDebugBinaryPath(BuildIDRef ID) {
  425. // Check collection; perform on-demand update if stale.
  426. Expected<std::optional<std::string>> PathOrErr = getDebugBinaryPath(ID);
  427. if (!PathOrErr)
  428. return PathOrErr.takeError();
  429. std::optional<std::string> Path = *PathOrErr;
  430. if (!Path) {
  431. Expected<bool> UpdatedOrErr = updateIfStale();
  432. if (!UpdatedOrErr)
  433. return UpdatedOrErr.takeError();
  434. if (*UpdatedOrErr) {
  435. // Try once more.
  436. PathOrErr = getBinaryPath(ID);
  437. if (!PathOrErr)
  438. return PathOrErr.takeError();
  439. Path = *PathOrErr;
  440. }
  441. }
  442. if (Path)
  443. return *Path;
  444. // Try federation.
  445. return getCachedOrDownloadDebuginfo(ID);
  446. }
  447. DebuginfodServer::DebuginfodServer(DebuginfodLog &Log,
  448. DebuginfodCollection &Collection)
  449. : Log(Log), Collection(Collection) {
  450. cantFail(
  451. Server.get(R"(/buildid/(.*)/debuginfo)", [&](HTTPServerRequest Request) {
  452. Log.push("GET " + Request.UrlPath);
  453. std::string IDString;
  454. if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
  455. Request.setResponse(
  456. {404, "text/plain", "Build ID is not a hex string\n"});
  457. return;
  458. }
  459. object::BuildID ID(IDString.begin(), IDString.end());
  460. Expected<std::string> PathOrErr = Collection.findDebugBinaryPath(ID);
  461. if (Error Err = PathOrErr.takeError()) {
  462. consumeError(std::move(Err));
  463. Request.setResponse({404, "text/plain", "Build ID not found\n"});
  464. return;
  465. }
  466. streamFile(Request, *PathOrErr);
  467. }));
  468. cantFail(
  469. Server.get(R"(/buildid/(.*)/executable)", [&](HTTPServerRequest Request) {
  470. Log.push("GET " + Request.UrlPath);
  471. std::string IDString;
  472. if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
  473. Request.setResponse(
  474. {404, "text/plain", "Build ID is not a hex string\n"});
  475. return;
  476. }
  477. object::BuildID ID(IDString.begin(), IDString.end());
  478. Expected<std::string> PathOrErr = Collection.findBinaryPath(ID);
  479. if (Error Err = PathOrErr.takeError()) {
  480. consumeError(std::move(Err));
  481. Request.setResponse({404, "text/plain", "Build ID not found\n"});
  482. return;
  483. }
  484. streamFile(Request, *PathOrErr);
  485. }));
  486. }
  487. } // namespace llvm