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(flag&: g_once_flag, F: []() { |
112 | PluginManager::RegisterPlugin( |
113 | name: GetPluginNameStatic(), description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
114 | locate_executable_object_file: LocateExecutableObjectFile, locate_executable_symbol_file: LocateExecutableSymbolFile, download_object_symbol_file: nullptr, |
115 | find_symbol_file_in_bundle: nullptr, debugger_init_callback: SymbolLocatorDebuginfod::DebuggerInitialize); |
116 | llvm::HTTPClient::initialize(); |
117 | }); |
118 | } |
119 | |
120 | void SymbolLocatorDebuginfod::DebuggerInitialize(Debugger &debugger) { |
121 | if (!PluginManager::GetSettingForSymbolLocatorPlugin( |
122 | debugger, setting_name: PluginProperties::GetSettingName())) { |
123 | const bool is_global_setting = true; |
124 | PluginManager::CreateSettingForSymbolLocatorPlugin( |
125 | debugger, properties_sp: GetGlobalPluginProperties().GetValueProperties(), |
126 | description: "Properties for the Debuginfod Symbol Locator plug-in." , |
127 | is_global_property: is_global_setting); |
128 | } |
129 | } |
130 | |
131 | void SymbolLocatorDebuginfod::Terminate() { |
132 | PluginManager::UnregisterPlugin(create_callback: 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(UrlPath: url_path); |
170 | llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact( |
171 | UniqueKey: cache_key, UrlPath: url_path, CacheDirectoryPath: cache_path, DebuginfodUrls: debuginfod_urls, Timeout: timeout); |
172 | if (result) |
173 | return FileSpec(*result); |
174 | |
175 | Log *log = GetLog(mask: LLDBLog::Symbols); |
176 | auto err_message = llvm::toString(E: 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, UrlBuilder: 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, UrlBuilder: llvm::getDebuginfodDebuginfoUrlPath); |
191 | } |
192 | |