2021-12-07 05:46:22 +08:00
|
|
|
//===-- 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"
|
2021-12-07 02:28:07 +08:00
|
|
|
#include "llvm/Debuginfod/HTTPClient.h"
|
2021-12-07 05:46:22 +08:00
|
|
|
#include "llvm/Support/CachePruning.h"
|
|
|
|
#include "llvm/Support/Caching.h"
|
|
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#include "llvm/Support/FileUtilities.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");
|
2021-12-29 00:52:25 +08:00
|
|
|
if (DebuginfodUrlsEnv == nullptr)
|
2021-12-07 05:46:22 +08:00
|
|
|
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.");
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
2021-12-14 06:35:27 +08:00
|
|
|
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.");
|
|
|
|
|
2021-12-07 05:46:22 +08:00
|
|
|
HTTPClient Client;
|
|
|
|
Client.setTimeout(Timeout);
|
|
|
|
for (StringRef ServerUrl : DebuginfodUrls) {
|
|
|
|
SmallString<64> ArtifactUrl;
|
|
|
|
sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath);
|
|
|
|
|
|
|
|
Expected<HTTPResponseBuffer> ResponseOrErr = Client.get(ArtifactUrl);
|
|
|
|
if (!ResponseOrErr)
|
|
|
|
return ResponseOrErr.takeError();
|
|
|
|
|
|
|
|
HTTPResponseBuffer &Response = *ResponseOrErr;
|
|
|
|
if (Response.Code != 200)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// We have retrieved the artifact from this server, and now add it to the
|
|
|
|
// file cache.
|
|
|
|
Expected<std::unique_ptr<CachedFileStream>> FileStreamOrErr =
|
|
|
|
CacheAddStream(Task);
|
2021-12-14 06:35:27 +08:00
|
|
|
if (!FileStreamOrErr)
|
2021-12-07 05:46:22 +08:00
|
|
|
return FileStreamOrErr.takeError();
|
|
|
|
std::unique_ptr<CachedFileStream> &FileStream = *FileStreamOrErr;
|
|
|
|
if (!Response.Body)
|
|
|
|
return createStringError(
|
|
|
|
errc::io_error, "Unallocated MemoryBuffer in HTTPResponseBuffer.");
|
|
|
|
|
|
|
|
*FileStream->OS << StringRef(Response.Body->getBufferStart(),
|
|
|
|
Response.Body->getBufferSize());
|
|
|
|
|
|
|
|
// 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
|