llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Med Ismail Bennani (medismailben) <details> <summary>Changes</summary> 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. ``` --- Full diff: https://github.com/llvm/llvm-project/pull/172848.diff 9 Files Affected: - (modified) lldb/examples/python/templates/scripted_frame_provider.py (+28) - (modified) lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h (+16) - (modified) lldb/include/lldb/Target/SyntheticFrameProvider.h (+21) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp (+18) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h (+2) - (modified) lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp (+7) - (modified) lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h (+2) - (modified) lldb/source/Target/SyntheticFrameProvider.cpp (+14) - (modified) lldb/source/Target/Thread.cpp (+23-6) ``````````diff 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. } } } `````````` </details> https://github.com/llvm/llvm-project/pull/172848 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
