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 llvm::StringRef getFileName(const ModuleSpec &module_spec, 145 std::string url_path) { 146 // Check if the URL path requests an executable file or a symbol file 147 bool is_executable = url_path.find("debuginfo") == std::string::npos; 148 if (is_executable) 149 return module_spec.GetFileSpec().GetFilename().GetStringRef(); 150 llvm::StringRef symbol_file = 151 module_spec.GetSymbolFileSpec().GetFilename().GetStringRef(); 152 // Remove llvmcache- prefix and hash, keep origin file name 153 if (symbol_file.starts_with("llvmcache-")) { 154 size_t pos = symbol_file.rfind('-'); 155 if (pos != llvm::StringRef::npos) { 156 symbol_file = symbol_file.substr(pos + 1); 157 } 158 } 159 return symbol_file; 160 } 161 162 static std::optional<FileSpec> 163 GetFileForModule(const ModuleSpec &module_spec, 164 std::function<std::string(llvm::object::BuildID)> UrlBuilder) { 165 const UUID &module_uuid = module_spec.GetUUID(); 166 // Don't bother if we don't have a valid UUID, Debuginfod isn't available, 167 // or if the 'symbols.enable-external-lookup' setting is false. 168 if (!module_uuid.IsValid() || !llvm::canUseDebuginfod() || 169 !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) 170 return {}; 171 172 // Grab LLDB's Debuginfod overrides from the 173 // plugin.symbol-locator.debuginfod.* settings. 174 PluginProperties &plugin_props = GetGlobalPluginProperties(); 175 llvm::Expected<std::string> cache_path_or_err = plugin_props.GetCachePath(); 176 // A cache location is *required*. 177 if (!cache_path_or_err) 178 return {}; 179 std::string cache_path = *cache_path_or_err; 180 llvm::SmallVector<llvm::StringRef> debuginfod_urls = 181 llvm::getDefaultDebuginfodUrls(); 182 std::chrono::milliseconds timeout = plugin_props.GetTimeout(); 183 184 // We're ready to ask the Debuginfod library to find our file. 185 llvm::object::BuildID build_id(module_uuid.GetBytes()); 186 std::string url_path = UrlBuilder(build_id); 187 llvm::StringRef file_name = getFileName(module_spec, url_path); 188 std::string cache_file_name = llvm::toHex(build_id, true); 189 if (!file_name.empty()) 190 cache_file_name += "-" + file_name.str(); 191 llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact( 192 cache_file_name, url_path, cache_path, debuginfod_urls, timeout); 193 if (result) 194 return FileSpec(*result); 195 196 Log *log = GetLog(LLDBLog::Symbols); 197 auto err_message = llvm::toString(result.takeError()); 198 LLDB_LOGV(log, 199 "Debuginfod failed to download symbol artifact {0} with error {1}", 200 url_path, err_message); 201 return {}; 202 } 203 204 std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile( 205 const ModuleSpec &module_spec) { 206 return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath); 207 } 208 209 std::optional<FileSpec> SymbolLocatorDebuginfod::LocateExecutableSymbolFile( 210 const ModuleSpec &module_spec, const FileSpecList &default_search_paths) { 211 return GetFileForModule(module_spec, llvm::getDebuginfodDebuginfoUrlPath); 212 } 213