10e0f1b28SNoah Shutty //===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===//
20e0f1b28SNoah Shutty //
30e0f1b28SNoah Shutty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40e0f1b28SNoah Shutty // See https://llvm.org/LICENSE.txt for license information.
50e0f1b28SNoah Shutty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60e0f1b28SNoah Shutty //
70e0f1b28SNoah Shutty //===----------------------------------------------------------------------===//
80e0f1b28SNoah Shutty ///
90e0f1b28SNoah Shutty /// \file
100e0f1b28SNoah Shutty ///
11babef908SNoah Shutty /// This file contains several definitions for the debuginfod client and server.
12babef908SNoah Shutty /// For the client, this file defines the fetchInfo function. For the server,
13babef908SNoah Shutty /// this file defines the DebuginfodLogEntry and DebuginfodServer structs, as
14babef908SNoah Shutty /// well as the DebuginfodLog, DebuginfodCollection classes. The fetchInfo
15babef908SNoah Shutty /// function retrieves any of the three supported artifact types: (executable,
16babef908SNoah Shutty /// debuginfo, source file) associated with a build-id from debuginfod servers.
17babef908SNoah Shutty /// If a source file is to be fetched, its absolute path must be specified in
18babef908SNoah Shutty /// the Description argument to fetchInfo. The DebuginfodLogEntry,
19babef908SNoah Shutty /// DebuginfodLog, and DebuginfodCollection are used by the DebuginfodServer to
20babef908SNoah Shutty /// scan the local filesystem for binaries and serve the debuginfod protocol.
210e0f1b28SNoah Shutty ///
220e0f1b28SNoah Shutty //===----------------------------------------------------------------------===//
230e0f1b28SNoah Shutty 
240e0f1b28SNoah Shutty #include "llvm/Debuginfod/Debuginfod.h"
250e0f1b28SNoah Shutty #include "llvm/ADT/StringRef.h"
26babef908SNoah Shutty #include "llvm/BinaryFormat/Magic.h"
27babef908SNoah Shutty #include "llvm/DebugInfo/DWARF/DWARFContext.h"
28babef908SNoah Shutty #include "llvm/DebugInfo/Symbolize/Symbolize.h"
29d9941f74SNoah Shutty #include "llvm/Debuginfod/HTTPClient.h"
30babef908SNoah Shutty #include "llvm/Object/Binary.h"
31babef908SNoah Shutty #include "llvm/Object/ELFObjectFile.h"
32babef908SNoah Shutty #include "llvm/Object/ObjectFile.h"
330e0f1b28SNoah Shutty #include "llvm/Support/CachePruning.h"
340e0f1b28SNoah Shutty #include "llvm/Support/Caching.h"
3575e164f6Sserge-sans-paille #include "llvm/Support/Errc.h"
360e0f1b28SNoah Shutty #include "llvm/Support/Error.h"
370e0f1b28SNoah Shutty #include "llvm/Support/FileUtilities.h"
3875e164f6Sserge-sans-paille #include "llvm/Support/Path.h"
39babef908SNoah Shutty #include "llvm/Support/ThreadPool.h"
400e0f1b28SNoah Shutty #include "llvm/Support/xxhash.h"
410e0f1b28SNoah Shutty 
42babef908SNoah Shutty #include <atomic>
43babef908SNoah Shutty 
440e0f1b28SNoah Shutty namespace llvm {
uniqueKey(llvm::StringRef S)450e0f1b28SNoah Shutty static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); }
460e0f1b28SNoah Shutty 
470e0f1b28SNoah Shutty // Returns a binary BuildID as a normalized hex string.
480e0f1b28SNoah Shutty // Uses lowercase for compatibility with common debuginfod servers.
buildIDToString(BuildIDRef ID)490e0f1b28SNoah Shutty static std::string buildIDToString(BuildIDRef ID) {
500e0f1b28SNoah Shutty   return llvm::toHex(ID, /*LowerCase=*/true);
510e0f1b28SNoah Shutty }
520e0f1b28SNoah Shutty 
getDefaultDebuginfodUrls()530e0f1b28SNoah Shutty Expected<SmallVector<StringRef>> getDefaultDebuginfodUrls() {
540e0f1b28SNoah Shutty   const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS");
555a667c0eSKazu Hirata   if (DebuginfodUrlsEnv == nullptr)
560e0f1b28SNoah Shutty     return SmallVector<StringRef>();
570e0f1b28SNoah Shutty 
580e0f1b28SNoah Shutty   SmallVector<StringRef> DebuginfodUrls;
590e0f1b28SNoah Shutty   StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " ");
600e0f1b28SNoah Shutty   return DebuginfodUrls;
610e0f1b28SNoah Shutty }
620e0f1b28SNoah Shutty 
63babef908SNoah Shutty /// Finds a default local file caching directory for the debuginfod client,
64babef908SNoah Shutty /// first checking DEBUGINFOD_CACHE_PATH.
getDefaultDebuginfodCacheDirectory()650e0f1b28SNoah Shutty Expected<std::string> getDefaultDebuginfodCacheDirectory() {
660e0f1b28SNoah Shutty   if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH"))
670e0f1b28SNoah Shutty     return CacheDirectoryEnv;
680e0f1b28SNoah Shutty 
690e0f1b28SNoah Shutty   SmallString<64> CacheDirectory;
700e0f1b28SNoah Shutty   if (!sys::path::cache_directory(CacheDirectory))
710e0f1b28SNoah Shutty     return createStringError(
720e0f1b28SNoah Shutty         errc::io_error, "Unable to determine appropriate cache directory.");
73fd0782a3SPetr Hosek   sys::path::append(CacheDirectory, "llvm-debuginfod", "client");
740e0f1b28SNoah Shutty   return std::string(CacheDirectory);
750e0f1b28SNoah Shutty }
760e0f1b28SNoah Shutty 
getDefaultDebuginfodTimeout()770e0f1b28SNoah Shutty std::chrono::milliseconds getDefaultDebuginfodTimeout() {
780e0f1b28SNoah Shutty   long Timeout;
790e0f1b28SNoah Shutty   const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT");
800e0f1b28SNoah Shutty   if (DebuginfodTimeoutEnv &&
810e0f1b28SNoah Shutty       to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10))
820e0f1b28SNoah Shutty     return std::chrono::milliseconds(Timeout * 1000);
830e0f1b28SNoah Shutty 
840e0f1b28SNoah Shutty   return std::chrono::milliseconds(90 * 1000);
850e0f1b28SNoah Shutty }
860e0f1b28SNoah Shutty 
870e0f1b28SNoah Shutty /// The following functions fetch a debuginfod artifact to a file in a local
880e0f1b28SNoah Shutty /// cache and return the cached file path. They first search the local cache,
890e0f1b28SNoah Shutty /// followed by the debuginfod servers.
900e0f1b28SNoah Shutty 
getCachedOrDownloadSource(BuildIDRef ID,StringRef SourceFilePath)910e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID,
920e0f1b28SNoah Shutty                                                 StringRef SourceFilePath) {
930e0f1b28SNoah Shutty   SmallString<64> UrlPath;
940e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
950e0f1b28SNoah Shutty                     buildIDToString(ID), "source",
960e0f1b28SNoah Shutty                     sys::path::convert_to_slash(SourceFilePath));
970e0f1b28SNoah Shutty   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
980e0f1b28SNoah Shutty }
990e0f1b28SNoah Shutty 
getCachedOrDownloadExecutable(BuildIDRef ID)1000e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) {
1010e0f1b28SNoah Shutty   SmallString<64> UrlPath;
1020e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
1030e0f1b28SNoah Shutty                     buildIDToString(ID), "executable");
1040e0f1b28SNoah Shutty   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
1050e0f1b28SNoah Shutty }
1060e0f1b28SNoah Shutty 
getCachedOrDownloadDebuginfo(BuildIDRef ID)1070e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) {
1080e0f1b28SNoah Shutty   SmallString<64> UrlPath;
1090e0f1b28SNoah Shutty   sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
1100e0f1b28SNoah Shutty                     buildIDToString(ID), "debuginfo");
1110e0f1b28SNoah Shutty   return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
1120e0f1b28SNoah Shutty }
1130e0f1b28SNoah Shutty 
1140e0f1b28SNoah Shutty // General fetching function.
getCachedOrDownloadArtifact(StringRef UniqueKey,StringRef UrlPath)1150e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
1160e0f1b28SNoah Shutty                                                   StringRef UrlPath) {
1170e0f1b28SNoah Shutty   SmallString<10> CacheDir;
1180e0f1b28SNoah Shutty 
1190e0f1b28SNoah Shutty   Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory();
1200e0f1b28SNoah Shutty   if (!CacheDirOrErr)
1210e0f1b28SNoah Shutty     return CacheDirOrErr.takeError();
1220e0f1b28SNoah Shutty   CacheDir = *CacheDirOrErr;
1230e0f1b28SNoah Shutty 
1240e0f1b28SNoah Shutty   Expected<SmallVector<StringRef>> DebuginfodUrlsOrErr =
1250e0f1b28SNoah Shutty       getDefaultDebuginfodUrls();
1260e0f1b28SNoah Shutty   if (!DebuginfodUrlsOrErr)
1270e0f1b28SNoah Shutty     return DebuginfodUrlsOrErr.takeError();
1280e0f1b28SNoah Shutty   SmallVector<StringRef> &DebuginfodUrls = *DebuginfodUrlsOrErr;
1290e0f1b28SNoah Shutty   return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir,
1300e0f1b28SNoah Shutty                                      DebuginfodUrls,
1310e0f1b28SNoah Shutty                                      getDefaultDebuginfodTimeout());
1320e0f1b28SNoah Shutty }
1330e0f1b28SNoah Shutty 
1347917b3c6SDaniel Thornburgh namespace {
1357917b3c6SDaniel Thornburgh 
1367917b3c6SDaniel Thornburgh /// A simple handler which streams the returned data to a cache file. The cache
1377917b3c6SDaniel Thornburgh /// file is only created if a 200 OK status is observed.
1387917b3c6SDaniel Thornburgh class StreamedHTTPResponseHandler : public HTTPResponseHandler {
1397917b3c6SDaniel Thornburgh   using CreateStreamFn =
1407917b3c6SDaniel Thornburgh       std::function<Expected<std::unique_ptr<CachedFileStream>>()>;
1417917b3c6SDaniel Thornburgh   CreateStreamFn CreateStream;
1427917b3c6SDaniel Thornburgh   HTTPClient &Client;
1437917b3c6SDaniel Thornburgh   std::unique_ptr<CachedFileStream> FileStream;
1447917b3c6SDaniel Thornburgh 
1457917b3c6SDaniel Thornburgh public:
StreamedHTTPResponseHandler(CreateStreamFn CreateStream,HTTPClient & Client)1467917b3c6SDaniel Thornburgh   StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
1477917b3c6SDaniel Thornburgh       : CreateStream(CreateStream), Client(Client) {}
148ace2a6c1SDaniel Thornburgh   virtual ~StreamedHTTPResponseHandler() = default;
1497917b3c6SDaniel Thornburgh 
1507917b3c6SDaniel Thornburgh   Error handleBodyChunk(StringRef BodyChunk) override;
1517917b3c6SDaniel Thornburgh };
1527917b3c6SDaniel Thornburgh 
1537917b3c6SDaniel Thornburgh } // namespace
1547917b3c6SDaniel Thornburgh 
handleBodyChunk(StringRef BodyChunk)1557917b3c6SDaniel Thornburgh Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
1567917b3c6SDaniel Thornburgh   if (!FileStream) {
1577917b3c6SDaniel Thornburgh     if (Client.responseCode() != 200)
1587917b3c6SDaniel Thornburgh       return Error::success();
1597917b3c6SDaniel Thornburgh     Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError =
1607917b3c6SDaniel Thornburgh         CreateStream();
1617917b3c6SDaniel Thornburgh     if (!FileStreamOrError)
1627917b3c6SDaniel Thornburgh       return FileStreamOrError.takeError();
1637917b3c6SDaniel Thornburgh     FileStream = std::move(*FileStreamOrError);
1647917b3c6SDaniel Thornburgh   }
1657917b3c6SDaniel Thornburgh   *FileStream->OS << BodyChunk;
1667917b3c6SDaniel Thornburgh   return Error::success();
1677917b3c6SDaniel Thornburgh }
1687917b3c6SDaniel Thornburgh 
getCachedOrDownloadArtifact(StringRef UniqueKey,StringRef UrlPath,StringRef CacheDirectoryPath,ArrayRef<StringRef> DebuginfodUrls,std::chrono::milliseconds Timeout)1690e0f1b28SNoah Shutty Expected<std::string> getCachedOrDownloadArtifact(
1700e0f1b28SNoah Shutty     StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
1710e0f1b28SNoah Shutty     ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) {
1720e0f1b28SNoah Shutty   SmallString<64> AbsCachedArtifactPath;
1730e0f1b28SNoah Shutty   sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath,
1740e0f1b28SNoah Shutty                     "llvmcache-" + UniqueKey);
1750e0f1b28SNoah Shutty 
1760e0f1b28SNoah Shutty   Expected<FileCache> CacheOrErr =
1770e0f1b28SNoah Shutty       localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath);
1780e0f1b28SNoah Shutty   if (!CacheOrErr)
1790e0f1b28SNoah Shutty     return CacheOrErr.takeError();
1800e0f1b28SNoah Shutty 
1810e0f1b28SNoah Shutty   FileCache Cache = *CacheOrErr;
1820e0f1b28SNoah Shutty   // We choose an arbitrary Task parameter as we do not make use of it.
1830e0f1b28SNoah Shutty   unsigned Task = 0;
1840e0f1b28SNoah Shutty   Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey);
1850e0f1b28SNoah Shutty   if (!CacheAddStreamOrErr)
1860e0f1b28SNoah Shutty     return CacheAddStreamOrErr.takeError();
1870e0f1b28SNoah Shutty   AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
1880e0f1b28SNoah Shutty   if (!CacheAddStream)
1890e0f1b28SNoah Shutty     return std::string(AbsCachedArtifactPath);
1900e0f1b28SNoah Shutty   // The artifact was not found in the local cache, query the debuginfod
1910e0f1b28SNoah Shutty   // servers.
1920e0f1b28SNoah Shutty   if (!HTTPClient::isAvailable())
1930e0f1b28SNoah Shutty     return createStringError(errc::io_error,
1940e0f1b28SNoah Shutty                              "No working HTTP client is available.");
1950e0f1b28SNoah Shutty 
19634491ca7SNoah Shutty   if (!HTTPClient::IsInitialized)
19734491ca7SNoah Shutty     return createStringError(
19834491ca7SNoah Shutty         errc::io_error,
19934491ca7SNoah Shutty         "A working HTTP client is available, but it is not initialized. To "
20034491ca7SNoah Shutty         "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() "
20134491ca7SNoah Shutty         "at the beginning of main.");
20234491ca7SNoah Shutty 
2030e0f1b28SNoah Shutty   HTTPClient Client;
2040e0f1b28SNoah Shutty   Client.setTimeout(Timeout);
2050e0f1b28SNoah Shutty   for (StringRef ServerUrl : DebuginfodUrls) {
2060e0f1b28SNoah Shutty     SmallString<64> ArtifactUrl;
2070e0f1b28SNoah Shutty     sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
2080e0f1b28SNoah Shutty 
2097917b3c6SDaniel Thornburgh     // Perform the HTTP request and if successful, write the response body to
2107917b3c6SDaniel Thornburgh     // the cache.
2117917b3c6SDaniel Thornburgh     StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); },
2127917b3c6SDaniel Thornburgh                                         Client);
2137917b3c6SDaniel Thornburgh     HTTPRequest Request(ArtifactUrl);
2147917b3c6SDaniel Thornburgh     Error Err = Client.perform(Request, Handler);
2157917b3c6SDaniel Thornburgh     if (Err)
216ace2a6c1SDaniel Thornburgh       return std::move(Err);
2170e0f1b28SNoah Shutty 
2187917b3c6SDaniel Thornburgh     if (Client.responseCode() != 200)
2190e0f1b28SNoah Shutty       continue;
2200e0f1b28SNoah Shutty 
2210e0f1b28SNoah Shutty     // Return the path to the artifact on disk.
2220e0f1b28SNoah Shutty     return std::string(AbsCachedArtifactPath);
2230e0f1b28SNoah Shutty   }
2240e0f1b28SNoah Shutty 
2250e0f1b28SNoah Shutty   return createStringError(errc::argument_out_of_domain, "build id not found");
2260e0f1b28SNoah Shutty }
227babef908SNoah Shutty 
DebuginfodLogEntry(const Twine & Message)228babef908SNoah Shutty DebuginfodLogEntry::DebuginfodLogEntry(const Twine &Message)
229babef908SNoah Shutty     : Message(Message.str()) {}
230babef908SNoah Shutty 
push(const Twine & Message)231babef908SNoah Shutty void DebuginfodLog::push(const Twine &Message) {
232babef908SNoah Shutty   push(DebuginfodLogEntry(Message));
233babef908SNoah Shutty }
234babef908SNoah Shutty 
push(DebuginfodLogEntry Entry)235babef908SNoah Shutty void DebuginfodLog::push(DebuginfodLogEntry Entry) {
236babef908SNoah Shutty   {
237babef908SNoah Shutty     std::lock_guard<std::mutex> Guard(QueueMutex);
238babef908SNoah Shutty     LogEntryQueue.push(Entry);
239babef908SNoah Shutty   }
240babef908SNoah Shutty   QueueCondition.notify_one();
241babef908SNoah Shutty }
242babef908SNoah Shutty 
pop()243babef908SNoah Shutty DebuginfodLogEntry DebuginfodLog::pop() {
244babef908SNoah Shutty   {
245babef908SNoah Shutty     std::unique_lock<std::mutex> Guard(QueueMutex);
246babef908SNoah Shutty     // Wait for messages to be pushed into the queue.
247babef908SNoah Shutty     QueueCondition.wait(Guard, [&] { return !LogEntryQueue.empty(); });
248babef908SNoah Shutty   }
249babef908SNoah Shutty   std::lock_guard<std::mutex> Guard(QueueMutex);
250babef908SNoah Shutty   if (!LogEntryQueue.size())
251babef908SNoah Shutty     llvm_unreachable("Expected message in the queue.");
252babef908SNoah Shutty 
253babef908SNoah Shutty   DebuginfodLogEntry Entry = LogEntryQueue.front();
254babef908SNoah Shutty   LogEntryQueue.pop();
255babef908SNoah Shutty   return Entry;
256babef908SNoah Shutty }
257babef908SNoah Shutty 
DebuginfodCollection(ArrayRef<StringRef> PathsRef,DebuginfodLog & Log,ThreadPool & Pool,double MinInterval)258babef908SNoah Shutty DebuginfodCollection::DebuginfodCollection(ArrayRef<StringRef> PathsRef,
259babef908SNoah Shutty                                            DebuginfodLog &Log, ThreadPool &Pool,
260babef908SNoah Shutty                                            double MinInterval)
261babef908SNoah Shutty     : Log(Log), Pool(Pool), MinInterval(MinInterval) {
262babef908SNoah Shutty   for (StringRef Path : PathsRef)
263babef908SNoah Shutty     Paths.push_back(Path.str());
264babef908SNoah Shutty }
265babef908SNoah Shutty 
update()266babef908SNoah Shutty Error DebuginfodCollection::update() {
267babef908SNoah Shutty   std::lock_guard<sys::Mutex> Guard(UpdateMutex);
268babef908SNoah Shutty   if (UpdateTimer.isRunning())
269babef908SNoah Shutty     UpdateTimer.stopTimer();
270babef908SNoah Shutty   UpdateTimer.clear();
271babef908SNoah Shutty   for (const std::string &Path : Paths) {
272babef908SNoah Shutty     Log.push("Updating binaries at path " + Path);
273babef908SNoah Shutty     if (Error Err = findBinaries(Path))
274babef908SNoah Shutty       return Err;
275babef908SNoah Shutty   }
276babef908SNoah Shutty   Log.push("Updated collection");
277babef908SNoah Shutty   UpdateTimer.startTimer();
278babef908SNoah Shutty   return Error::success();
279babef908SNoah Shutty }
280babef908SNoah Shutty 
updateIfStale()281babef908SNoah Shutty Expected<bool> DebuginfodCollection::updateIfStale() {
282babef908SNoah Shutty   if (!UpdateTimer.isRunning())
283babef908SNoah Shutty     return false;
284babef908SNoah Shutty   UpdateTimer.stopTimer();
285babef908SNoah Shutty   double Time = UpdateTimer.getTotalTime().getWallTime();
286babef908SNoah Shutty   UpdateTimer.startTimer();
287babef908SNoah Shutty   if (Time < MinInterval)
288babef908SNoah Shutty     return false;
289babef908SNoah Shutty   if (Error Err = update())
290babef908SNoah Shutty     return std::move(Err);
291babef908SNoah Shutty   return true;
292babef908SNoah Shutty }
293babef908SNoah Shutty 
updateForever(std::chrono::milliseconds Interval)294babef908SNoah Shutty Error DebuginfodCollection::updateForever(std::chrono::milliseconds Interval) {
295babef908SNoah Shutty   while (true) {
296babef908SNoah Shutty     if (Error Err = update())
297babef908SNoah Shutty       return Err;
298babef908SNoah Shutty     std::this_thread::sleep_for(Interval);
299babef908SNoah Shutty   }
300babef908SNoah Shutty   llvm_unreachable("updateForever loop should never end");
301babef908SNoah Shutty }
302babef908SNoah Shutty 
isDebugBinary(object::ObjectFile * Object)303babef908SNoah Shutty static bool isDebugBinary(object::ObjectFile *Object) {
304babef908SNoah Shutty   // TODO: handle PDB debuginfo
305babef908SNoah Shutty   std::unique_ptr<DWARFContext> Context = DWARFContext::create(
306babef908SNoah Shutty       *Object, DWARFContext::ProcessDebugRelocations::Process);
307babef908SNoah Shutty   const DWARFObject &DObj = Context->getDWARFObj();
308babef908SNoah Shutty   unsigned NumSections = 0;
309babef908SNoah Shutty   DObj.forEachInfoSections([&](const DWARFSection &S) { NumSections++; });
310babef908SNoah Shutty   return NumSections;
311babef908SNoah Shutty }
312babef908SNoah Shutty 
hasELFMagic(StringRef FilePath)313babef908SNoah Shutty static bool hasELFMagic(StringRef FilePath) {
314babef908SNoah Shutty   file_magic Type;
315babef908SNoah Shutty   std::error_code EC = identify_magic(FilePath, Type);
316babef908SNoah Shutty   if (EC)
317babef908SNoah Shutty     return false;
318babef908SNoah Shutty   switch (Type) {
319babef908SNoah Shutty   case file_magic::elf:
320babef908SNoah Shutty   case file_magic::elf_relocatable:
321babef908SNoah Shutty   case file_magic::elf_executable:
322babef908SNoah Shutty   case file_magic::elf_shared_object:
323babef908SNoah Shutty   case file_magic::elf_core:
324babef908SNoah Shutty     return true;
325babef908SNoah Shutty   default:
326babef908SNoah Shutty     return false;
327babef908SNoah Shutty   }
328babef908SNoah Shutty }
329babef908SNoah Shutty 
findBinaries(StringRef Path)330babef908SNoah Shutty Error DebuginfodCollection::findBinaries(StringRef Path) {
331babef908SNoah Shutty   std::error_code EC;
332babef908SNoah Shutty   sys::fs::recursive_directory_iterator I(Twine(Path), EC), E;
333babef908SNoah Shutty   std::mutex IteratorMutex;
334babef908SNoah Shutty   ThreadPoolTaskGroup IteratorGroup(Pool);
335babef908SNoah Shutty   for (unsigned WorkerIndex = 0; WorkerIndex < Pool.getThreadCount();
336babef908SNoah Shutty        WorkerIndex++) {
337babef908SNoah Shutty     IteratorGroup.async([&, this]() -> void {
338babef908SNoah Shutty       std::string FilePath;
339babef908SNoah Shutty       while (true) {
340babef908SNoah Shutty         {
341babef908SNoah Shutty           // Check if iteration is over or there is an error during iteration
342babef908SNoah Shutty           std::lock_guard<std::mutex> Guard(IteratorMutex);
343babef908SNoah Shutty           if (I == E || EC)
344babef908SNoah Shutty             return;
345babef908SNoah Shutty           // Grab a file path from the directory iterator and advance the
346babef908SNoah Shutty           // iterator.
347babef908SNoah Shutty           FilePath = I->path();
348babef908SNoah Shutty           I.increment(EC);
349babef908SNoah Shutty         }
350babef908SNoah Shutty 
351babef908SNoah Shutty         // Inspect the file at this path to determine if it is debuginfo.
352babef908SNoah Shutty         if (!hasELFMagic(FilePath))
353babef908SNoah Shutty           continue;
354babef908SNoah Shutty 
355babef908SNoah Shutty         Expected<object::OwningBinary<object::Binary>> BinOrErr =
356babef908SNoah Shutty             object::createBinary(FilePath);
357babef908SNoah Shutty 
358babef908SNoah Shutty         if (!BinOrErr) {
359babef908SNoah Shutty           consumeError(BinOrErr.takeError());
360babef908SNoah Shutty           continue;
361babef908SNoah Shutty         }
362babef908SNoah Shutty         object::Binary *Bin = std::move(BinOrErr.get().getBinary());
363babef908SNoah Shutty         if (!Bin->isObject())
364babef908SNoah Shutty           continue;
365babef908SNoah Shutty 
366babef908SNoah Shutty         // TODO: Support non-ELF binaries
367babef908SNoah Shutty         object::ELFObjectFileBase *Object =
368babef908SNoah Shutty             dyn_cast<object::ELFObjectFileBase>(Bin);
369babef908SNoah Shutty         if (!Object)
370babef908SNoah Shutty           continue;
371babef908SNoah Shutty 
372babef908SNoah Shutty         Optional<BuildIDRef> ID = symbolize::getBuildID(Object);
373babef908SNoah Shutty         if (!ID)
374babef908SNoah Shutty           continue;
375babef908SNoah Shutty 
376*611ffcf4SKazu Hirata         std::string IDString = buildIDToString(ID.value());
377babef908SNoah Shutty         if (isDebugBinary(Object)) {
378babef908SNoah Shutty           std::lock_guard<sys::RWMutex> DebugBinariesGuard(DebugBinariesMutex);
379babef908SNoah Shutty           DebugBinaries[IDString] = FilePath;
380babef908SNoah Shutty         } else {
381babef908SNoah Shutty           std::lock_guard<sys::RWMutex> BinariesGuard(BinariesMutex);
382babef908SNoah Shutty           Binaries[IDString] = FilePath;
383babef908SNoah Shutty         }
384babef908SNoah Shutty       }
385babef908SNoah Shutty     });
386babef908SNoah Shutty   }
387babef908SNoah Shutty   IteratorGroup.wait();
388babef908SNoah Shutty   std::unique_lock<std::mutex> Guard(IteratorMutex);
389babef908SNoah Shutty   if (EC)
390babef908SNoah Shutty     return errorCodeToError(EC);
391babef908SNoah Shutty   return Error::success();
392babef908SNoah Shutty }
393babef908SNoah Shutty 
394babef908SNoah Shutty Expected<Optional<std::string>>
getBinaryPath(BuildIDRef ID)395babef908SNoah Shutty DebuginfodCollection::getBinaryPath(BuildIDRef ID) {
396babef908SNoah Shutty   Log.push("getting binary path of ID " + buildIDToString(ID));
397babef908SNoah Shutty   std::shared_lock<sys::RWMutex> Guard(BinariesMutex);
398babef908SNoah Shutty   auto Loc = Binaries.find(buildIDToString(ID));
399babef908SNoah Shutty   if (Loc != Binaries.end()) {
400babef908SNoah Shutty     std::string Path = Loc->getValue();
401babef908SNoah Shutty     return Path;
402babef908SNoah Shutty   }
403babef908SNoah Shutty   return None;
404babef908SNoah Shutty }
405babef908SNoah Shutty 
406babef908SNoah Shutty Expected<Optional<std::string>>
getDebugBinaryPath(BuildIDRef ID)407babef908SNoah Shutty DebuginfodCollection::getDebugBinaryPath(BuildIDRef ID) {
408babef908SNoah Shutty   Log.push("getting debug binary path of ID " + buildIDToString(ID));
409babef908SNoah Shutty   std::shared_lock<sys::RWMutex> Guard(DebugBinariesMutex);
410babef908SNoah Shutty   auto Loc = DebugBinaries.find(buildIDToString(ID));
411babef908SNoah Shutty   if (Loc != DebugBinaries.end()) {
412babef908SNoah Shutty     std::string Path = Loc->getValue();
413babef908SNoah Shutty     return Path;
414babef908SNoah Shutty   }
415babef908SNoah Shutty   return None;
416babef908SNoah Shutty }
417babef908SNoah Shutty 
findBinaryPath(BuildIDRef ID)418babef908SNoah Shutty Expected<std::string> DebuginfodCollection::findBinaryPath(BuildIDRef ID) {
419babef908SNoah Shutty   {
420babef908SNoah Shutty     // Check collection; perform on-demand update if stale.
421babef908SNoah Shutty     Expected<Optional<std::string>> PathOrErr = getBinaryPath(ID);
422babef908SNoah Shutty     if (!PathOrErr)
423babef908SNoah Shutty       return PathOrErr.takeError();
424babef908SNoah Shutty     Optional<std::string> Path = *PathOrErr;
425babef908SNoah Shutty     if (!Path) {
426babef908SNoah Shutty       Expected<bool> UpdatedOrErr = updateIfStale();
427babef908SNoah Shutty       if (!UpdatedOrErr)
428babef908SNoah Shutty         return UpdatedOrErr.takeError();
429babef908SNoah Shutty       if (*UpdatedOrErr) {
430babef908SNoah Shutty         // Try once more.
431babef908SNoah Shutty         PathOrErr = getBinaryPath(ID);
432babef908SNoah Shutty         if (!PathOrErr)
433babef908SNoah Shutty           return PathOrErr.takeError();
434babef908SNoah Shutty         Path = *PathOrErr;
435babef908SNoah Shutty       }
436babef908SNoah Shutty     }
437babef908SNoah Shutty     if (Path)
438*611ffcf4SKazu Hirata       return Path.value();
439babef908SNoah Shutty   }
440babef908SNoah Shutty 
441babef908SNoah Shutty   // Try federation.
442babef908SNoah Shutty   Expected<std::string> PathOrErr = getCachedOrDownloadExecutable(ID);
443babef908SNoah Shutty   if (!PathOrErr)
444babef908SNoah Shutty     consumeError(PathOrErr.takeError());
445babef908SNoah Shutty 
446babef908SNoah Shutty   // Fall back to debug binary.
447babef908SNoah Shutty   return findDebugBinaryPath(ID);
448babef908SNoah Shutty }
449babef908SNoah Shutty 
findDebugBinaryPath(BuildIDRef ID)450babef908SNoah Shutty Expected<std::string> DebuginfodCollection::findDebugBinaryPath(BuildIDRef ID) {
451babef908SNoah Shutty   // Check collection; perform on-demand update if stale.
452babef908SNoah Shutty   Expected<Optional<std::string>> PathOrErr = getDebugBinaryPath(ID);
453babef908SNoah Shutty   if (!PathOrErr)
454babef908SNoah Shutty     return PathOrErr.takeError();
455babef908SNoah Shutty   Optional<std::string> Path = *PathOrErr;
456babef908SNoah Shutty   if (!Path) {
457babef908SNoah Shutty     Expected<bool> UpdatedOrErr = updateIfStale();
458babef908SNoah Shutty     if (!UpdatedOrErr)
459babef908SNoah Shutty       return UpdatedOrErr.takeError();
460babef908SNoah Shutty     if (*UpdatedOrErr) {
461babef908SNoah Shutty       // Try once more.
462babef908SNoah Shutty       PathOrErr = getBinaryPath(ID);
463babef908SNoah Shutty       if (!PathOrErr)
464babef908SNoah Shutty         return PathOrErr.takeError();
465babef908SNoah Shutty       Path = *PathOrErr;
466babef908SNoah Shutty     }
467babef908SNoah Shutty   }
468babef908SNoah Shutty   if (Path)
469*611ffcf4SKazu Hirata     return Path.value();
470babef908SNoah Shutty 
471babef908SNoah Shutty   // Try federation.
472babef908SNoah Shutty   return getCachedOrDownloadDebuginfo(ID);
473babef908SNoah Shutty }
474babef908SNoah Shutty 
DebuginfodServer(DebuginfodLog & Log,DebuginfodCollection & Collection)475babef908SNoah Shutty DebuginfodServer::DebuginfodServer(DebuginfodLog &Log,
476babef908SNoah Shutty                                    DebuginfodCollection &Collection)
477babef908SNoah Shutty     : Log(Log), Collection(Collection) {
478babef908SNoah Shutty   cantFail(
479babef908SNoah Shutty       Server.get(R"(/buildid/(.*)/debuginfo)", [&](HTTPServerRequest Request) {
480babef908SNoah Shutty         Log.push("GET " + Request.UrlPath);
481babef908SNoah Shutty         std::string IDString;
482babef908SNoah Shutty         if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
483babef908SNoah Shutty           Request.setResponse(
484babef908SNoah Shutty               {404, "text/plain", "Build ID is not a hex string\n"});
485babef908SNoah Shutty           return;
486babef908SNoah Shutty         }
487babef908SNoah Shutty         BuildID ID(IDString.begin(), IDString.end());
488babef908SNoah Shutty         Expected<std::string> PathOrErr = Collection.findDebugBinaryPath(ID);
489babef908SNoah Shutty         if (Error Err = PathOrErr.takeError()) {
490babef908SNoah Shutty           consumeError(std::move(Err));
491babef908SNoah Shutty           Request.setResponse({404, "text/plain", "Build ID not found\n"});
492babef908SNoah Shutty           return;
493babef908SNoah Shutty         }
494babef908SNoah Shutty         streamFile(Request, *PathOrErr);
495babef908SNoah Shutty       }));
496babef908SNoah Shutty   cantFail(
497babef908SNoah Shutty       Server.get(R"(/buildid/(.*)/executable)", [&](HTTPServerRequest Request) {
498babef908SNoah Shutty         Log.push("GET " + Request.UrlPath);
499babef908SNoah Shutty         std::string IDString;
500babef908SNoah Shutty         if (!tryGetFromHex(Request.UrlPathMatches[0], IDString)) {
501babef908SNoah Shutty           Request.setResponse(
502babef908SNoah Shutty               {404, "text/plain", "Build ID is not a hex string\n"});
503babef908SNoah Shutty           return;
504babef908SNoah Shutty         }
505babef908SNoah Shutty         BuildID ID(IDString.begin(), IDString.end());
506babef908SNoah Shutty         Expected<std::string> PathOrErr = Collection.findBinaryPath(ID);
507babef908SNoah Shutty         if (Error Err = PathOrErr.takeError()) {
508babef908SNoah Shutty           consumeError(std::move(Err));
509babef908SNoah Shutty           Request.setResponse({404, "text/plain", "Build ID not found\n"});
510babef908SNoah Shutty           return;
511babef908SNoah Shutty         }
512babef908SNoah Shutty         streamFile(Request, *PathOrErr);
513babef908SNoah Shutty       }));
514babef908SNoah Shutty }
515babef908SNoah Shutty 
5160e0f1b28SNoah Shutty } // namespace llvm
517