xref: /llvm-project/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp (revision 1908c41259dbd43567bb8fd32ee69862411305ef)
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