Skip to content

[lldb] Implement priority system for symbol locator plugin #144406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

GeorgeHuyubo
Copy link
Contributor

In the context if locate_module callback is registered and debuginfod is enabled.
If debuginfod symbol locator is called first then we are double downloading the debuginfo file through locate module callback and debuginfod.

To fix this, we want to a way to define the priority of a plugin, so that we can adjust the order of each plugin gets called at runtime.

(lldb)settings set plugin.symbol-locator.debuginfod.priority 1
(lldb) image list
...
[  4] 581109F0-138B-AD5B-2E0B-1EB3E5174353-632286CF 0x00007f977c600000 /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1 
      /home/hyubo/.cache/llvm-debuginfod/client/llvmcache-581109f0138bad5b2e0b1eb3e5174353632286cf-libcrypto.so.1.1.debuginfo

Debuginfod plugin is prioritized

(lldb)settings set plugin.symbol-locator.debuginfod.priority 3
(lldb) image list
...
[  4] 581109F0-138B-AD5B-2E0B-1EB3E5174353-632286CF 0x00007f977c600000 /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1 
      /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1.debuginfo

Default plugin is prioritized, local debuginfo is used.

@GeorgeHuyubo GeorgeHuyubo marked this pull request as ready for review June 16, 2025 20:18
@llvmbot llvmbot added the lldb label Jun 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 16, 2025

@llvm/pr-subscribers-lldb

Author: None (GeorgeHuyubo)

Changes

In the context if locate_module callback is registered and debuginfod is enabled.
If debuginfod symbol locator is called first then we are double downloading the debuginfo file through locate module callback and debuginfod.

To fix this, we want to a way to define the priority of a plugin, so that we can adjust the order of each plugin gets called at runtime.

(lldb)settings set plugin.symbol-locator.debuginfod.priority 1
(lldb) image list
...
[  4] 581109F0-138B-AD5B-2E0B-1EB3E5174353-632286CF 0x00007f977c600000 /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1 
      /home/hyubo/.cache/llvm-debuginfod/client/llvmcache-581109f0138bad5b2e0b1eb3e5174353632286cf-libcrypto.so.1.1.debuginfo

Debuginfod plugin is prioritized

(lldb)settings set plugin.symbol-locator.debuginfod.priority 3
(lldb) image list
...
[  4] 581109F0-138B-AD5B-2E0B-1EB3E5174353-632286CF 0x00007f977c600000 /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1 
      /tmp/symbol_cache/581109f0138bad5b2e0b1eb3e5174353632286cf/libcrypto.so.1.1.debuginfo

Default plugin is prioritized, local debuginfo is used.


Full diff: https://github.com/llvm/llvm-project/pull/144406.diff

6 Files Affected:

  • (modified) lldb/include/lldb/Core/PluginManager.h (+2-1)
  • (modified) lldb/include/lldb/Symbol/SymbolLocator.h (+3)
  • (modified) lldb/include/lldb/lldb-private-interfaces.h (+1)
  • (modified) lldb/source/Core/PluginManager.cpp (+52-8)
  • (modified) lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp (+19-1)
  • (modified) lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfodProperties.td (+3)
diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
index e7b1691031111..021f90f54abff 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -431,7 +431,8 @@ class PluginManager {
       SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file =
           nullptr,
       SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle = nullptr,
-      DebuggerInitializeCallback debugger_init_callback = nullptr);
+      DebuggerInitializeCallback debugger_init_callback = nullptr,
+      SymbolLocatorGetPriority get_priority_callback = nullptr);
 
   static bool UnregisterPlugin(SymbolLocatorCreateInstance create_callback);
 
diff --git a/lldb/include/lldb/Symbol/SymbolLocator.h b/lldb/include/lldb/Symbol/SymbolLocator.h
index 8d2f26b3d0cca..0eb9d34cb1cc6 100644
--- a/lldb/include/lldb/Symbol/SymbolLocator.h
+++ b/lldb/include/lldb/Symbol/SymbolLocator.h
@@ -14,6 +14,9 @@
 
 namespace lldb_private {
 
+// Default priority for symbol locator plugins
+static constexpr uint32_t kDefaultSymbolLocatorPriority = 2;
+
 class SymbolLocator : public PluginInterface {
 public:
   SymbolLocator() = default;
diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h
index d366dbd1d7832..0fe59e28adaa6 100644
--- a/lldb/include/lldb/lldb-private-interfaces.h
+++ b/lldb/include/lldb/lldb-private-interfaces.h
@@ -100,6 +100,7 @@ typedef std::optional<FileSpec> (*SymbolLocatorLocateExecutableSymbolFile)(
 typedef bool (*SymbolLocatorDownloadObjectAndSymbolFile)(
     ModuleSpec &module_spec, Status &error, bool force_lookup,
     bool copy_executable);
+typedef uint64_t (*SymbolLocatorGetPriority)();
 using BreakpointHitCallback =
     std::function<bool(void *baton, StoppointCallbackContext *context,
                        lldb::user_id_t break_id, lldb::user_id_t break_loc_id)>;
diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
index 5d44434033c55..fedfd3f1e7b6f 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -13,6 +13,7 @@
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Interpreter/OptionValueProperties.h"
 #include "lldb/Symbol/SaveCoreOptions.h"
+#include "lldb/Symbol/SymbolLocator.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/Status.h"
@@ -25,6 +26,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
@@ -323,6 +325,29 @@ template <typename Instance> class PluginInstances {
     return enabled_instances;
   }
 
+  // Return a priority queue of all enabled instances, ordered by priority
+  // (lower priority values = higher priority in queue)
+  template <typename PriorityGetter>
+  std::priority_queue<Instance, std::vector<Instance>,
+                      std::function<bool(const Instance &, const Instance &)>>
+  GetPriorityQueue(PriorityGetter get_priority) {
+    // Create comparator that orders by priority (lower values = higher
+    // priority)
+    auto comparator = [get_priority](const Instance &a, const Instance &b) {
+      return get_priority(a) > get_priority(b); // Reverse for min-heap behavior
+    };
+
+    std::priority_queue<Instance, std::vector<Instance>,
+                        std::function<bool(const Instance &, const Instance &)>>
+        pq(comparator);
+
+    for (const auto &instance : m_instances) {
+      if (instance.enabled)
+        pq.push(instance);
+    }
+    return pq;
+  }
+
   const Instance *GetInstanceAtIndex(uint32_t idx) {
     uint32_t count = 0;
 
@@ -1223,18 +1248,26 @@ struct SymbolLocatorInstance
       SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file,
       SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file,
       SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle,
-      DebuggerInitializeCallback debugger_init_callback)
+      DebuggerInitializeCallback debugger_init_callback,
+      SymbolLocatorGetPriority get_priority_callback)
       : PluginInstance<SymbolLocatorCreateInstance>(
             name, description, create_callback, debugger_init_callback),
         locate_executable_object_file(locate_executable_object_file),
         locate_executable_symbol_file(locate_executable_symbol_file),
         download_object_symbol_file(download_object_symbol_file),
-        find_symbol_file_in_bundle(find_symbol_file_in_bundle) {}
+        find_symbol_file_in_bundle(find_symbol_file_in_bundle),
+        get_priority_callback(get_priority_callback) {}
+
+  uint64_t GetPriority() const {
+    return get_priority_callback ? get_priority_callback()
+                                 : kDefaultSymbolLocatorPriority;
+  }
 
   SymbolLocatorLocateExecutableObjectFile locate_executable_object_file;
   SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file;
   SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file;
   SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle;
+  SymbolLocatorGetPriority get_priority_callback;
 };
 typedef PluginInstances<SymbolLocatorInstance> SymbolLocatorInstances;
 
@@ -1250,11 +1283,13 @@ bool PluginManager::RegisterPlugin(
     SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file,
     SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file,
     SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle,
-    DebuggerInitializeCallback debugger_init_callback) {
+    DebuggerInitializeCallback debugger_init_callback,
+    SymbolLocatorGetPriority get_priority_callback) {
   return GetSymbolLocatorInstances().RegisterPlugin(
       name, description, create_callback, locate_executable_object_file,
       locate_executable_symbol_file, download_object_symbol_file,
-      find_symbol_file_in_bundle, debugger_init_callback);
+      find_symbol_file_in_bundle, debugger_init_callback,
+      get_priority_callback);
 }
 
 bool PluginManager::UnregisterPlugin(
@@ -1270,8 +1305,13 @@ PluginManager::GetSymbolLocatorCreateCallbackAtIndex(uint32_t idx) {
 ModuleSpec
 PluginManager::LocateExecutableObjectFile(const ModuleSpec &module_spec,
                                           StatisticsMap &map) {
-  auto instances = GetSymbolLocatorInstances().GetSnapshot();
-  for (auto &instance : instances) {
+  auto pq = GetSymbolLocatorInstances().GetPriorityQueue(
+      [](const auto &instance) { return instance.GetPriority(); });
+
+  while (!pq.empty()) {
+    auto instance = pq.top();
+    pq.pop();
+
     if (instance.locate_executable_object_file) {
       StatsDuration time;
       std::optional<ModuleSpec> result;
@@ -1290,8 +1330,12 @@ PluginManager::LocateExecutableObjectFile(const ModuleSpec &module_spec,
 FileSpec PluginManager::LocateExecutableSymbolFile(
     const ModuleSpec &module_spec, const FileSpecList &default_search_paths,
     StatisticsMap &map) {
-  auto instances = GetSymbolLocatorInstances().GetSnapshot();
-  for (auto &instance : instances) {
+  auto pq = GetSymbolLocatorInstances().GetPriorityQueue(
+      [](const auto &instance) { return instance.GetPriority(); });
+
+  while (!pq.empty()) {
+    auto instance = pq.top();
+    pq.pop();
     if (instance.locate_executable_symbol_file) {
       StatsDuration time;
       std::optional<FileSpec> result;
diff --git a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
index f9aa6b1a98765..3e2d863d0d20e 100644
--- a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
+++ b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
@@ -83,6 +83,16 @@ class PluginProperties : public Properties {
     }
   }
 
+  uint32_t GetPriority() const {
+    std::optional<uint32_t> priority =
+        m_collection_sp->GetPropertyAtIndexAs<uint64_t>(ePropertyPriority);
+    if (priority && *priority > 0) {
+      return priority.value();
+    } else {
+      return kDefaultSymbolLocatorPriority;
+    }
+  }
+
 private:
   void ServerURLsChangedCallback() {
     m_server_urls = GetDebugInfoDURLs();
@@ -103,6 +113,13 @@ static PluginProperties &GetGlobalPluginProperties() {
   return g_settings;
 }
 
+static uint64_t GetDebuginfodPriority() {
+  // Grab LLDB's Debuginfod overrides from the
+  // plugin.symbol-locator.debuginfod.* settings.
+  PluginProperties &plugin_props = GetGlobalPluginProperties();
+  return plugin_props.GetPriority();
+}
+
 SymbolLocatorDebuginfod::SymbolLocatorDebuginfod() : SymbolLocator() {}
 
 void SymbolLocatorDebuginfod::Initialize() {
@@ -112,7 +129,8 @@ void SymbolLocatorDebuginfod::Initialize() {
     PluginManager::RegisterPlugin(
         GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
         LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr,
-        nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);
+        nullptr, SymbolLocatorDebuginfod::DebuggerInitialize,
+        GetDebuginfodPriority);
     llvm::HTTPClient::initialize();
   });
 }
diff --git a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfodProperties.td b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfodProperties.td
index 0ff02674b8ea3..32389f8808b6f 100644
--- a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfodProperties.td
+++ b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfodProperties.td
@@ -10,4 +10,7 @@ let Definition = "symbollocatordebuginfod" in {
   def Timeout : Property<"timeout", "UInt64">,
     DefaultUnsignedValue<0>,
     Desc<"Timeout (in seconds) for requests made to a DEBUGINFOD server. A value of zero means we use the debuginfod default timeout: DEBUGINFOD_TIMEOUT if the environment variable is set and 90 seconds otherwise.">;
+  def Priority : Property<"priority", "UInt64">,
+    DefaultUnsignedValue<2>,
+    Desc<"The priority order for this symbol locator plugin when multiple symbol locators are available. Lower values indicate higher priority. The default symbol locator priority is 2">;
 }

@JDevlieghere
Copy link
Member

I don't yet understand how changing the priority of the plugins prevents the "double download" you mention. Your problem statements talks about the callback and the debuginfod plugin, but your solution talks about the debuginfod plugin and the default symbol locator. What confuses me is that locate_module mechanism isn't using the symbol locator plugin mechanism. How does prioritizing one plugin over another prevent us from invoking the callback? Is this somehow called from the default symbol locator (I tried to look at the code but couldn't immediately find something like that).

In #99362, the order of the plugins was changed to prioritize the debuginfod plugin for stripped binaries. How does this interact with that? Will this go back to doing the wrong thing is the priority is too low?

Finally, this PR only supports setting the priority of the debuginfod symbol locator. That only works if you have exactly two plugins. As soon as you have more (as is the case on Darwin, and possibly soon we'll have a fourth one) that mechanism falls short.

In hindsight, I don't think the locate_module callback mechanism was the right choice. It probably was at the time, when the symbol locators weren't plugins and we didn't have debuginfod support yet. But I think moving, forward, I'd very much prefer to move to the idea in https://discourse.llvm.org/t/rfc-python-callback-for-source-file-resolution/83545 and https://discourse.llvm.org/t/rfc-support-fetching-source-files-with-debuginfod/86579 and implementing a "Scripted Symbol Locator" instead. That way we can let the usual plugin mechanism deal with falling back between plugins. If then there's still a need to set a dynamic order, I think the priority queue could make sense.

@GeorgeHuyubo
Copy link
Contributor Author

I like the "Scripted Symbol Locator" idea, it should solve all the problem I have.

@GeorgeHuyubo
Copy link
Contributor Author

GeorgeHuyubo commented Jun 17, 2025

I don't yet understand how changing the priority of the plugins prevents the "double download" you mention. Your problem statements talks about the callback and the debuginfod plugin, but your solution talks about the debuginfod plugin and the default symbol locator. What confuses me is that locate_module mechanism isn't using the symbol locator plugin mechanism. How does prioritizing one plugin over another prevent us from invoking the callback? Is this somehow called from the default symbol locator (I tried to look at the code but couldn't immediately find something like that).

So I believe the locate_module callback is designed for user to do something like:

symbol_file_spec.SetDirectory(symbol_dir)
symbol_file_spec.SetFilename(symbol_file_name)

...after the callback function finish downloading the symbol. This means that we still rely on the Default symbol locator(which should simply look for symbol locally by the dir&filename) to actually locate the symbol file. But if debuginfod is also enabled and called first, debuginfod plugin will ignore the local symbol file(even if it exist), but query the debuginfod server by the gnu_build_id, and do a double downloading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants