212 lines · cpp
1//===-- SymbolLocatorDebuginfod.cpp ---------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SymbolLocatorDebuginfod.h"10 11#include "lldb/Core/PluginManager.h"12#include "lldb/Interpreter/OptionValueString.h"13#include "lldb/Utility/Args.h"14#include "lldb/Utility/LLDBLog.h"15#include "lldb/Utility/Log.h"16 17#include "llvm/Debuginfod/Debuginfod.h"18#include "llvm/Debuginfod/HTTPClient.h"19 20using namespace lldb;21using namespace lldb_private;22 23LLDB_PLUGIN_DEFINE(SymbolLocatorDebuginfod)24 25namespace {26 27#define LLDB_PROPERTIES_symbollocatordebuginfod28#include "SymbolLocatorDebuginfodProperties.inc"29 30enum {31#define LLDB_PROPERTIES_symbollocatordebuginfod32#include "SymbolLocatorDebuginfodPropertiesEnum.inc"33};34 35class PluginProperties : public Properties {36public:37 static llvm::StringRef GetSettingName() {38 return SymbolLocatorDebuginfod::GetPluginNameStatic();39 }40 41 PluginProperties() {42 m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());43 m_collection_sp->Initialize(g_symbollocatordebuginfod_properties);44 45 // We need to read the default value first to read the environment variable.46 llvm::SmallVector<llvm::StringRef> urls = llvm::getDefaultDebuginfodUrls();47 Args arg_urls{urls};48 m_collection_sp->SetPropertyAtIndexFromArgs(ePropertyServerURLs, arg_urls);49 50 m_collection_sp->SetValueChangedCallback(51 ePropertyServerURLs, [this] { ServerURLsChangedCallback(); });52 }53 54 Args GetDebugInfoDURLs() const {55 Args urls;56 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyServerURLs, urls);57 return urls;58 }59 60 llvm::Expected<std::string> GetCachePath() {61 OptionValueString *s =62 m_collection_sp->GetPropertyAtIndexAsOptionValueString(63 ePropertySymbolCachePath);64 // If we don't have a valid cache location, use the default one.65 if (!s || !s->GetCurrentValueAsRef().size()) {66 llvm::Expected<std::string> maybeCachePath =67 llvm::getDefaultDebuginfodCacheDirectory();68 if (!maybeCachePath)69 return maybeCachePath;70 return *maybeCachePath;71 }72 return s->GetCurrentValue();73 }74 75 std::chrono::milliseconds GetTimeout() const {76 std::optional<uint64_t> seconds =77 m_collection_sp->GetPropertyAtIndexAs<uint64_t>(ePropertyTimeout);78 if (seconds && *seconds != 0) {79 return std::chrono::duration_cast<std::chrono::milliseconds>(80 std::chrono::seconds(*seconds));81 } else {82 return llvm::getDefaultDebuginfodTimeout();83 }84 }85 86private:87 void ServerURLsChangedCallback() {88 m_server_urls = GetDebugInfoDURLs();89 llvm::SmallVector<llvm::StringRef> dbginfod_urls;90 for (const auto &obj : m_server_urls)91 dbginfod_urls.push_back(obj.ref());92 llvm::setDefaultDebuginfodUrls(dbginfod_urls);93 }94 // Storage for the StringRef's used within the Debuginfod library.95 Args m_server_urls;96};97 98} // namespace99 100static PluginProperties &GetGlobalPluginProperties() {101 static PluginProperties g_settings;102 return g_settings;103}104 105SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {}106 107void SymbolLocatorDebuginfod::Initialize() {108 static llvm::once_flag g_once_flag;109 110 llvm::call_once(g_once_flag, []() {111 PluginManager::RegisterPlugin(112 GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,113 LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr,114 nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);115 llvm::HTTPClient::initialize();116 });117}118 119void SymbolLocatorDebuginfod::DebuggerInitialize(Debugger &debugger) {120 if (!PluginManager::GetSettingForSymbolLocatorPlugin(121 debugger, PluginProperties::GetSettingName())) {122 const bool is_global_setting = true;123 PluginManager::CreateSettingForSymbolLocatorPlugin(124 debugger, GetGlobalPluginProperties().GetValueProperties(),125 "Properties for the Debuginfod Symbol Locator plug-in.",126 is_global_setting);127 }128}129 130void SymbolLocatorDebuginfod::Terminate() {131 PluginManager::UnregisterPlugin(CreateInstance);132 llvm::HTTPClient::cleanup();133}134 135llvm::StringRef SymbolLocatorDebuginfod::GetPluginDescriptionStatic() {136 return "Debuginfod symbol locator.";137}138 139SymbolLocator *SymbolLocatorDebuginfod::CreateInstance() {140 return new SymbolLocatorDebuginfod();141}142 143static llvm::StringRef getFileName(const ModuleSpec &module_spec,144 std::string url_path) {145 // Check if the URL path requests an executable file or a symbol file146 bool is_executable = url_path.find("debuginfo") == std::string::npos;147 if (is_executable)148 return module_spec.GetFileSpec().GetFilename().GetStringRef();149 llvm::StringRef symbol_file =150 module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();151 // Remove llvmcache- prefix and hash, keep origin file name152 if (symbol_file.starts_with("llvmcache-")) {153 size_t pos = symbol_file.rfind('-');154 if (pos != llvm::StringRef::npos) {155 symbol_file = symbol_file.substr(pos + 1);156 }157 }158 return symbol_file;159}160 161static std::optional<FileSpec>162GetFileForModule(const ModuleSpec &module_spec,163 std::function<std::string(llvm::object::BuildID)> UrlBuilder) {164 const UUID &module_uuid = module_spec.GetUUID();165 // Don't bother if we don't have a valid UUID, Debuginfod isn't available,166 // or if the 'symbols.enable-external-lookup' setting is false.167 if (!module_uuid.IsValid() || !llvm::canUseDebuginfod() ||168 !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())169 return {};170 171 // Grab LLDB's Debuginfod overrides from the172 // plugin.symbol-locator.debuginfod.* settings.173 PluginProperties &plugin_props = GetGlobalPluginProperties();174 llvm::Expected<std::string> cache_path_or_err = plugin_props.GetCachePath();175 // A cache location is *required*.176 if (!cache_path_or_err)177 return {};178 std::string cache_path = *cache_path_or_err;179 llvm::SmallVector<llvm::StringRef> debuginfod_urls =180 llvm::getDefaultDebuginfodUrls();181 std::chrono::milliseconds timeout = plugin_props.GetTimeout();182 183 // We're ready to ask the Debuginfod library to find our file.184 llvm::object::BuildID build_id(module_uuid.GetBytes());185 std::string url_path = UrlBuilder(build_id);186 llvm::StringRef file_name = getFileName(module_spec, url_path);187 std::string cache_file_name = llvm::toHex(build_id, true);188 if (!file_name.empty())189 cache_file_name += "-" + file_name.str();190 llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact(191 cache_file_name, url_path, cache_path, debuginfod_urls, timeout);192 if (result)193 return FileSpec(*result);194 195 Log *log = GetLog(LLDBLog::Symbols);196 auto err_message = llvm::toString(result.takeError());197 LLDB_LOGV(log,198 "Debuginfod failed to download symbol artifact {0} with error {1}",199 url_path, err_message);200 return {};201}202 203std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile(204 const ModuleSpec &module_spec) {205 return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath);206}207 208std::optional<FileSpec> SymbolLocatorDebuginfod::LocateExecutableSymbolFile(209 const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {210 return GetFileForModule(module_spec, llvm::getDebuginfodDebuginfoUrlPath);211}212