212 lines
7.4 KiB
C++
212 lines
7.4 KiB
C++
//===-- SymbolLocatorDebuginfod.cpp ---------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SymbolLocatorDebuginfod.h"
|
|
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Interpreter/OptionValueString.h"
|
|
#include "lldb/Utility/Args.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
|
|
#include "llvm/Debuginfod/Debuginfod.h"
|
|
#include "llvm/Debuginfod/HTTPClient.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
LLDB_PLUGIN_DEFINE(SymbolLocatorDebuginfod)
|
|
|
|
namespace {
|
|
|
|
#define LLDB_PROPERTIES_symbollocatordebuginfod
|
|
#include "SymbolLocatorDebuginfodProperties.inc"
|
|
|
|
enum {
|
|
#define LLDB_PROPERTIES_symbollocatordebuginfod
|
|
#include "SymbolLocatorDebuginfodPropertiesEnum.inc"
|
|
};
|
|
|
|
class PluginProperties : public Properties {
|
|
public:
|
|
static llvm::StringRef GetSettingName() {
|
|
return SymbolLocatorDebuginfod::GetPluginNameStatic();
|
|
}
|
|
|
|
PluginProperties() {
|
|
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
|
|
m_collection_sp->Initialize(g_symbollocatordebuginfod_properties);
|
|
|
|
// We need to read the default value first to read the environment variable.
|
|
llvm::SmallVector<llvm::StringRef> urls = llvm::getDefaultDebuginfodUrls();
|
|
Args arg_urls{urls};
|
|
m_collection_sp->SetPropertyAtIndexFromArgs(ePropertyServerURLs, arg_urls);
|
|
|
|
m_collection_sp->SetValueChangedCallback(
|
|
ePropertyServerURLs, [this] { ServerURLsChangedCallback(); });
|
|
}
|
|
|
|
Args GetDebugInfoDURLs() const {
|
|
Args urls;
|
|
m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyServerURLs, urls);
|
|
return urls;
|
|
}
|
|
|
|
llvm::Expected<std::string> GetCachePath() {
|
|
OptionValueString *s =
|
|
m_collection_sp->GetPropertyAtIndexAsOptionValueString(
|
|
ePropertySymbolCachePath);
|
|
// If we don't have a valid cache location, use the default one.
|
|
if (!s || !s->GetCurrentValueAsRef().size()) {
|
|
llvm::Expected<std::string> maybeCachePath =
|
|
llvm::getDefaultDebuginfodCacheDirectory();
|
|
if (!maybeCachePath)
|
|
return maybeCachePath;
|
|
return *maybeCachePath;
|
|
}
|
|
return s->GetCurrentValue();
|
|
}
|
|
|
|
std::chrono::milliseconds GetTimeout() const {
|
|
std::optional<uint64_t> seconds =
|
|
m_collection_sp->GetPropertyAtIndexAs<uint64_t>(ePropertyTimeout);
|
|
if (seconds && *seconds != 0) {
|
|
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::seconds(*seconds));
|
|
} else {
|
|
return llvm::getDefaultDebuginfodTimeout();
|
|
}
|
|
}
|
|
|
|
private:
|
|
void ServerURLsChangedCallback() {
|
|
m_server_urls = GetDebugInfoDURLs();
|
|
llvm::SmallVector<llvm::StringRef> dbginfod_urls;
|
|
for (const auto &obj : m_server_urls)
|
|
dbginfod_urls.push_back(obj.ref());
|
|
llvm::setDefaultDebuginfodUrls(dbginfod_urls);
|
|
}
|
|
// Storage for the StringRef's used within the Debuginfod library.
|
|
Args m_server_urls;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static PluginProperties &GetGlobalPluginProperties() {
|
|
static PluginProperties g_settings;
|
|
return g_settings;
|
|
}
|
|
|
|
SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {}
|
|
|
|
void SymbolLocatorDebuginfod::Initialize() {
|
|
static llvm::once_flag g_once_flag;
|
|
|
|
llvm::call_once(g_once_flag, []() {
|
|
PluginManager::RegisterPlugin(
|
|
GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
|
|
LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr,
|
|
nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);
|
|
llvm::HTTPClient::initialize();
|
|
});
|
|
}
|
|
|
|
void SymbolLocatorDebuginfod::DebuggerInitialize(Debugger &debugger) {
|
|
if (!PluginManager::GetSettingForSymbolLocatorPlugin(
|
|
debugger, PluginProperties::GetSettingName())) {
|
|
const bool is_global_setting = true;
|
|
PluginManager::CreateSettingForSymbolLocatorPlugin(
|
|
debugger, GetGlobalPluginProperties().GetValueProperties(),
|
|
"Properties for the Debuginfod Symbol Locator plug-in.",
|
|
is_global_setting);
|
|
}
|
|
}
|
|
|
|
void SymbolLocatorDebuginfod::Terminate() {
|
|
PluginManager::UnregisterPlugin(CreateInstance);
|
|
llvm::HTTPClient::cleanup();
|
|
}
|
|
|
|
llvm::StringRef SymbolLocatorDebuginfod::GetPluginDescriptionStatic() {
|
|
return "Debuginfod symbol locator.";
|
|
}
|
|
|
|
SymbolLocator *SymbolLocatorDebuginfod::CreateInstance() {
|
|
return new SymbolLocatorDebuginfod();
|
|
}
|
|
|
|
static llvm::StringRef getFileName(const ModuleSpec &module_spec,
|
|
std::string url_path) {
|
|
// Check if the URL path requests an executable file or a symbol file
|
|
bool is_executable = url_path.find("debuginfo") == std::string::npos;
|
|
if (is_executable)
|
|
return module_spec.GetFileSpec().GetFilename().GetStringRef();
|
|
llvm::StringRef symbol_file =
|
|
module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();
|
|
// Remove llvmcache- prefix and hash, keep origin file name
|
|
if (symbol_file.starts_with("llvmcache-")) {
|
|
size_t pos = symbol_file.rfind('-');
|
|
if (pos != llvm::StringRef::npos) {
|
|
symbol_file = symbol_file.substr(pos + 1);
|
|
}
|
|
}
|
|
return symbol_file;
|
|
}
|
|
|
|
static std::optional<FileSpec>
|
|
GetFileForModule(const ModuleSpec &module_spec,
|
|
std::function<std::string(llvm::object::BuildID)> UrlBuilder) {
|
|
const UUID &module_uuid = module_spec.GetUUID();
|
|
// Don't bother if we don't have a valid UUID, Debuginfod isn't available,
|
|
// or if the 'symbols.enable-external-lookup' setting is false.
|
|
if (!module_uuid.IsValid() || !llvm::canUseDebuginfod() ||
|
|
!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
|
|
return {};
|
|
|
|
// Grab LLDB's Debuginfod overrides from the
|
|
// plugin.symbol-locator.debuginfod.* settings.
|
|
PluginProperties &plugin_props = GetGlobalPluginProperties();
|
|
llvm::Expected<std::string> cache_path_or_err = plugin_props.GetCachePath();
|
|
// A cache location is *required*.
|
|
if (!cache_path_or_err)
|
|
return {};
|
|
std::string cache_path = *cache_path_or_err;
|
|
llvm::SmallVector<llvm::StringRef> debuginfod_urls =
|
|
llvm::getDefaultDebuginfodUrls();
|
|
std::chrono::milliseconds timeout = plugin_props.GetTimeout();
|
|
|
|
// We're ready to ask the Debuginfod library to find our file.
|
|
llvm::object::BuildID build_id(module_uuid.GetBytes());
|
|
std::string url_path = UrlBuilder(build_id);
|
|
llvm::StringRef file_name = getFileName(module_spec, url_path);
|
|
std::string cache_file_name = llvm::toHex(build_id, true);
|
|
if (!file_name.empty())
|
|
cache_file_name += "-" + file_name.str();
|
|
llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact(
|
|
cache_file_name, url_path, cache_path, debuginfod_urls, timeout);
|
|
if (result)
|
|
return FileSpec(*result);
|
|
|
|
Log *log = GetLog(LLDBLog::Symbols);
|
|
auto err_message = llvm::toString(result.takeError());
|
|
LLDB_LOGV(log,
|
|
"Debuginfod failed to download symbol artifact {0} with error {1}",
|
|
url_path, err_message);
|
|
return {};
|
|
}
|
|
|
|
std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile(
|
|
const ModuleSpec &module_spec) {
|
|
return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath);
|
|
}
|
|
|
|
std::optional<FileSpec> SymbolLocatorDebuginfod::LocateExecutableSymbolFile(
|
|
const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
|
|
return GetFileForModule(module_spec, llvm::getDebuginfodDebuginfoUrlPath);
|
|
}
|