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:
GetSettingName()37 static llvm::StringRef GetSettingName() {
38 return SymbolLocatorDebuginfod::GetPluginNameStatic();
39 }
40
PluginProperties()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
GetDebugInfoDURLs() const54 Args GetDebugInfoDURLs() const {
55 Args urls;
56 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyServerURLs, urls);
57 return urls;
58 }
59
GetCachePath()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
GetTimeout() const75 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:
ServerURLsChangedCallback()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
GetGlobalPluginProperties()101 static PluginProperties &GetGlobalPluginProperties() {
102 static PluginProperties g_settings;
103 return g_settings;
104 }
105
SymbolLocatorDebuginfod()106 SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {}
107
Initialize()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
DebuggerInitialize(Debugger & debugger)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
Terminate()131 void SymbolLocatorDebuginfod::Terminate() {
132 PluginManager::UnregisterPlugin(CreateInstance);
133 llvm::HTTPClient::cleanup();
134 }
135
GetPluginDescriptionStatic()136 llvm::StringRef SymbolLocatorDebuginfod::GetPluginDescriptionStatic() {
137 return "Debuginfod symbol locator.";
138 }
139
CreateInstance()140 SymbolLocator *SymbolLocatorDebuginfod::CreateInstance() {
141 return new SymbolLocatorDebuginfod();
142 }
143
getFileName(const ModuleSpec & module_spec,std::string url_path)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>
GetFileForModule(const ModuleSpec & module_spec,std::function<std::string (llvm::object::BuildID)> UrlBuilder)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
LocateExecutableObjectFile(const ModuleSpec & module_spec)204 std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile(
205 const ModuleSpec &module_spec) {
206 return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath);
207 }
208
LocateExecutableSymbolFile(const ModuleSpec & module_spec,const FileSpecList & default_search_paths)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