Author: Med Ismail Bennani
Date: 2026-07-16T18:00:26-07:00
New Revision: a07b91d8243b8a8051dbbe5739eacc7942269c61

URL: 
https://github.com/llvm/llvm-project/commit/a07b91d8243b8a8051dbbe5739eacc7942269c61
DIFF: 
https://github.com/llvm/llvm-project/commit/a07b91d8243b8a8051dbbe5739eacc7942269c61.diff

LOG: [lldb/script] Migrate frame recognizers onto ScriptedPythonInterface  
(#209805)

Give `frame recognizer add -l` a formal
ScriptedStackFrameRecognizerInterface, matching the architecture already
used by ScriptedProcess, ScriptedBreakpoint, etc.: a C++ interface
header, a Python-backed implementation built on
ScriptedPythonInterface's CreatePluginObject/Dispatch machinery,
PluginManager registration, and a generatable Python ABC template
(scripted_stackframe_recognizer.py).

get_recognized_arguments' documented contract returns a plain list of
lldb.SBValue (not an lldb.SBValueList), so it bypasses the generic
Dispatch<ValueObjectListSP>() extractor and calls the SWIG bridge
directly, matching the legacy behavior exactly.

ScriptedStackFrameRecognizer now holds a single interface object created
once in its constructor and reused across every RecognizeFrame() call,
same lifecycle as before.

Signed-off-by: Med Ismail Bennani <[email protected]>

Added: 
    lldb/examples/python/templates/scripted_stackframe_recognizer.py
    
lldb/include/lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.h

Modified: 
    lldb/bindings/python/CMakeLists.txt
    lldb/bindings/python/python-wrapper.swig
    lldb/docs/CMakeLists.txt
    lldb/include/lldb/Interpreter/ScriptInterpreter.h
    lldb/include/lldb/Target/StackFrameRecognizer.h
    lldb/include/lldb/lldb-enumerations.h
    lldb/include/lldb/lldb-forward.h
    lldb/source/Interpreter/ScriptInterpreter.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
    lldb/source/Target/StackFrameRecognizer.cpp
    lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
    lldb/test/API/commands/frame/recognizer/recognizer.py
    lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/python/CMakeLists.txt 
b/lldb/bindings/python/CMakeLists.txt
index 9a8a8d420e18a..d29b143c1408c 100644
--- a/lldb/bindings/python/CMakeLists.txt
+++ b/lldb/bindings/python/CMakeLists.txt
@@ -119,6 +119,7 @@ function(finish_swig_python swig_target 
lldb_python_bindings_dir lldb_python_tar
     "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_thread_plan.py"
     "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_breakpoint.py"
     "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_hook.py"
+    
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_stackframe_recognizer.py"
     )
 
   if(APPLE)

diff  --git a/lldb/bindings/python/python-wrapper.swig 
b/lldb/bindings/python/python-wrapper.swig
index dea4f6b4c7f7c..2392737402e20 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -798,53 +798,6 @@ PythonObject 
lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin(
   return pfunc(SWIGBridge::ToSWIGWrapper(process_sp));
 }
 
-PythonObject 
lldb_private::python::SWIGBridge::LLDBSWIGPython_CreateFrameRecognizer(
-    const char *python_class_name, const char *session_dictionary_name) {
-  if (python_class_name == NULL || python_class_name[0] == '\0' ||
-      !session_dictionary_name)
-    return PythonObject();
-
-  PyErr_Cleaner py_err_cleaner(true);
-
-  auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
-      session_dictionary_name);
-  auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
-      python_class_name, dict);
-
-  if (!pfunc.IsAllocated())
-    return PythonObject();
-
-  return pfunc();
-}
-
-PyObject 
*lldb_private::python::SWIGBridge::LLDBSwigPython_GetRecognizedArguments(
-    PyObject *implementor, const lldb::StackFrameSP &frame_sp) {
-  static char callee_name[] = "get_recognized_arguments";
-
-  PythonObject arg = SWIGBridge::ToSWIGWrapper(frame_sp);
-
-  PythonString str(callee_name);
-  PyObject *result =
-      PyObject_CallMethodObjArgs(implementor, str.get(), arg.get(), NULL);
-  return result;
-}
-
-bool lldb_private::python::SWIGBridge::LLDBSwigPython_ShouldHide(
-    PyObject *implementor, const lldb::StackFrameSP &frame_sp) {
-  static char callee_name[] = "should_hide";
-
-  PythonObject arg = SWIGBridge::ToSWIGWrapper(frame_sp);
-
-  PythonString str(callee_name);
-
-  PyObject *result =
-      PyObject_CallMethodObjArgs(implementor, str.get(), arg.get(), NULL);
-  bool ret_val = result ? PyObject_IsTrue(result) : false;
-  Py_XDECREF(result);
-
-  return ret_val;
-}
-
 void *lldb_private::python::SWIGBridge::LLDBSWIGPython_GetDynamicSetting(
     void *module, const char *setting, const lldb::TargetSP &target_sp) {
   if (!module || !setting)

diff  --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt
index 58c740f16bf78..dd091836dc1aa 100644
--- a/lldb/docs/CMakeLists.txt
+++ b/lldb/docs/CMakeLists.txt
@@ -32,6 +32,7 @@ if (LLDB_ENABLE_PYTHON AND SPHINX_FOUND)
       COMMAND "${CMAKE_COMMAND}" -E copy 
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_thread_plan.py" 
"${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/"
       COMMAND "${CMAKE_COMMAND}" -E copy 
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_breakpoint.py" 
"${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/"
       COMMAND "${CMAKE_COMMAND}" -E copy 
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_hook.py" 
"${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/"
+      COMMAND "${CMAKE_COMMAND}" -E copy 
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_stackframe_recognizer.py"
 "${CMAKE_CURRENT_BINARY_DIR}/lldb/plugins/"
       COMMENT "Copying lldb.py to pretend its a Python package.")
 
     add_dependencies(lldb-python-doc-package swig_wrapper_python)

diff  --git a/lldb/examples/python/templates/scripted_stackframe_recognizer.py 
b/lldb/examples/python/templates/scripted_stackframe_recognizer.py
new file mode 100644
index 0000000000000..0c8489f8d6d2d
--- /dev/null
+++ b/lldb/examples/python/templates/scripted_stackframe_recognizer.py
@@ -0,0 +1,102 @@
+from abc import ABCMeta
+from typing import Optional
+
+import lldb
+
+
+class ScriptedStackFrameRecognizer(metaclass=ABCMeta):
+    """
+    The base class for a scripted stack frame recognizer.
+
+    A frame recognizer inspects a stack frame at stop time and can:
+
+    - attach recognized arguments to it (surfaced the same way as args
+      for unrecognized frames);
+    - hide the frame from backtraces;
+    - override the stop description reported for it;
+    - expose an exception object associated with it (e.g. a runtime's
+      thrown value);
+    - redirect auto-selection to a more relevant frame (e.g. bounce past
+      a language-runtime trampoline).
+
+    Register the recognizer with `frame recognizer add -l <ClassName> ...`.
+
+    Every method is optional: a recognizer only implements the ones it
+    cares about. A recognizer that just hides frames can implement only
+    `should_hide`; one that surfaces exception info can implement only
+    `get_exception` and `get_stop_description`.
+    """
+
+    def __init__(self):
+        """Construct a scripted stack frame recognizer.
+
+        Recognizers are constructed with no arguments and are shared across
+        every frame they're asked to recognize.
+        """
+        pass
+
+    def get_recognized_arguments(self, frame: lldb.SBFrame) -> list:
+        """Get the arguments recognized for this frame.
+
+        Args:
+            frame (lldb.SBFrame): The frame to inspect.
+
+        Returns:
+            list of lldb.SBValue: The recognized arguments, or an empty list
+            if none could be recognized.
+        """
+        return []
+
+    def should_hide(self, frame: lldb.SBFrame) -> bool:
+        """Whether this frame should be hidden when displaying backtraces.
+
+        Args:
+            frame (lldb.SBFrame): The frame to inspect.
+
+        Returns:
+            bool: `True` if this frame should be hidden, `False` otherwise.
+            Defaults to `False`.
+        """
+        return False
+
+    def select_most_relevant_frame(self, frame: lldb.SBFrame) -> 
Optional[lldb.SBFrame]:
+        """Pick a 
diff erent frame to auto-select when the process stops in
+        this recognized frame. Useful when the recognized frame is trampoline
+        code that the user probably didn't want to land in (e.g. a language
+        runtime's exception-throw path).
+
+        Args:
+            frame (lldb.SBFrame): The recognized frame.
+
+        Returns:
+            lldb.SBFrame: The frame to select instead, or `None` to keep
+            the default selection.
+        """
+        return None
+
+    def get_exception(self, frame: lldb.SBFrame) -> Optional[lldb.SBValue]:
+        """Get the exception value associated with this recognized frame,
+        if any (e.g. the exception object at a language runtime's throw
+        site).
+
+        Args:
+            frame (lldb.SBFrame): The recognized frame.
+
+        Returns:
+            lldb.SBValue: The exception value, or `None` if this frame
+            doesn't correspond to an exception.
+        """
+        return None
+
+    def get_stop_description(self, frame: lldb.SBFrame) -> str:
+        """Get the stop description to surface for this recognized frame,
+        replacing whatever description lldb would have printed.
+
+        Args:
+            frame (lldb.SBFrame): The recognized frame.
+
+        Returns:
+            str: The stop description, or the empty string to keep the
+            default.
+        """
+        return ""

diff  --git 
a/lldb/include/lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h
 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h
new file mode 100644
index 0000000000000..333973b9f933b
--- /dev/null
+++ 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERINTERFACE_H
+
+#include "ScriptedInterface.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+class ScriptedStackFrameRecognizerInterface : virtual public ScriptedInterface 
{
+public:
+  virtual llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(const ScriptedMetadata &scripted_metadata) = 0;
+
+  virtual lldb::ValueObjectListSP
+  GetRecognizedArguments(lldb::StackFrameSP frame_sp) {
+    return lldb::ValueObjectListSP();
+  }
+
+  virtual bool ShouldHide(lldb::StackFrameSP frame_sp) { return false; }
+
+  virtual lldb::StackFrameSP
+  SelectMostRelevantFrame(lldb::StackFrameSP frame_sp) {
+    return nullptr;
+  }
+
+  virtual lldb::ValueObjectSP GetException(lldb::StackFrameSP frame_sp) {
+    return lldb::ValueObjectSP();
+  }
+
+  virtual std::string GetStopDescription(lldb::StackFrameSP frame_sp) {
+    return "";
+  }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERINTERFACE_H

diff  --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h 
b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 2b9cb8f7bb866..122e779bf0279 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -248,22 +248,6 @@ class ScriptInterpreter : public PluginInterface {
     return StructuredData::GenericSP();
   }
 
-  virtual StructuredData::GenericSP
-  CreateFrameRecognizer(const char *class_name) {
-    return StructuredData::GenericSP();
-  }
-
-  virtual lldb::ValueObjectListSP GetRecognizedArguments(
-      const StructuredData::ObjectSP &implementor,
-      lldb::StackFrameSP frame_sp) {
-    return lldb::ValueObjectListSP();
-  }
-
-  virtual bool ShouldHide(const StructuredData::ObjectSP &implementor,
-                          lldb::StackFrameSP frame_sp) {
-    return false;
-  }
-
   virtual StructuredData::ObjectSP
   LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) {
     return StructuredData::ObjectSP();
@@ -572,6 +556,11 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
+  virtual lldb::ScriptedStackFrameRecognizerInterfaceSP
+  CreateScriptedStackFrameRecognizerInterface() {
+    return {};
+  }
+
   virtual StructuredData::ObjectSP
   CreateStructuredDataFromScriptObject(ScriptObject obj) {
     return {};

diff  --git a/lldb/include/lldb/Target/StackFrameRecognizer.h 
b/lldb/include/lldb/Target/StackFrameRecognizer.h
index 97a35778ae070..95e8a03cac96c 100644
--- a/lldb/include/lldb/Target/StackFrameRecognizer.h
+++ b/lldb/include/lldb/Target/StackFrameRecognizer.h
@@ -77,8 +77,7 @@ class StackFrameRecognizer
 /// tracks a particular Python classobject, which will be asked to recognize
 /// stack frames.
 class ScriptedStackFrameRecognizer : public StackFrameRecognizer {
-  lldb_private::ScriptInterpreter *m_interpreter;
-  lldb_private::StructuredData::ObjectSP m_python_object_sp;
+  lldb::ScriptedStackFrameRecognizerInterfaceSP m_interface_sp;
 
   std::string m_python_class;
 

diff  --git a/lldb/include/lldb/lldb-enumerations.h 
b/lldb/include/lldb/lldb-enumerations.h
index b36ad09f2e892..93c252b55de99 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -267,7 +267,8 @@ enum ScriptedExtension {
   eScriptedExtensionScriptedHook,
   eScriptedExtensionScriptedThread,
   eScriptedExtensionScriptedFrame,
-  kLastScriptedExtension = eScriptedExtensionScriptedFrame
+  eScriptedExtensionScriptedStackFrameRecognizer,
+  kLastScriptedExtension = eScriptedExtensionScriptedStackFrameRecognizer
 };
 
 /// Register numbering types.

diff  --git a/lldb/include/lldb/lldb-forward.h 
b/lldb/include/lldb/lldb-forward.h
index 6dbc095f15ef6..157aa5743f016 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -198,6 +198,7 @@ class ScriptedPlatformInterface;
 class ScriptedProcessInterface;
 class ScriptedThreadInterface;
 class ScriptedThreadPlanInterface;
+class ScriptedStackFrameRecognizerInterface;
 class ScriptedSyntheticChildren;
 class SearchFilter;
 class Section;
@@ -435,6 +436,8 @@ typedef 
std::shared_ptr<lldb_private::ScriptedThreadPlanInterface>
     ScriptedThreadPlanInterfaceSP;
 typedef std::shared_ptr<lldb_private::ScriptedBreakpointInterface>
     ScriptedBreakpointInterfaceSP;
+typedef std::shared_ptr<lldb_private::ScriptedStackFrameRecognizerInterface>
+    ScriptedStackFrameRecognizerInterfaceSP;
 typedef std::shared_ptr<lldb_private::Section> SectionSP;
 typedef std::unique_ptr<lldb_private::SectionList> SectionListUP;
 typedef std::weak_ptr<lldb_private::Section> SectionWP;

diff  --git a/lldb/source/Interpreter/ScriptInterpreter.cpp 
b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 8420782b02814..6436d8161583d 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -212,6 +212,8 @@ 
ScriptInterpreter::ExtensionToString(lldb::ScriptedExtension extension) {
     return "ScriptedThread";
   case eScriptedExtensionScriptedFrame:
     return "ScriptedFrame";
+  case eScriptedExtensionScriptedStackFrameRecognizer:
+    return "ScriptedStackFrameRecognizer";
   }
   llvm_unreachable("unhandled ScriptedExtension");
 }
@@ -230,6 +232,8 @@ ScriptInterpreter::StringToExtension(llvm::StringRef 
string) {
       .CaseLower("ScriptedHook", eScriptedExtensionScriptedHook)
       .CaseLower("ScriptedThread", eScriptedExtensionScriptedThread)
       .CaseLower("ScriptedFrame", eScriptedExtensionScriptedFrame)
+      .CaseLower("ScriptedStackFrameRecognizer",
+                 eScriptedExtensionScriptedStackFrameRecognizer)
       .Default(eScriptedExtensionInvalid);
 }
 

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt 
b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
index fb7d0cb82f8ca..201574da72a19 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
@@ -32,6 +32,7 @@ set(python_plugin_sources
   Interfaces/ScriptedPythonInterface.cpp
   Interfaces/ScriptedHookPythonInterface.cpp
   Interfaces/ScriptedBreakpointPythonInterface.cpp
+  Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
   Interfaces/ScriptedThreadPlanPythonInterface.cpp
   Interfaces/ScriptedThreadPythonInterface.cpp
 )

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
index cdbb624706f83..8914f6b239023 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
@@ -29,6 +29,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() {
   ScriptedFrameProviderPythonInterface::Initialize();
   ScriptedThreadPythonInterface::Initialize();
   ScriptedFramePythonInterface::Initialize();
+  ScriptedStackFrameRecognizerPythonInterface::Initialize();
 }
 
 void ScriptInterpreterPythonInterfaces::Terminate() {
@@ -41,4 +42,5 @@ void ScriptInterpreterPythonInterfaces::Terminate() {
   ScriptedFrameProviderPythonInterface::Terminate();
   ScriptedThreadPythonInterface::Terminate();
   ScriptedFramePythonInterface::Terminate();
+  ScriptedStackFrameRecognizerPythonInterface::Terminate();
 }

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 9f6324d0503f7..03d747e63a592 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -19,6 +19,7 @@
 #include "ScriptedHookPythonInterface.h"
 #include "ScriptedPlatformPythonInterface.h"
 #include "ScriptedProcessPythonInterface.h"
+#include "ScriptedStackFrameRecognizerPythonInterface.h"
 #include "ScriptedThreadPlanPythonInterface.h"
 #include "ScriptedThreadPythonInterface.h"
 

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index 4612b1939b5db..61ceb40dd9d32 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -305,22 +305,57 @@ template <>
 lldb::ValueObjectListSP
 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>(
     python::PythonObject &p, Status &error) {
-  lldb::SBValueList *sb_value_list = reinterpret_cast<lldb::SBValueList *>(
-      python::LLDBSWIGPython_CastPyObjectToSBValueList(p.get()));
-
-  if (!sb_value_list) {
-    error = Status::FromErrorStringWithFormat(
-        "couldn't cast lldb::SBValueList to lldb::ValueObjectListSP");
-    return {};
+  // Two Python return shapes are accepted here so callers can go through
+  // Dispatch<ValueObjectListSP>() uniformly: an `SBValueList` wrapper
+  // (what most extension methods return) and a plain Python `list` of
+  // `SBValue` (what `get_recognized_arguments` is documented to return).
+  lldb::ValueObjectListSP out = std::make_shared<ValueObjectList>();
+  if (auto *sb_value_list = reinterpret_cast<lldb::SBValueList *>(
+          python::LLDBSWIGPython_CastPyObjectToSBValueList(p.get()))) {
+    for (uint32_t i = 0, e = sb_value_list->GetSize(); i < e; ++i) {
+      SBValue value = sb_value_list->GetValueAtIndex(i);
+      out->Append(m_interpreter.GetOpaqueTypeFromSBValue(value));
+    }
+    return out;
   }
 
-  lldb::ValueObjectListSP out = std::make_shared<ValueObjectList>();
-  for (uint32_t i = 0, e = sb_value_list->GetSize(); i < e; ++i) {
-    SBValue value = sb_value_list->GetValueAtIndex(i);
-    out->Append(m_interpreter.GetOpaqueTypeFromSBValue(value));
+  // Fallback: a plain Python `list` of `SBValue`. Round-trip through
+  // `CreateStructuredObject` so we don't touch the `PyList_*` C API
+  // directly; unknown-shape items surface as `StructuredData::Generic`
+  // holding their opaque `PyObject*`, which we hand back to SWIG to
+  // recover the SBValue wrapper.
+  StructuredData::ObjectSP structured = p.CreateStructuredObject();
+  StructuredData::Array *arr = structured ? structured->GetAsArray() : nullptr;
+  if (arr) {
+    size_t index = 0;
+    bool aborted = false;
+    arr->ForEach([&](StructuredData::Object *item) {
+      StructuredData::Generic *generic = item ? item->GetAsGeneric() : nullptr;
+      if (!generic) {
+        error = Status::FromErrorStringWithFormatv(
+            "ValueObjectList item at index {0} is not a "
+            "StructuredData::Generic",
+            index);
+        aborted = true;
+        return false;
+      }
+      auto *sb_value = reinterpret_cast<lldb::SBValue *>(
+          python::LLDBSWIGPython_CastPyObjectToSBValue(
+              static_cast<PyObject *>(generic->GetValue())));
+      if (sb_value)
+        if (auto valobj_sp = m_interpreter.GetOpaqueTypeFromSBValue(*sb_value))
+          out->Append(valobj_sp);
+      ++index;
+      return true;
+    });
+    if (aborted)
+      return {};
+    return out;
   }
 
-  return out;
+  error = Status::FromErrorStringWithFormat(
+      "couldn't extract ValueObjectList from Python return value");
+  return {};
 }
 
 template <>

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
new file mode 100644
index 0000000000000..2e05928a2ad57
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../lldb-python.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedStackFrameRecognizerPythonInterface.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+
+ScriptedStackFrameRecognizerPythonInterface::
+    ScriptedStackFrameRecognizerPythonInterface(
+        ScriptInterpreterPythonImpl &interpreter)
+    : ScriptedStackFrameRecognizerInterface(),
+      ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedStackFrameRecognizerPythonInterface::CreatePluginObject(
+    const ScriptedMetadata &scripted_metadata) {
+  return ScriptedPythonInterface::CreatePluginObject(scripted_metadata,
+                                                     nullptr);
+}
+
+lldb::ValueObjectListSP
+ScriptedStackFrameRecognizerPythonInterface::GetRecognizedArguments(
+    lldb::StackFrameSP frame_sp) {
+  Status error;
+  return Dispatch<lldb::ValueObjectListSP>("get_recognized_arguments", error,
+                                           frame_sp);
+}
+
+bool ScriptedStackFrameRecognizerPythonInterface::ShouldHide(
+    lldb::StackFrameSP frame_sp) {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("should_hide", error, frame_sp);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+lldb::StackFrameSP
+ScriptedStackFrameRecognizerPythonInterface::SelectMostRelevantFrame(
+    lldb::StackFrameSP frame_sp) {
+  Status error;
+  return Dispatch<lldb::StackFrameSP>("select_most_relevant_frame", error,
+                                      frame_sp);
+}
+
+lldb::ValueObjectSP ScriptedStackFrameRecognizerPythonInterface::GetException(
+    lldb::StackFrameSP frame_sp) {
+  Status error;
+  return Dispatch<lldb::ValueObjectSP>("get_exception", error, frame_sp);
+}
+
+std::string ScriptedStackFrameRecognizerPythonInterface::GetStopDescription(
+    lldb::StackFrameSP frame_sp) {
+  Status error;
+  StructuredData::ObjectSP obj =
+      Dispatch("get_stop_description", error, frame_sp);
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return "";
+  return obj->GetStringValue().str();
+}
+
+void ScriptedStackFrameRecognizerPythonInterface::Initialize() {
+  const std::vector<llvm::StringRef> ci_usages = {
+      "frame recognizer add -l <script-name> [-s <shlib> ...] "
+      "[-n <symbol> ... | -x <symbol-regex>] [-f false] "
+      "[--mangled-name-preference <mode>]"};
+  PluginManager::RegisterPlugin(
+      GetPluginNameStatic(),
+      "Recognize a stack frame and provide extra information about it "
+      "(recognized arguments, exception object, stop description, "
+      "hidden/most-relevant frame).",
+      CreateInstance, eScriptedExtensionScriptedStackFrameRecognizer,
+      eScriptLanguagePython, {ci_usages, {}});
+}
+
+void ScriptedStackFrameRecognizerPythonInterface::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.h
new file mode 100644
index 0000000000000..eaf4116ab54c0
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStackFrameRecognizerPythonInterface.h
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef 
LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERPYTHONINTERFACE_H
+#define 
LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERPYTHONINTERFACE_H
+
+#include "lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h"
+
+#include "ScriptedPythonInterface.h"
+namespace lldb_private {
+
+class ScriptedStackFrameRecognizerPythonInterface
+    : public ScriptedStackFrameRecognizerInterface,
+      public ScriptedPythonInterface,
+      public PluginInterface {
+public:
+  ScriptedStackFrameRecognizerPythonInterface(
+      ScriptInterpreterPythonImpl &interpreter);
+
+  llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(const ScriptedMetadata &scripted_metadata) override;
+
+  llvm::SmallVector<AbstractMethodRequirement>
+  GetAbstractMethodRequirements() const override {
+    return {};
+  }
+
+  lldb::ValueObjectListSP
+  GetRecognizedArguments(lldb::StackFrameSP frame_sp) override;
+
+  bool ShouldHide(lldb::StackFrameSP frame_sp) override;
+
+  lldb::StackFrameSP
+  SelectMostRelevantFrame(lldb::StackFrameSP frame_sp) override;
+
+  lldb::ValueObjectSP GetException(lldb::StackFrameSP frame_sp) override;
+
+  std::string GetStopDescription(lldb::StackFrameSP frame_sp) override;
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() {
+    return "ScriptedStackFrameRecognizerPythonInterface";
+  }
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+};
+} // namespace lldb_private
+
+#endif // 
LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSTACKFRAMERECOGNIZERPYTHONINTERFACE_H

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h 
b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index ce9e12dbf7d3d..07e0da1dcf70d 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -214,17 +214,6 @@ class SWIGBridge {
                                const char *session_dictionary_name,
                                const lldb::ProcessSP &process_sp);
 
-  static python::PythonObject
-  LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name,
-                                       const char *session_dictionary_name);
-
-  static PyObject *
-  LLDBSwigPython_GetRecognizedArguments(PyObject *implementor,
-                                        const lldb::StackFrameSP &frame_sp);
-
-  static bool LLDBSwigPython_ShouldHide(PyObject *implementor,
-                                        const lldb::StackFrameSP &frame_sp);
-
   static bool LLDBSWIGPythonRunScriptKeywordProcess(
       const char *python_function_name, const char *session_dictionary_name,
       const lldb::ProcessSP &process, std::string &output);

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index f3f2b801d43e2..a153563b4534b 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1624,92 +1624,6 @@ bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
   return true;
 }
 
-StructuredData::GenericSP
-ScriptInterpreterPythonImpl::CreateFrameRecognizer(const char *class_name) {
-  if (class_name == nullptr || class_name[0] == '\0')
-    return StructuredData::GenericSP();
-
-  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
-  PythonObject ret_val = SWIGBridge::LLDBSWIGPython_CreateFrameRecognizer(
-      class_name, m_dictionary_name.c_str());
-
-  return StructuredData::GenericSP(
-      new StructuredPythonObject(std::move(ret_val)));
-}
-
-lldb::ValueObjectListSP ScriptInterpreterPythonImpl::GetRecognizedArguments(
-    const StructuredData::ObjectSP &os_plugin_object_sp,
-    lldb::StackFrameSP frame_sp) {
-  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
-
-  if (!os_plugin_object_sp)
-    return ValueObjectListSP();
-
-  StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
-  if (!generic)
-    return nullptr;
-
-  PythonObject implementor(PyRefType::Borrowed,
-                           (PyObject *)generic->GetValue());
-
-  if (!implementor.IsAllocated())
-    return ValueObjectListSP();
-
-  PythonObject py_return(PyRefType::Owned,
-                         SWIGBridge::LLDBSwigPython_GetRecognizedArguments(
-                             implementor.get(), frame_sp));
-
-  // if it fails, print the error but otherwise go on
-  if (PyErr_Occurred()) {
-    PyErr_Print();
-    PyErr_Clear();
-  }
-  if (py_return.get()) {
-    PythonList result_list(PyRefType::Borrowed, py_return.get());
-    ValueObjectListSP result = std::make_shared<ValueObjectList>();
-    for (size_t i = 0; i < result_list.GetSize(); i++) {
-      PyObject *item = result_list.GetItemAtIndex(i).get();
-      lldb::SBValue *sb_value_ptr =
-          (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(item);
-      auto valobj_sp =
-          SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
-      if (valobj_sp)
-        result->Append(valobj_sp);
-    }
-    return result;
-  }
-  return ValueObjectListSP();
-}
-
-bool ScriptInterpreterPythonImpl::ShouldHide(
-    const StructuredData::ObjectSP &os_plugin_object_sp,
-    lldb::StackFrameSP frame_sp) {
-  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
-
-  if (!os_plugin_object_sp)
-    return false;
-
-  StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
-  if (!generic)
-    return false;
-
-  PythonObject implementor(PyRefType::Borrowed,
-                           (PyObject *)generic->GetValue());
-
-  if (!implementor.IsAllocated())
-    return false;
-
-  bool result =
-      SWIGBridge::LLDBSwigPython_ShouldHide(implementor.get(), frame_sp);
-
-  // if it fails, print the error but otherwise go on
-  if (PyErr_Occurred()) {
-    PyErr_Print();
-    PyErr_Clear();
-  }
-  return result;
-}
-
 ScriptedProcessInterfaceUP
 ScriptInterpreterPythonImpl::CreateScriptedProcessInterface() {
   return std::make_unique<ScriptedProcessPythonInterface>(*this);
@@ -1725,6 +1639,11 @@ 
ScriptInterpreterPythonImpl::CreateScriptedBreakpointInterface() {
   return std::make_shared<ScriptedBreakpointPythonInterface>(*this);
 }
 
+ScriptedStackFrameRecognizerInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedStackFrameRecognizerInterface() {
+  return std::make_shared<ScriptedStackFrameRecognizerPythonInterface>(*this);
+}
+
 ScriptedThreadInterfaceSP
 ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
   return std::make_shared<ScriptedThreadPythonInterface>(*this);

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 353332a784e60..d8a817198253f 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -76,16 +76,6 @@ class ScriptInterpreterPythonImpl : public 
ScriptInterpreterPython {
   StructuredData::ObjectSP
   CreateStructuredDataFromScriptObject(ScriptObject obj) override;
 
-  StructuredData::GenericSP
-  CreateFrameRecognizer(const char *class_name) override;
-
-  lldb::ValueObjectListSP
-  GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
-                         lldb::StackFrameSP frame_sp) override;
-
-  bool ShouldHide(const StructuredData::ObjectSP &implementor,
-                  lldb::StackFrameSP frame_sp) override;
-
   lldb::ScriptedProcessInterfaceUP CreateScriptedProcessInterface() override;
 
   lldb::ScriptedHookInterfaceSP CreateScriptedHookInterface() override;
@@ -93,6 +83,9 @@ class ScriptInterpreterPythonImpl : public 
ScriptInterpreterPython {
   lldb::ScriptedBreakpointInterfaceSP
   CreateScriptedBreakpointInterface() override;
 
+  lldb::ScriptedStackFrameRecognizerInterfaceSP
+  CreateScriptedStackFrameRecognizerInterface() override;
+
   lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
 
   lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;

diff  --git a/lldb/source/Target/StackFrameRecognizer.cpp 
b/lldb/source/Target/StackFrameRecognizer.cpp
index f297c71ff44d7..589f3805e3a67 100644
--- a/lldb/source/Target/StackFrameRecognizer.cpp
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -8,39 +8,62 @@
 
 #include "lldb/Target/StackFrameRecognizer.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 
 using namespace lldb;
 using namespace lldb_private;
 
 class ScriptedRecognizedStackFrame : public RecognizedStackFrame {
   bool m_hidden;
+  lldb::StackFrameSP m_most_relevant_frame;
+  lldb::ValueObjectSP m_exception;
 
 public:
-  ScriptedRecognizedStackFrame(ValueObjectListSP args, bool hidden)
-      : m_hidden(hidden) {
+  ScriptedRecognizedStackFrame(ValueObjectListSP args, bool hidden,
+                               lldb::StackFrameSP most_relevant_frame,
+                               lldb::ValueObjectSP exception,
+                               std::string stop_desc)
+      : m_hidden(hidden), 
m_most_relevant_frame(std::move(most_relevant_frame)),
+        m_exception(std::move(exception)) {
     m_arguments = std::move(args);
+    m_stop_desc = std::move(stop_desc);
   }
   bool ShouldHide() override { return m_hidden; }
+  lldb::StackFrameSP GetMostRelevantFrame() override {
+    return m_most_relevant_frame;
+  }
+  lldb::ValueObjectSP GetExceptionObject() override { return m_exception; }
 };
 
 ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer(
     ScriptInterpreter *interpreter, const char *pclass)
-    : m_interpreter(interpreter), m_python_class(pclass) {
-  m_python_object_sp =
-      m_interpreter->CreateFrameRecognizer(m_python_class.c_str());
+    : m_python_class(pclass) {
+  if (!interpreter)
+    return;
+
+  m_interface_sp = interpreter->CreateScriptedStackFrameRecognizerInterface();
+  if (!m_interface_sp)
+    return;
+
+  ScriptedMetadata scripted_metadata(m_python_class, nullptr);
+  auto obj_or_err = m_interface_sp->CreatePluginObject(scripted_metadata);
+  if (!obj_or_err) {
+    llvm::consumeError(obj_or_err.takeError());
+    m_interface_sp.reset();
+  }
 }
 
 RecognizedStackFrameSP
 ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
-  if (!m_python_object_sp || !m_interpreter)
+  if (!m_interface_sp)
     return RecognizedStackFrameSP();
 
-  ValueObjectListSP args =
-      m_interpreter->GetRecognizedArguments(m_python_object_sp, frame);
+  ValueObjectListSP args = m_interface_sp->GetRecognizedArguments(frame);
   auto args_synthesized = std::make_shared<ValueObjectList>();
   if (args) {
     for (const auto &o : args->GetObjects())
@@ -48,10 +71,15 @@ 
ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
           *o, eValueTypeVariableArgument));
   }
 
-  bool hidden = m_interpreter->ShouldHide(m_python_object_sp, frame);
+  bool hidden = m_interface_sp->ShouldHide(frame);
+  lldb::StackFrameSP most_relevant =
+      m_interface_sp->SelectMostRelevantFrame(frame);
+  lldb::ValueObjectSP exception = m_interface_sp->GetException(frame);
+  std::string stop_desc = m_interface_sp->GetStopDescription(frame);
 
-  return RecognizedStackFrameSP(
-      new ScriptedRecognizedStackFrame(args_synthesized, hidden));
+  return RecognizedStackFrameSP(new ScriptedRecognizedStackFrame(
+      args_synthesized, hidden, std::move(most_relevant), std::move(exception),
+      std::move(stop_desc)));
 }
 
 void StackFrameRecognizerManager::BumpGeneration() {

diff  --git a/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py 
b/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
index 68df086143585..9e3142a691c72 100644
--- a/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
+++ b/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
@@ -222,6 +222,46 @@ def test_frame_recognizer_hiding(self):
         frame = thread.GetSelectedFrame()
         self.assertIn("main", frame.name)
 
+    def test_frame_recognizer_optional_hooks(self):
+        """Exercise select_most_relevant_frame, get_exception, and
+        get_stop_description via a recognizer that implements all three.
+        Each hook records the frame name it saw in a class-level list so
+        the test can assert the hook actually fired."""
+        self.build()
+
+        target, process, thread, _ = lldbutil.run_to_name_breakpoint(self, 
"nested")
+
+        self.expect("frame recognizer clear")
+        self.expect(
+            "command script import "
+            + os.path.join(self.getSourceDir(), "recognizer.py")
+        )
+
+        # Reset the call trackers to isolate this test's activity from
+        # anything the recognizer might have observed earlier.
+        import recognizer
+
+        
recognizer.NestedFrameRecognizer.select_most_relevant_frame_calls.clear()
+        recognizer.NestedFrameRecognizer.get_exception_calls.clear()
+        recognizer.NestedFrameRecognizer.get_stop_description_calls.clear()
+
+        self.expect(
+            "frame recognizer add -l recognizer.NestedFrameRecognizer "
+            "-f false -s a.out -n nested"
+        )
+
+        # Backtracing triggers recognition on each frame lldb walks.
+        self.runCmd("thread backtrace")
+
+        self.assertIn(
+            "nested", 
recognizer.NestedFrameRecognizer.select_most_relevant_frame_calls
+        )
+        self.assertIn("nested", 
recognizer.NestedFrameRecognizer.get_exception_calls)
+        self.assertIn(
+            "nested",
+            recognizer.NestedFrameRecognizer.get_stop_description_calls,
+        )
+
     def test_frame_recognizer_multi_symbol(self):
         self.build()
         exe = self.getBuildArtifact("a.out")

diff  --git a/lldb/test/API/commands/frame/recognizer/recognizer.py 
b/lldb/test/API/commands/frame/recognizer/recognizer.py
index 98666b720b1e2..e7ab03e18bbc4 100644
--- a/lldb/test/API/commands/frame/recognizer/recognizer.py
+++ b/lldb/test/API/commands/frame/recognizer/recognizer.py
@@ -3,7 +3,7 @@
 import lldb
 
 
-class MyFrameRecognizer(object):
+class MyFrameRecognizer:
     def get_recognized_arguments(self, frame):
         if frame.name == "foo":
             arg1 = frame.EvaluateExpression("$arg1").signed
@@ -33,11 +33,38 @@ def get_recognized_arguments(self, frame):
         return []
 
 
-class MyOtherFrameRecognizer(object):
+class MyOtherFrameRecognizer:
     def get_recognized_arguments(self, frame):
         return []
 
 
-class BazFrameRecognizer(object):
+class BazFrameRecognizer:
     def should_hide(self, frame):
         return "baz" in frame.name
+
+
+class NestedFrameRecognizer:
+    """Exercises the optional hooks beyond `should_hide`: replace the
+    stop description, redirect the selected frame, and expose a synthetic
+    exception object. Tracks invocation via class-level lists so a test
+    can assert each hook actually fires.
+    """
+
+    select_most_relevant_frame_calls = []
+    get_exception_calls = []
+    get_stop_description_calls = []
+
+    def select_most_relevant_frame(self, frame):
+        self.select_most_relevant_frame_calls.append(frame.name)
+        # Redirect from `nested` (frame 0) to its caller `baz` (frame 1).
+        return frame.thread.frames[1] if frame.thread.num_frames > 1 else None
+
+    def get_exception(self, frame):
+        self.get_exception_calls.append(frame.name)
+        return frame.thread.process.target.CreateValueFromExpression(
+            "recognized_exception", "42"
+        )
+
+    def get_stop_description(self, frame):
+        self.get_stop_description_calls.append(frame.name)
+        return "recognized nested()"

diff  --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp 
b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 1ed6bee384a84..c9298191ec3c1 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -260,18 +260,6 @@ 
lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin(
   return python::PythonObject();
 }
 
-python::PythonObject
-lldb_private::python::SWIGBridge::LLDBSWIGPython_CreateFrameRecognizer(
-    const char *python_class_name, const char *session_dictionary_name) {
-  return python::PythonObject();
-}
-
-PyObject *
-lldb_private::python::SWIGBridge::LLDBSwigPython_GetRecognizedArguments(
-    PyObject *implementor, const lldb::StackFrameSP &frame_sp) {
-  return nullptr;
-}
-
 bool lldb_private::python::SWIGBridge::LLDBSWIGPythonRunScriptKeywordProcess(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::ProcessSP &process, std::string &output) {


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

Reply via email to