https://github.com/medismailben created 
https://github.com/llvm/llvm-project/pull/172848

This patch adds `get_priority()` support to synthetic frame providers to enable 
priority-based selection when multiple providers match a thread. This is the 
first step toward supporting frame provider chaining for visualizing 
coroutines, Swift async tasks, and et al.

Priority ordering follows Unix nice convention where lower numbers indicate 
higher priority (0 = highest). Providers without explicit priority return 
`std::nullopt`, which maps to UINT32_MAX (lowest priority), ensuring backward 
compatibility with existing providers.

The implementation adds `GetPriority()` as a virtual method to 
`SyntheticFrameProvider` base class, implements it through the scripting 
interface hierarchy (`ScriptedFrameProviderInterface` and 
`ScriptedFrameProviderPythonInterface`), and updates 
`Thread::GetStackFrameList()` to sort applicable providers by priority before 
attempting to load them.

Python frame providers can now specify priority:

```python
@staticmethod
def get_priority():
   return 10  # Or return None for default priority
```

>From 80850184ad4e13108b39f458493d711a92437cef Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <[email protected]>
Date: Sun, 14 Dec 2025 07:22:18 +0100
Subject: [PATCH] [lldb] Add priority support to synthetic frame providers

This patch adds `get_priority()` support to synthetic frame providers
to enable priority-based selection when multiple providers match a thread.
This is the first step toward supporting frame provider chaining for
visualizing coroutines, Swift async tasks, and et al.

Priority ordering follows Unix nice convention where lower numbers
indicate higher priority (0 = highest). Providers without explicit
priority return `std::nullopt`, which maps to UINT32_MAX (lowest priority),
ensuring backward compatibility with existing providers.

The implementation adds `GetPriority()` as a virtual method to
`SyntheticFrameProvider` base class, implements it through the scripting
interface hierarchy (`ScriptedFrameProviderInterface` and
`ScriptedFrameProviderPythonInterface`), and updates 
`Thread::GetStackFrameList()`
to sort applicable providers by priority before attempting to load them.

Python frame providers can now specify priority:

```python
@staticmethod
def get_priority():
   return 10  # Or return None for default priority
```

Signed-off-by: Med Ismail Bennani <[email protected]>
---
 .../templates/scripted_frame_provider.py      | 28 ++++++++++++++++++
 .../ScriptedFrameProviderInterface.h          | 16 ++++++++++
 .../lldb/Target/SyntheticFrameProvider.h      | 21 ++++++++++++++
 .../ScriptedFrameProviderPythonInterface.cpp  | 18 ++++++++++++
 .../ScriptedFrameProviderPythonInterface.h    |  2 ++
 .../ScriptedFrameProvider.cpp                 |  7 +++++
 .../ScriptedFrameProvider.h                   |  2 ++
 lldb/source/Target/SyntheticFrameProvider.cpp | 14 +++++++++
 lldb/source/Target/Thread.cpp                 | 29 +++++++++++++++----
 9 files changed, 131 insertions(+), 6 deletions(-)

diff --git a/lldb/examples/python/templates/scripted_frame_provider.py 
b/lldb/examples/python/templates/scripted_frame_provider.py
index 7a72f1a24c9da..a45ef9427a532 100644
--- a/lldb/examples/python/templates/scripted_frame_provider.py
+++ b/lldb/examples/python/templates/scripted_frame_provider.py
@@ -79,6 +79,34 @@ def get_description(self):
         """
         pass
 
+    @staticmethod
+    def get_priority():
+        """Get the priority of this frame provider.
+
+        This static method is called to determine the evaluation order when
+        multiple frame providers could apply to the same thread. Lower numbers
+        indicate higher priority (like Unix nice values).
+
+        Returns:
+            int or None: Priority value where 0 is highest priority.
+                Return None for default priority (UINT32_MAX - lowest 
priority).
+
+        Example:
+
+        .. code-block:: python
+
+            @staticmethod
+            def get_priority():
+                # High priority - runs before most providers
+                return 10
+
+            @staticmethod
+            def get_priority():
+                # Default priority - runs last
+                return None
+        """
+        return None  # Default/lowest priority
+
     def __init__(self, input_frames, args):
         """Construct a scripted frame provider.
 
diff --git 
a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
index 49b60131399d5..b04af0c817b6e 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
@@ -39,6 +39,22 @@ class ScriptedFrameProviderInterface : public 
ScriptedInterface {
   ///         empty string if no description is available.
   virtual std::string GetDescription(llvm::StringRef class_name) { return {}; }
 
+  /// Get the priority of this frame provider.
+  ///
+  /// This is called by the descriptor to fetch the priority from the
+  /// scripted implementation. Implementations should call a static method
+  /// on the scripting class to retrieve the priority. Lower numbers indicate
+  /// higher priority (like Unix nice values).
+  ///
+  /// \param class_name The name of the scripting class implementing the
+  /// provider.
+  ///
+  /// \return Priority value where 0 is highest priority, or std::nullopt for
+  ///         default priority (UINT32_MAX - lowest priority).
+  virtual std::optional<uint32_t> GetPriority(llvm::StringRef class_name) {
+    return std::nullopt;
+  }
+
   virtual StructuredData::ObjectSP GetFrameAtIndex(uint32_t index) {
     return {};
   }
diff --git a/lldb/include/lldb/Target/SyntheticFrameProvider.h 
b/lldb/include/lldb/Target/SyntheticFrameProvider.h
index 2d5330cb03105..bbd52b144412d 100644
--- a/lldb/include/lldb/Target/SyntheticFrameProvider.h
+++ b/lldb/include/lldb/Target/SyntheticFrameProvider.h
@@ -56,6 +56,16 @@ struct ScriptedFrameProviderDescriptor {
   ///         empty string if no description is available.
   std::string GetDescription() const;
 
+  /// Get the priority of this frame provider.
+  ///
+  /// Priority determines the order in which providers are evaluated when
+  /// multiple providers could apply to the same thread. Lower numbers indicate
+  /// higher priority (like Unix nice values).
+  ///
+  /// \return Priority value where 0 is highest priority, or std::nullopt for
+  ///         default priority (UINT32_MAX - lowest priority).
+  std::optional<uint32_t> GetPriority() const;
+
   /// Check if this descriptor applies to the given thread.
   bool AppliesToThread(Thread &thread) const {
     // If no thread specs specified, applies to all threads.
@@ -143,6 +153,17 @@ class SyntheticFrameProvider : public PluginInterface {
 
   virtual std::string GetDescription() const = 0;
 
+  /// Get the priority of this frame provider.
+  ///
+  /// Priority determines the order in which providers are evaluated when
+  /// multiple providers could apply to the same thread. Lower numbers indicate
+  /// higher priority (like Unix nice values).
+  ///
+  /// \return
+  ///     Priority value where 0 is highest priority, or std::nullopt for
+  ///     default priority (UINT32_MAX - lowest priority).
+  virtual std::optional<uint32_t> GetPriority() const { return std::nullopt; }
+
   /// Get a single stack frame at the specified index.
   ///
   /// This method is called lazily - frames are only created when requested.
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
index 3dde5036453f4..318d901ca5eda 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
@@ -70,6 +70,24 @@ std::string 
ScriptedFrameProviderPythonInterface::GetDescription(
   return obj->GetStringValue().str();
 }
 
+std::optional<uint32_t> ScriptedFrameProviderPythonInterface::GetPriority(
+    llvm::StringRef class_name) {
+  Status error;
+  StructuredData::ObjectSP obj =
+      CallStaticMethod(class_name, "get_priority", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return std::nullopt;
+
+  // Try to extract as unsigned integer. Return nullopt if Python returned None
+  // or if extraction fails.
+  if (StructuredData::UnsignedInteger *int_obj = obj->GetAsUnsignedInteger())
+    return static_cast<uint32_t>(int_obj->GetValue());
+
+  return std::nullopt;
+}
+
 StructuredData::ObjectSP
 ScriptedFrameProviderPythonInterface::GetFrameAtIndex(uint32_t index) {
   Status error;
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
index 97a5cc7c669ea..884b0355a659e 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
@@ -43,6 +43,8 @@ class ScriptedFrameProviderPythonInterface
 
   std::string GetDescription(llvm::StringRef class_name) override;
 
+  std::optional<uint32_t> GetPriority(llvm::StringRef class_name) override;
+
   StructuredData::ObjectSP GetFrameAtIndex(uint32_t index) override;
 
   static void Initialize();
diff --git 
a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
 
b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
index 739963e6f0c2f..4aad8f2cb628f 100644
--- 
a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
+++ 
b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
@@ -106,6 +106,13 @@ std::string ScriptedFrameProvider::GetDescription() const {
   return m_interface_sp->GetDescription(m_descriptor.GetName());
 }
 
+std::optional<uint32_t> ScriptedFrameProvider::GetPriority() const {
+  if (!m_interface_sp)
+    return std::nullopt;
+
+  return m_interface_sp->GetPriority(m_descriptor.GetName());
+}
+
 llvm::Expected<StackFrameSP>
 ScriptedFrameProvider::GetFrameAtIndex(uint32_t idx) {
   if (!m_interface_sp)
diff --git 
a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h
 
b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h
index 3434bf26ade24..6937f9acbc9a9 100644
--- 
a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h
+++ 
b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h
@@ -40,6 +40,8 @@ class ScriptedFrameProvider : public SyntheticFrameProvider {
 
   std::string GetDescription() const override;
 
+  std::optional<uint32_t> GetPriority() const override;
+
   /// Get a single stack frame at the specified index.
   llvm::Expected<lldb::StackFrameSP> GetFrameAtIndex(uint32_t idx) override;
 
diff --git a/lldb/source/Target/SyntheticFrameProvider.cpp 
b/lldb/source/Target/SyntheticFrameProvider.cpp
index 97ff42d1ed53e..e799ad23b7512 100644
--- a/lldb/source/Target/SyntheticFrameProvider.cpp
+++ b/lldb/source/Target/SyntheticFrameProvider.cpp
@@ -34,6 +34,13 @@ void ScriptedFrameProviderDescriptor::Dump(Stream *s) const {
   if (!description.empty())
     s->Printf("  Description: %s\n", description.c_str());
 
+  // Show priority information.
+  std::optional<uint32_t> priority = GetPriority();
+  if (priority.has_value())
+    s->Printf("  Priority: %u\n", *priority);
+  else
+    s->PutCString("  Priority: Default (no priority specified)\n");
+
   // Show thread filter information.
   if (thread_specs.empty()) {
     s->PutCString("  Thread Filter: (applies to all threads)\n");
@@ -62,6 +69,13 @@ std::string 
ScriptedFrameProviderDescriptor::GetDescription() const {
   return {};
 }
 
+std::optional<uint32_t> ScriptedFrameProviderDescriptor::GetPriority() const {
+  // If we have an interface, call get_priority() to fetch it.
+  if (interface_sp && scripted_metadata_sp)
+    return interface_sp->GetPriority(scripted_metadata_sp->GetClassName());
+  return std::nullopt;
+}
+
 llvm::Expected<SyntheticFrameProviderSP> 
SyntheticFrameProvider::CreateInstance(
     StackFrameListSP input_frames,
     const ScriptedFrameProviderDescriptor &descriptor) {
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index b40e753aca1e9..9c816d6f8d749 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1455,16 +1455,33 @@ StackFrameListSP Thread::GetStackFrameList() {
       Target &target = process_sp->GetTarget();
       const auto &descriptors = target.GetScriptedFrameProviderDescriptors();
 
-      // Find first descriptor that applies to this thread.
+      // Collect all descriptors that apply to this thread.
+      std::vector<const ScriptedFrameProviderDescriptor *> 
applicable_descriptors;
       for (const auto &entry : descriptors) {
         const ScriptedFrameProviderDescriptor &descriptor = entry.second;
         if (descriptor.IsValid() && descriptor.AppliesToThread(*this)) {
-          if (llvm::Error error = LoadScriptedFrameProvider(descriptor)) {
-            LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), std::move(error),
-                           "Failed to load scripted frame provider: {0}");
-          }
-          break; // Use first matching descriptor (success or failure).
+          applicable_descriptors.push_back(&descriptor);
+        }
+      }
+
+      // Sort by priority (lower number = higher priority).
+      std::sort(applicable_descriptors.begin(), applicable_descriptors.end(),
+                [](const ScriptedFrameProviderDescriptor *a,
+                   const ScriptedFrameProviderDescriptor *b) {
+                  // nullopt (no priority) sorts last (UINT32_MAX).
+                  uint32_t priority_a = a->GetPriority().value_or(UINT32_MAX);
+                  uint32_t priority_b = b->GetPriority().value_or(UINT32_MAX);
+                  return priority_a < priority_b;
+                });
+
+      // Load the highest priority provider that successfully instantiates.
+      for (const auto *descriptor : applicable_descriptors) {
+        if (llvm::Error error = LoadScriptedFrameProvider(*descriptor)) {
+          LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), std::move(error),
+                         "Failed to load scripted frame provider: {0}");
+          continue; // Try next provider if this one fails.
         }
+        break; // Successfully loaded provider.
       }
     }
   }

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to