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-exception 6 // 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 20 using namespace lldb; 21 using namespace lldb_private; 22 23 LLDB_PLUGIN_DEFINE(SymbolLocatorDebuginfod) 24 25 namespace { 26 27 #define LLDB_PROPERTIES_symbollocatordebuginfod 28 #include "SymbolLocatorDebuginfodProperties.inc" 29 30 enum { 31 #define LLDB_PROPERTIES_symbollocatordebuginfod 32 #include "SymbolLocatorDebuginfodPropertiesEnum.inc" 33 }; 34 35 class PluginProperties : public Properties { 36 public: 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 86 private: 87 void ServerURLsChangedCallback() { 88 m_server_urls = GetDebugInfoDURLs(); 89 llvm::SmallVector<llvm::StringRef> dbginfod_urls; 90 llvm::for_each(m_server_urls, [&](const auto &obj) { 91 dbginfod_urls.push_back(obj.ref()); 92 }); 93 llvm::setDefaultDebuginfodUrls(dbginfod_urls); 94 } 95 // Storage for the StringRef's used within the Debuginfod library. 96 Args m_server_urls; 97 }; 98 99 } // namespace 100 101 static PluginProperties &GetGlobalPluginProperties() { 102 static PluginProperties g_settings; 103 return g_settings; 104 } 105 106 SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {} 107 108 void SymbolLocatorDebuginfod::Initialize() { 109 static llvm::once_flag g_once_flag; 110 111 llvm::call_once(g_once_flag, []() { 112 PluginManager::RegisterPlugin( 113 GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, 114 LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr, 115 nullptr, SymbolLocatorDebuginfod::DebuggerInitialize); 116 llvm::HTTPClient::initialize(); 117 }); 118 } 119 120 void SymbolLocatorDebuginfod::DebuggerInitialize(Debugger &debugger) { 121 if (!PluginManager::GetSettingForSymbolLocatorPlugin( 122 debugger, PluginProperties::GetSettingName())) { 123 const bool is_global_setting = true; 124 PluginManager::CreateSettingForSymbolLocatorPlugin( 125 debugger, GetGlobalPluginProperties().GetValueProperties(), 126 "Properties for the Debuginfod Symbol Locator plug-in.", 127 is_global_setting); 128 } 129 } 130 131 void SymbolLocatorDebuginfod::Terminate() { 132 PluginManager::UnregisterPlugin(CreateInstance); 133 llvm::HTTPClient::cleanup(); 134 } 135 136 llvm::StringRef SymbolLocatorDebuginfod::GetPluginDescriptionStatic() { 137 return "Debuginfod symbol locator."; 138 } 139 140 SymbolLocator *SymbolLocatorDebuginfod::CreateInstance() { 141 return new SymbolLocatorDebuginfod(); 142 } 143 144 static std::optional<FileSpec> 145 GetFileForModule(const ModuleSpec &module_spec, 146 std::function<std::string(llvm::object::BuildID)> UrlBuilder) { 147 const UUID &module_uuid = module_spec.GetUUID(); 148 // Don't bother if we don't have a valid UUID, Debuginfod isn't available, 149 // or if the 'symbols.enable-external-lookup' setting is false. 150 if (!module_uuid.IsValid() || !llvm::canUseDebuginfod() || 151 !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) 152 return {}; 153 154 // Grab LLDB's Debuginfod overrides from the 155 // plugin.symbol-locator.debuginfod.* settings. 156 PluginProperties &plugin_props = GetGlobalPluginProperties(); 157 llvm::Expected<std::string> cache_path_or_err = plugin_props.GetCachePath(); 158 // A cache location is *required*. 159 if (!cache_path_or_err) 160 return {}; 161 std::string cache_path = *cache_path_or_err; 162 llvm::SmallVector<llvm::StringRef> debuginfod_urls = 163 llvm::getDefaultDebuginfodUrls(); 164 std::chrono::milliseconds timeout = plugin_props.GetTimeout(); 165 166 // We're ready to ask the Debuginfod library to find our file. 167 llvm::object::BuildID build_id(module_uuid.GetBytes()); 168 std::string url_path = UrlBuilder(build_id); 169 std::string cache_key = llvm::getDebuginfodCacheKey(url_path); 170 llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact( 171 cache_key, url_path, cache_path, debuginfod_urls, timeout); 172 if (result) 173 return FileSpec(*result); 174 175 Log *log = GetLog(LLDBLog::Symbols); 176 auto err_message = llvm::toString(result.takeError()); 177 LLDB_LOGV(log, 178 "Debuginfod failed to download symbol artifact {0} with error {1}", 179 url_path, err_message); 180 return {}; 181 } 182 183 std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile( 184 const ModuleSpec &module_spec) { 185 return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath); 186 } 187 188 std::optional<FileSpec> SymbolLocatorDebuginfod::LocateExecutableSymbolFile( 189 const ModuleSpec &module_spec, const FileSpecList &default_search_paths) { 190 return GetFileForModule(module_spec, llvm::getDebuginfodDebuginfoUrlPath); 191 } 192