15f757f3fSDimitry Andric //===-- SymbolLocatorDebuginfod.cpp ---------------------------------------===//
25f757f3fSDimitry Andric //
35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f757f3fSDimitry Andric //
75f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
85f757f3fSDimitry Andric 
95f757f3fSDimitry Andric #include "SymbolLocatorDebuginfod.h"
105f757f3fSDimitry Andric 
115f757f3fSDimitry Andric #include "lldb/Core/PluginManager.h"
12*7a6dacacSDimitry Andric #include "lldb/Interpreter/OptionValueString.h"
135f757f3fSDimitry Andric #include "lldb/Utility/Args.h"
14*7a6dacacSDimitry Andric #include "lldb/Utility/LLDBLog.h"
15*7a6dacacSDimitry Andric #include "lldb/Utility/Log.h"
165f757f3fSDimitry Andric 
175f757f3fSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h"
185f757f3fSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
195f757f3fSDimitry Andric 
205f757f3fSDimitry Andric using namespace lldb;
215f757f3fSDimitry Andric using namespace lldb_private;
225f757f3fSDimitry Andric 
235f757f3fSDimitry Andric LLDB_PLUGIN_DEFINE(SymbolLocatorDebuginfod)
245f757f3fSDimitry Andric 
255f757f3fSDimitry Andric namespace {
265f757f3fSDimitry Andric 
275f757f3fSDimitry Andric #define LLDB_PROPERTIES_symbollocatordebuginfod
285f757f3fSDimitry Andric #include "SymbolLocatorDebuginfodProperties.inc"
295f757f3fSDimitry Andric 
305f757f3fSDimitry Andric enum {
315f757f3fSDimitry Andric #define LLDB_PROPERTIES_symbollocatordebuginfod
325f757f3fSDimitry Andric #include "SymbolLocatorDebuginfodPropertiesEnum.inc"
335f757f3fSDimitry Andric };
345f757f3fSDimitry Andric 
355f757f3fSDimitry Andric class PluginProperties : public Properties {
365f757f3fSDimitry Andric public:
GetSettingName()375f757f3fSDimitry Andric   static llvm::StringRef GetSettingName() {
385f757f3fSDimitry Andric     return SymbolLocatorDebuginfod::GetPluginNameStatic();
395f757f3fSDimitry Andric   }
405f757f3fSDimitry Andric 
PluginProperties()415f757f3fSDimitry Andric   PluginProperties() {
425f757f3fSDimitry Andric     m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
435f757f3fSDimitry Andric     m_collection_sp->Initialize(g_symbollocatordebuginfod_properties);
445f757f3fSDimitry Andric 
455f757f3fSDimitry Andric     // We need to read the default value first to read the environment variable.
465f757f3fSDimitry Andric     llvm::SmallVector<llvm::StringRef> urls = llvm::getDefaultDebuginfodUrls();
475f757f3fSDimitry Andric     Args arg_urls{urls};
485f757f3fSDimitry Andric     m_collection_sp->SetPropertyAtIndexFromArgs(ePropertyServerURLs, arg_urls);
495f757f3fSDimitry Andric 
505f757f3fSDimitry Andric     m_collection_sp->SetValueChangedCallback(
515f757f3fSDimitry Andric         ePropertyServerURLs, [this] { ServerURLsChangedCallback(); });
525f757f3fSDimitry Andric   }
535f757f3fSDimitry Andric 
GetDebugInfoDURLs() const545f757f3fSDimitry Andric   Args GetDebugInfoDURLs() const {
555f757f3fSDimitry Andric     Args urls;
565f757f3fSDimitry Andric     m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyServerURLs, urls);
575f757f3fSDimitry Andric     return urls;
585f757f3fSDimitry Andric   }
595f757f3fSDimitry Andric 
GetCachePath()60*7a6dacacSDimitry Andric   llvm::Expected<std::string> GetCachePath() {
61*7a6dacacSDimitry Andric     OptionValueString *s =
62*7a6dacacSDimitry Andric         m_collection_sp->GetPropertyAtIndexAsOptionValueString(
63*7a6dacacSDimitry Andric             ePropertySymbolCachePath);
64*7a6dacacSDimitry Andric     // If we don't have a valid cache location, use the default one.
65*7a6dacacSDimitry Andric     if (!s || !s->GetCurrentValueAsRef().size()) {
66*7a6dacacSDimitry Andric       llvm::Expected<std::string> maybeCachePath =
67*7a6dacacSDimitry Andric           llvm::getDefaultDebuginfodCacheDirectory();
68*7a6dacacSDimitry Andric       if (!maybeCachePath)
69*7a6dacacSDimitry Andric         return maybeCachePath;
70*7a6dacacSDimitry Andric       return *maybeCachePath;
71*7a6dacacSDimitry Andric     }
72*7a6dacacSDimitry Andric     return s->GetCurrentValue();
73*7a6dacacSDimitry Andric   }
74*7a6dacacSDimitry Andric 
GetTimeout() const75*7a6dacacSDimitry Andric   std::chrono::milliseconds GetTimeout() const {
76*7a6dacacSDimitry Andric     std::optional<uint64_t> seconds =
77*7a6dacacSDimitry Andric         m_collection_sp->GetPropertyAtIndexAs<uint64_t>(ePropertyTimeout);
78*7a6dacacSDimitry Andric     if (seconds && *seconds != 0) {
79*7a6dacacSDimitry Andric       return std::chrono::duration_cast<std::chrono::milliseconds>(
80*7a6dacacSDimitry Andric           std::chrono::seconds(*seconds));
81*7a6dacacSDimitry Andric     } else {
82*7a6dacacSDimitry Andric       return llvm::getDefaultDebuginfodTimeout();
83*7a6dacacSDimitry Andric     }
84*7a6dacacSDimitry Andric   }
85*7a6dacacSDimitry Andric 
865f757f3fSDimitry Andric private:
ServerURLsChangedCallback()875f757f3fSDimitry Andric   void ServerURLsChangedCallback() {
885f757f3fSDimitry Andric     m_server_urls = GetDebugInfoDURLs();
895f757f3fSDimitry Andric     llvm::SmallVector<llvm::StringRef> dbginfod_urls;
905f757f3fSDimitry Andric     llvm::for_each(m_server_urls, [&](const auto &obj) {
915f757f3fSDimitry Andric       dbginfod_urls.push_back(obj.ref());
925f757f3fSDimitry Andric     });
935f757f3fSDimitry Andric     llvm::setDefaultDebuginfodUrls(dbginfod_urls);
945f757f3fSDimitry Andric   }
955f757f3fSDimitry Andric   // Storage for the StringRef's used within the Debuginfod library.
965f757f3fSDimitry Andric   Args m_server_urls;
975f757f3fSDimitry Andric };
985f757f3fSDimitry Andric 
995f757f3fSDimitry Andric } // namespace
1005f757f3fSDimitry Andric 
GetGlobalPluginProperties()1015f757f3fSDimitry Andric static PluginProperties &GetGlobalPluginProperties() {
1025f757f3fSDimitry Andric   static PluginProperties g_settings;
1035f757f3fSDimitry Andric   return g_settings;
1045f757f3fSDimitry Andric }
1055f757f3fSDimitry Andric 
SymbolLocatorDebuginfod()1065f757f3fSDimitry Andric SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {}
1075f757f3fSDimitry Andric 
Initialize()1085f757f3fSDimitry Andric void SymbolLocatorDebuginfod::Initialize() {
1095f757f3fSDimitry Andric   static llvm::once_flag g_once_flag;
1105f757f3fSDimitry Andric 
1115f757f3fSDimitry Andric   llvm::call_once(g_once_flag, []() {
1125f757f3fSDimitry Andric     PluginManager::RegisterPlugin(
1135f757f3fSDimitry Andric         GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
1145f757f3fSDimitry Andric         LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr,
1155f757f3fSDimitry Andric         nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);
1165f757f3fSDimitry Andric     llvm::HTTPClient::initialize();
1175f757f3fSDimitry Andric   });
1185f757f3fSDimitry Andric }
1195f757f3fSDimitry Andric 
DebuggerInitialize(Debugger & debugger)1205f757f3fSDimitry Andric void SymbolLocatorDebuginfod::DebuggerInitialize(Debugger &debugger) {
1215f757f3fSDimitry Andric   if (!PluginManager::GetSettingForSymbolLocatorPlugin(
1225f757f3fSDimitry Andric           debugger, PluginProperties::GetSettingName())) {
1235f757f3fSDimitry Andric     const bool is_global_setting = true;
1245f757f3fSDimitry Andric     PluginManager::CreateSettingForSymbolLocatorPlugin(
1255f757f3fSDimitry Andric         debugger, GetGlobalPluginProperties().GetValueProperties(),
1265f757f3fSDimitry Andric         "Properties for the Debuginfod Symbol Locator plug-in.",
1275f757f3fSDimitry Andric         is_global_setting);
1285f757f3fSDimitry Andric   }
1295f757f3fSDimitry Andric }
1305f757f3fSDimitry Andric 
Terminate()1315f757f3fSDimitry Andric void SymbolLocatorDebuginfod::Terminate() {
1325f757f3fSDimitry Andric   PluginManager::UnregisterPlugin(CreateInstance);
1335f757f3fSDimitry Andric   llvm::HTTPClient::cleanup();
1345f757f3fSDimitry Andric }
1355f757f3fSDimitry Andric 
GetPluginDescriptionStatic()1365f757f3fSDimitry Andric llvm::StringRef SymbolLocatorDebuginfod::GetPluginDescriptionStatic() {
1375f757f3fSDimitry Andric   return "Debuginfod symbol locator.";
1385f757f3fSDimitry Andric }
1395f757f3fSDimitry Andric 
CreateInstance()1405f757f3fSDimitry Andric SymbolLocator *SymbolLocatorDebuginfod::CreateInstance() {
1415f757f3fSDimitry Andric   return new SymbolLocatorDebuginfod();
1425f757f3fSDimitry Andric }
1435f757f3fSDimitry Andric 
144*7a6dacacSDimitry Andric static std::optional<FileSpec>
GetFileForModule(const ModuleSpec & module_spec,std::function<std::string (llvm::object::BuildID)> UrlBuilder)145*7a6dacacSDimitry Andric GetFileForModule(const ModuleSpec &module_spec,
146*7a6dacacSDimitry Andric                  std::function<std::string(llvm::object::BuildID)> UrlBuilder) {
1475f757f3fSDimitry Andric   const UUID &module_uuid = module_spec.GetUUID();
148*7a6dacacSDimitry Andric   // Don't bother if we don't have a valid UUID, Debuginfod isn't available,
149*7a6dacacSDimitry Andric   // or if the 'symbols.enable-external-lookup' setting is false.
150*7a6dacacSDimitry Andric   if (!module_uuid.IsValid() || !llvm::canUseDebuginfod() ||
151*7a6dacacSDimitry Andric       !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
152*7a6dacacSDimitry Andric     return {};
153*7a6dacacSDimitry Andric 
154*7a6dacacSDimitry Andric   // Grab LLDB's Debuginfod overrides from the
155*7a6dacacSDimitry Andric   // plugin.symbol-locator.debuginfod.* settings.
156*7a6dacacSDimitry Andric   PluginProperties &plugin_props = GetGlobalPluginProperties();
157*7a6dacacSDimitry Andric   llvm::Expected<std::string> cache_path_or_err = plugin_props.GetCachePath();
158*7a6dacacSDimitry Andric   // A cache location is *required*.
159*7a6dacacSDimitry Andric   if (!cache_path_or_err)
160*7a6dacacSDimitry Andric     return {};
161*7a6dacacSDimitry Andric   std::string cache_path = *cache_path_or_err;
162*7a6dacacSDimitry Andric   llvm::SmallVector<llvm::StringRef> debuginfod_urls =
163*7a6dacacSDimitry Andric       llvm::getDefaultDebuginfodUrls();
164*7a6dacacSDimitry Andric   std::chrono::milliseconds timeout = plugin_props.GetTimeout();
165*7a6dacacSDimitry Andric 
166*7a6dacacSDimitry Andric   // We're ready to ask the Debuginfod library to find our file.
1675f757f3fSDimitry Andric   llvm::object::BuildID build_id(module_uuid.GetBytes());
168*7a6dacacSDimitry Andric   std::string url_path = UrlBuilder(build_id);
169*7a6dacacSDimitry Andric   std::string cache_key = llvm::getDebuginfodCacheKey(url_path);
170*7a6dacacSDimitry Andric   llvm::Expected<std::string> result = llvm::getCachedOrDownloadArtifact(
171*7a6dacacSDimitry Andric       cache_key, url_path, cache_path, debuginfod_urls, timeout);
1725f757f3fSDimitry Andric   if (result)
1735f757f3fSDimitry Andric     return FileSpec(*result);
174*7a6dacacSDimitry Andric 
175*7a6dacacSDimitry Andric   Log *log = GetLog(LLDBLog::Symbols);
176*7a6dacacSDimitry Andric   auto err_message = llvm::toString(result.takeError());
177*7a6dacacSDimitry Andric   LLDB_LOGV(log,
178*7a6dacacSDimitry Andric             "Debuginfod failed to download symbol artifact {0} with error {1}",
179*7a6dacacSDimitry Andric             url_path, err_message);
1805f757f3fSDimitry Andric   return {};
1815f757f3fSDimitry Andric }
1825f757f3fSDimitry Andric 
LocateExecutableObjectFile(const ModuleSpec & module_spec)1835f757f3fSDimitry Andric std::optional<ModuleSpec> SymbolLocatorDebuginfod::LocateExecutableObjectFile(
1845f757f3fSDimitry Andric     const ModuleSpec &module_spec) {
185*7a6dacacSDimitry Andric   return GetFileForModule(module_spec, llvm::getDebuginfodExecutableUrlPath);
1865f757f3fSDimitry Andric }
1875f757f3fSDimitry Andric 
LocateExecutableSymbolFile(const ModuleSpec & module_spec,const FileSpecList & default_search_paths)1885f757f3fSDimitry Andric std::optional<FileSpec> SymbolLocatorDebuginfod::LocateExecutableSymbolFile(
1895f757f3fSDimitry Andric     const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
190*7a6dacacSDimitry Andric   return GetFileForModule(module_spec, llvm::getDebuginfodDebuginfoUrlPath);
1915f757f3fSDimitry Andric }
192