forked from OSchip/llvm-project
212 lines
7.8 KiB
C++
212 lines
7.8 KiB
C++
//===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
///
|
|
/// This file defines the fetchInfo function, which retrieves
|
|
/// any of the three supported artifact types: (executable, debuginfo, source
|
|
/// file) associated with a build-id from debuginfod servers. If a source file
|
|
/// is to be fetched, its absolute path must be specified in the Description
|
|
/// argument to fetchInfo.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Debuginfod/Debuginfod.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Debuginfod/HTTPClient.h"
|
|
#include "llvm/Support/CachePruning.h"
|
|
#include "llvm/Support/Caching.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileUtilities.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/xxhash.h"
|
|
|
|
namespace llvm {
|
|
static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); }
|
|
|
|
// Returns a binary BuildID as a normalized hex string.
|
|
// Uses lowercase for compatibility with common debuginfod servers.
|
|
static std::string buildIDToString(BuildIDRef ID) {
|
|
return llvm::toHex(ID, /*LowerCase=*/true);
|
|
}
|
|
|
|
Expected<SmallVector<StringRef>> getDefaultDebuginfodUrls() {
|
|
const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS");
|
|
if (DebuginfodUrlsEnv == nullptr)
|
|
return SmallVector<StringRef>();
|
|
|
|
SmallVector<StringRef> DebuginfodUrls;
|
|
StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " ");
|
|
return DebuginfodUrls;
|
|
}
|
|
|
|
Expected<std::string> getDefaultDebuginfodCacheDirectory() {
|
|
if (const char *CacheDirectoryEnv = std::getenv("DEBUGINFOD_CACHE_PATH"))
|
|
return CacheDirectoryEnv;
|
|
|
|
SmallString<64> CacheDirectory;
|
|
if (!sys::path::cache_directory(CacheDirectory))
|
|
return createStringError(
|
|
errc::io_error, "Unable to determine appropriate cache directory.");
|
|
sys::path::append(CacheDirectory, "llvm-debuginfod", "client");
|
|
return std::string(CacheDirectory);
|
|
}
|
|
|
|
std::chrono::milliseconds getDefaultDebuginfodTimeout() {
|
|
long Timeout;
|
|
const char *DebuginfodTimeoutEnv = std::getenv("DEBUGINFOD_TIMEOUT");
|
|
if (DebuginfodTimeoutEnv &&
|
|
to_integer(StringRef(DebuginfodTimeoutEnv).trim(), Timeout, 10))
|
|
return std::chrono::milliseconds(Timeout * 1000);
|
|
|
|
return std::chrono::milliseconds(90 * 1000);
|
|
}
|
|
|
|
/// The following functions fetch a debuginfod artifact to a file in a local
|
|
/// cache and return the cached file path. They first search the local cache,
|
|
/// followed by the debuginfod servers.
|
|
|
|
Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID,
|
|
StringRef SourceFilePath) {
|
|
SmallString<64> UrlPath;
|
|
sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
|
|
buildIDToString(ID), "source",
|
|
sys::path::convert_to_slash(SourceFilePath));
|
|
return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
|
|
}
|
|
|
|
Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) {
|
|
SmallString<64> UrlPath;
|
|
sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
|
|
buildIDToString(ID), "executable");
|
|
return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
|
|
}
|
|
|
|
Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) {
|
|
SmallString<64> UrlPath;
|
|
sys::path::append(UrlPath, sys::path::Style::posix, "buildid",
|
|
buildIDToString(ID), "debuginfo");
|
|
return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath);
|
|
}
|
|
|
|
// General fetching function.
|
|
Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
|
|
StringRef UrlPath) {
|
|
SmallString<10> CacheDir;
|
|
|
|
Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory();
|
|
if (!CacheDirOrErr)
|
|
return CacheDirOrErr.takeError();
|
|
CacheDir = *CacheDirOrErr;
|
|
|
|
Expected<SmallVector<StringRef>> DebuginfodUrlsOrErr =
|
|
getDefaultDebuginfodUrls();
|
|
if (!DebuginfodUrlsOrErr)
|
|
return DebuginfodUrlsOrErr.takeError();
|
|
SmallVector<StringRef> &DebuginfodUrls = *DebuginfodUrlsOrErr;
|
|
return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir,
|
|
DebuginfodUrls,
|
|
getDefaultDebuginfodTimeout());
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A simple handler which streams the returned data to a cache file. The cache
|
|
/// file is only created if a 200 OK status is observed.
|
|
class StreamedHTTPResponseHandler : public HTTPResponseHandler {
|
|
using CreateStreamFn =
|
|
std::function<Expected<std::unique_ptr<CachedFileStream>>()>;
|
|
CreateStreamFn CreateStream;
|
|
HTTPClient &Client;
|
|
std::unique_ptr<CachedFileStream> FileStream;
|
|
|
|
public:
|
|
StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
|
|
: CreateStream(CreateStream), Client(Client) {}
|
|
virtual ~StreamedHTTPResponseHandler() = default;
|
|
|
|
Error handleBodyChunk(StringRef BodyChunk) override;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
|
|
if (!FileStream) {
|
|
if (Client.responseCode() != 200)
|
|
return Error::success();
|
|
Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError =
|
|
CreateStream();
|
|
if (!FileStreamOrError)
|
|
return FileStreamOrError.takeError();
|
|
FileStream = std::move(*FileStreamOrError);
|
|
}
|
|
*FileStream->OS << BodyChunk;
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<std::string> getCachedOrDownloadArtifact(
|
|
StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
|
|
ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) {
|
|
SmallString<64> AbsCachedArtifactPath;
|
|
sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath,
|
|
"llvmcache-" + UniqueKey);
|
|
|
|
Expected<FileCache> CacheOrErr =
|
|
localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath);
|
|
if (!CacheOrErr)
|
|
return CacheOrErr.takeError();
|
|
|
|
FileCache Cache = *CacheOrErr;
|
|
// We choose an arbitrary Task parameter as we do not make use of it.
|
|
unsigned Task = 0;
|
|
Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey);
|
|
if (!CacheAddStreamOrErr)
|
|
return CacheAddStreamOrErr.takeError();
|
|
AddStreamFn &CacheAddStream = *CacheAddStreamOrErr;
|
|
if (!CacheAddStream)
|
|
return std::string(AbsCachedArtifactPath);
|
|
// The artifact was not found in the local cache, query the debuginfod
|
|
// servers.
|
|
if (!HTTPClient::isAvailable())
|
|
return createStringError(errc::io_error,
|
|
"No working HTTP client is available.");
|
|
|
|
if (!HTTPClient::IsInitialized)
|
|
return createStringError(
|
|
errc::io_error,
|
|
"A working HTTP client is available, but it is not initialized. To "
|
|
"allow Debuginfod to make HTTP requests, call HTTPClient::initialize() "
|
|
"at the beginning of main.");
|
|
|
|
HTTPClient Client;
|
|
Client.setTimeout(Timeout);
|
|
for (StringRef ServerUrl : DebuginfodUrls) {
|
|
SmallString<64> ArtifactUrl;
|
|
sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
|
|
|
|
// Perform the HTTP request and if successful, write the response body to
|
|
// the cache.
|
|
StreamedHTTPResponseHandler Handler([&]() { return CacheAddStream(Task); },
|
|
Client);
|
|
HTTPRequest Request(ArtifactUrl);
|
|
Error Err = Client.perform(Request, Handler);
|
|
if (Err)
|
|
return std::move(Err);
|
|
|
|
if (Client.responseCode() != 200)
|
|
continue;
|
|
|
|
// Return the path to the artifact on disk.
|
|
return std::string(AbsCachedArtifactPath);
|
|
}
|
|
|
|
return createStringError(errc::argument_out_of_domain, "build id not found");
|
|
}
|
|
} // namespace llvm
|