Author: Med Ismail Bennani
Date: 2025-09-04T15:07:11-07:00
New Revision: 84b56202fbe150e06f92c855107489e08cc17bcd

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

LOG: [lldb] Introduce ScriptedFrame affordance (#149622)

This patch introduces a new scripting affordance in lldb:
`ScriptedFrame`.

This allows user to produce mock stackframes in scripted threads and
scripted processes from a python script.

With this change, StackFrame can be synthetized from different sources:
- Either from a dictionary containing a load address, and a frame index,
  which is the legacy way.
- Or by creating a ScriptedFrame python object.

One particularity of synthezising stackframes from the ScriptedFrame
python object, is that these frame have an optional PC, meaning that
they don't have a report a valid PC and they can act as shells that just
contain static information, like the frame function name, the list of
variables or registers, etc. It can also provide a symbol context.

rdar://157260006

Signed-off-by: Med Ismail Bennani <ism...@bennani.ma>

Signed-off-by: Med Ismail Bennani <ism...@bennani.ma>

Added: 
    lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
    lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
    lldb/source/Plugins/Process/scripted/ScriptedFrame.h
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h

Modified: 
    lldb/examples/python/templates/scripted_process.py
    lldb/include/lldb/API/SBSymbolContext.h
    lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
    lldb/include/lldb/Interpreter/ScriptInterpreter.h
    lldb/include/lldb/Target/StackFrame.h
    lldb/include/lldb/lldb-forward.h
    lldb/source/Core/FormatEntity.cpp
    lldb/source/Plugins/Process/scripted/CMakeLists.txt
    lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
    lldb/source/Plugins/Process/scripted/ScriptedThread.h
    lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
    
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
    lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py

Removed: 
    


################################################################################
diff  --git a/lldb/examples/python/templates/scripted_process.py 
b/lldb/examples/python/templates/scripted_process.py
index b6360b8519077..49059d533f38a 100644
--- a/lldb/examples/python/templates/scripted_process.py
+++ b/lldb/examples/python/templates/scripted_process.py
@@ -383,6 +383,142 @@ def get_extended_info(self):
         """
         return self.extended_info
 
+    def get_scripted_frame_plugin(self):
+        """Get scripted frame plugin name.
+
+        Returns:
+            str: Name of the scripted frame plugin.
+        """
+        return None
+
+
+class ScriptedFrame(metaclass=ABCMeta):
+    """
+    The base class for a scripted frame.
+
+    Most of the base class methods are `@abstractmethod` that need to be
+    overwritten by the inheriting class.
+    """
+
+    @abstractmethod
+    def __init__(self, thread, args):
+        """Construct a scripted frame.
+
+        Args:
+            thread (ScriptedThread): The thread owning this frame.
+            args (lldb.SBStructuredData): A Dictionary holding arbitrary
+                key/value pairs used by the scripted frame.
+        """
+        self.target = None
+        self.originating_thread = None
+        self.thread = None
+        self.args = None
+        self.id = None
+        self.name = None
+        self.register_info = None
+        self.register_ctx = {}
+        self.variables = []
+
+        if (
+            isinstance(thread, ScriptedThread)
+            or isinstance(thread, lldb.SBThread)
+            and thread.IsValid()
+        ):
+            self.target = thread.target
+            self.process = thread.process
+            self.originating_thread = thread
+            self.thread = self.process.GetThreadByIndexID(thread.tid)
+            self.get_register_info()
+
+    @abstractmethod
+    def get_id(self):
+        """Get the scripted frame identifier.
+
+        Returns:
+            int: The identifier of the scripted frame in the scripted thread.
+        """
+        pass
+
+    def get_pc(self):
+        """Get the scripted frame address.
+
+        Returns:
+            int: The optional address of the scripted frame in the scripted 
thread.
+        """
+        return None
+
+    def get_symbol_context(self):
+        """Get the scripted frame symbol context.
+
+        Returns:
+            lldb.SBSymbolContext: The symbol context of the scripted frame in 
the scripted thread.
+        """
+        return None
+
+    def is_inlined(self):
+        """Check if the scripted frame is inlined.
+
+        Returns:
+            bool: True if scripted frame is inlined. False otherwise.
+        """
+        return False
+
+    def is_artificial(self):
+        """Check if the scripted frame is artificial.
+
+        Returns:
+            bool: True if scripted frame is artificial. False otherwise.
+        """
+        return True
+
+    def is_hidden(self):
+        """Check if the scripted frame is hidden.
+
+        Returns:
+            bool: True if scripted frame is hidden. False otherwise.
+        """
+        return False
+
+    def get_function_name(self):
+        """Get the scripted frame function name.
+
+        Returns:
+            str: The function name of the scripted frame.
+        """
+        return self.name
+
+    def get_display_function_name(self):
+        """Get the scripted frame display function name.
+
+        Returns:
+            str: The display function name of the scripted frame.
+        """
+        return self.get_function_name()
+
+    def get_variables(self, filters):
+        """Get the scripted thread state type.
+
+        Args:
+            filter (lldb.SBVariablesOptions): The filter used to resolve the 
variables
+        Returns:
+            lldb.SBValueList: The SBValueList containing the SBValue for each 
resolved variable.
+                              Returns None by default.
+        """
+        return None
+
+    def get_register_info(self):
+        if self.register_info is None:
+            self.register_info = self.originating_thread.get_register_info()
+        return self.register_info
+
+    @abstractmethod
+    def get_register_context(self):
+        """Get the scripted thread register context
+
+        Returns:
+            str: A byte representing all register's value.
+        """
+        pass
 
 class PassthroughScriptedProcess(ScriptedProcess):
     driving_target = None

diff  --git a/lldb/include/lldb/API/SBSymbolContext.h 
b/lldb/include/lldb/API/SBSymbolContext.h
index 128b0b65b7860..19f29c629d094 100644
--- a/lldb/include/lldb/API/SBSymbolContext.h
+++ b/lldb/include/lldb/API/SBSymbolContext.h
@@ -66,6 +66,7 @@ class LLDB_API SBSymbolContext {
   friend class SBTarget;
   friend class SBSymbolContextList;
 
+  friend class lldb_private::ScriptInterpreter;
   friend class lldb_private::python::SWIGBridge;
 
   SBSymbolContext(const lldb_private::SymbolContext &sc_ptr);

diff  --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
new file mode 100644
index 0000000000000..8ef4b37d6ba12
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SCRIPTEDFRAMEINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
+
+#include "ScriptedInterface.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/lldb-private.h"
+#include <optional>
+#include <string>
+
+namespace lldb_private {
+class ScriptedFrameInterface : virtual public ScriptedInterface {
+public:
+  virtual llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+                     StructuredData::DictionarySP args_sp,
+                     StructuredData::Generic *script_obj = nullptr) = 0;
+
+  virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; }
+
+  virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; }
+
+  virtual std::optional<SymbolContext> GetSymbolContext() {
+    return std::nullopt;
+  }
+
+  virtual std::optional<std::string> GetFunctionName() { return std::nullopt; }
+
+  virtual std::optional<std::string> GetDisplayFunctionName() {
+    return std::nullopt;
+  }
+
+  virtual bool IsInlined() { return false; }
+
+  virtual bool IsArtificial() { return false; }
+
+  virtual bool IsHidden() { return false; }
+
+  virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; }
+
+  virtual std::optional<std::string> GetRegisterContext() {
+    return std::nullopt;
+  }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H

diff  --git 
a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
index a7cfc690b67dc..bc58f344d36f8 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
@@ -44,6 +44,16 @@ class ScriptedThreadInterface : virtual public 
ScriptedInterface {
   }
 
   virtual StructuredData::ArraySP GetExtendedInfo() { return {}; }
+
+  virtual std::optional<std::string> GetScriptedFramePluginName() {
+    return std::nullopt;
+  }
+
+protected:
+  friend class ScriptedFrame;
+  virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+    return {};
+  }
 };
 } // namespace lldb_private
 

diff  --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h 
b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index dffb9b82abf3d..024bbc90a9a39 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -26,6 +26,7 @@
 #include "lldb/Host/PseudoTerminal.h"
 #include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -531,6 +532,10 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
+  virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+    return {};
+  }
+
   virtual lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() {
     return {};

diff  --git a/lldb/include/lldb/Target/StackFrame.h 
b/lldb/include/lldb/Target/StackFrame.h
index 4ffbf97ce6e90..cdbe8ae3c6779 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -398,7 +398,7 @@ class StackFrame : public ExecutionContextScope,
   ///
   /// \return
   ///   true if this is an inlined frame.
-  bool IsInlined();
+  virtual bool IsInlined();
 
   /// Query whether this frame is synthetic.
   bool IsSynthetic() const;
@@ -409,12 +409,12 @@ class StackFrame : public ExecutionContextScope,
   /// Query whether this frame is artificial (e.g a synthesized result of
   /// inferring missing tail call frames from a backtrace). Artificial frames
   /// may have limited support for inspecting variables.
-  bool IsArtificial() const;
+  virtual bool IsArtificial() const;
 
   /// Query whether this frame should be hidden from backtraces. Frame
   /// recognizers can customize this behavior and hide distracting
   /// system implementation details this way.
-  bool IsHidden();
+  virtual bool IsHidden();
 
   /// Language plugins can use this API to report language-specific
   /// runtime information about this compile unit, such as additional
@@ -425,13 +425,13 @@ class StackFrame : public ExecutionContextScope,
   ///
   ///  /// \return
   ///   A C-String containing the function demangled name. Can be null.
-  const char *GetFunctionName();
+  virtual const char *GetFunctionName();
 
   /// Get the frame's demangled display name.
   ///
   ///  /// \return
   ///   A C-String containing the function demangled display name. Can be null.
-  const char *GetDisplayFunctionName();
+  virtual const char *GetDisplayFunctionName();
 
   /// Query this frame to find what frame it is in this Thread's
   /// StackFrameList.
@@ -543,18 +543,7 @@ class StackFrame : public ExecutionContextScope,
 
   bool HasCachedData() const;
 
-private:
-  /// Private methods, called from GetValueForVariableExpressionPath.
-  /// See that method for documentation of parameters and return value.
-  lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
-      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
-  lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
-      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
-  /// For StackFrame only.
+  /// For StackFrame and derived classes only.
   /// \{
   lldb::ThreadWP m_thread_wp;
   uint32_t m_frame_index;
@@ -591,6 +580,17 @@ class StackFrame : public ExecutionContextScope,
   StreamString m_disassembly;
   std::recursive_mutex m_mutex;
 
+private:
+  /// Private methods, called from GetValueForVariableExpressionPath.
+  /// See that method for documentation of parameters and return value.
+  lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
+      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
+  lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
+      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
   StackFrame(const StackFrame &) = delete;
   const StackFrame &operator=(const StackFrame &) = delete;
 };

diff  --git a/lldb/include/lldb/lldb-forward.h 
b/lldb/include/lldb/lldb-forward.h
index 483dce98ea427..af5656b3dcad1 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -187,6 +187,7 @@ class SaveCoreOptions;
 class Scalar;
 class ScriptInterpreter;
 class ScriptInterpreterLocker;
+class ScriptedFrameInterface;
 class ScriptedMetadata;
 class ScriptedBreakpointInterface;
 class ScriptedPlatformInterface;
@@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
 typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
     ScriptSummaryFormatSP;
 typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
+    ScriptedFrameInterfaceSP;
 typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
 typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
     ScriptedPlatformInterfaceUP;

diff  --git a/lldb/source/Core/FormatEntity.cpp 
b/lldb/source/Core/FormatEntity.cpp
index 2ff73979e4976..491f5c6320d97 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1681,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
       StackFrame *frame = exe_ctx->GetFramePtr();
       if (frame) {
         const Address &pc_addr = frame->GetFrameCodeAddress();
-        if (pc_addr.IsValid()) {
+        if (pc_addr.IsValid() || frame->IsSynthetic()) {
           if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
             return true;
         }

diff  --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt 
b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
index 590166591a41e..1516ad3132e3b 100644
--- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_library(lldbPluginScriptedProcess PLUGIN
   ScriptedProcess.cpp
   ScriptedThread.cpp
+  ScriptedFrame.cpp
 
   LINK_COMPONENTS
     BinaryFormat

diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
new file mode 100644
index 0000000000000..6519df9185df0
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -0,0 +1,191 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ScriptedFrame.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void ScriptedFrame::CheckInterpreterAndScriptObject() const {
+  lldbassert(m_script_object_sp && "Invalid Script Object.");
+  lldbassert(GetInterface() && "Invalid Scripted Frame Interface.");
+}
+
+llvm::Expected<std::shared_ptr<ScriptedFrame>>
+ScriptedFrame::Create(ScriptedThread &thread,
+                      StructuredData::DictionarySP args_sp,
+                      StructuredData::Generic *script_object) {
+  if (!thread.IsValid())
+    return llvm::createStringError("Invalid scripted thread.");
+
+  thread.CheckInterpreterAndScriptObject();
+
+  auto scripted_frame_interface =
+      thread.GetInterface()->CreateScriptedFrameInterface();
+  if (!scripted_frame_interface)
+    return llvm::createStringError("failed to create scripted frame 
interface");
+
+  llvm::StringRef frame_class_name;
+  if (!script_object) {
+    std::optional<std::string> class_name =
+        thread.GetInterface()->GetScriptedFramePluginName();
+    if (!class_name || class_name->empty())
+      return llvm::createStringError(
+          "failed to get scripted thread class name");
+    frame_class_name = *class_name;
+  }
+
+  ExecutionContext exe_ctx(thread);
+  auto obj_or_err = scripted_frame_interface->CreatePluginObject(
+      frame_class_name, exe_ctx, args_sp, script_object);
+
+  if (!obj_or_err)
+    return llvm::createStringError(
+        "failed to create script object: %s",
+        llvm::toString(obj_or_err.takeError()).c_str());
+
+  StructuredData::GenericSP owned_script_object_sp = *obj_or_err;
+
+  if (!owned_script_object_sp->IsValid())
+    return llvm::createStringError("created script object is invalid");
+
+  lldb::user_id_t frame_id = scripted_frame_interface->GetID();
+
+  lldb::addr_t pc = scripted_frame_interface->GetPC();
+  SymbolContext sc;
+  Address symbol_addr;
+  if (pc != LLDB_INVALID_ADDRESS) {
+    symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget());
+    symbol_addr.CalculateSymbolContext(&sc);
+  }
+
+  std::optional<SymbolContext> maybe_sym_ctx =
+      scripted_frame_interface->GetSymbolContext();
+  if (maybe_sym_ctx) {
+    sc = *maybe_sym_ctx;
+  }
+
+  StructuredData::DictionarySP reg_info =
+      scripted_frame_interface->GetRegisterInfo();
+
+  if (!reg_info)
+    return llvm::createStringError(
+        "failed to get scripted thread registers info");
+
+  std::shared_ptr<DynamicRegisterInfo> register_info_sp =
+      DynamicRegisterInfo::Create(
+          *reg_info, thread.GetProcess()->GetTarget().GetArchitecture());
+
+  lldb::RegisterContextSP reg_ctx_sp;
+
+  std::optional<std::string> reg_data =
+      scripted_frame_interface->GetRegisterContext();
+  if (reg_data) {
+    DataBufferSP data_sp(
+        std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
+
+    if (!data_sp->GetByteSize())
+      return llvm::createStringError("failed to copy raw registers data");
+
+    std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
+        std::make_shared<RegisterContextMemory>(
+            thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS);
+    if (!reg_ctx_memory)
+      return llvm::createStringError("failed to create a register context.");
+
+    reg_ctx_memory->SetAllRegisterData(data_sp);
+    reg_ctx_sp = reg_ctx_memory;
+  }
+
+  return std::make_shared<ScriptedFrame>(
+      thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp,
+      register_info_sp, owned_script_object_sp);
+}
+
+ScriptedFrame::ScriptedFrame(ScriptedThread &thread,
+                             ScriptedFrameInterfaceSP interface_sp,
+                             lldb::user_id_t id, lldb::addr_t pc,
+                             SymbolContext &sym_ctx,
+                             lldb::RegisterContextSP reg_ctx_sp,
+                             std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+                             StructuredData::GenericSP script_object_sp)
+    : StackFrame(thread.shared_from_this(), /*frame_idx=*/id,
+                 /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp,
+                 /*cfa=*/0, /*pc=*/pc,
+                 /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx),
+      m_scripted_frame_interface_sp(interface_sp),
+      m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {}
+
+ScriptedFrame::~ScriptedFrame() {}
+
+const char *ScriptedFrame::GetFunctionName() {
+  CheckInterpreterAndScriptObject();
+  std::optional<std::string> function_name = GetInterface()->GetFunctionName();
+  if (!function_name)
+    return nullptr;
+  return ConstString(function_name->c_str()).AsCString();
+}
+
+const char *ScriptedFrame::GetDisplayFunctionName() {
+  CheckInterpreterAndScriptObject();
+  std::optional<std::string> function_name =
+      GetInterface()->GetDisplayFunctionName();
+  if (!function_name)
+    return nullptr;
+  return ConstString(function_name->c_str()).AsCString();
+}
+
+bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); }
+
+bool ScriptedFrame::IsArtificial() const {
+  return GetInterface()->IsArtificial();
+}
+
+bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); }
+
+lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const {
+  return m_scripted_frame_interface_sp;
+}
+
+std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() {
+  CheckInterpreterAndScriptObject();
+
+  if (!m_register_info_sp) {
+    StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
+
+    Status error;
+    if (!reg_info)
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.",
+          error, LLDBLog::Thread);
+
+    ThreadSP thread_sp = m_thread_wp.lock();
+    if (!thread_sp || !thread_sp->IsValid())
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION,
+          "Failed to get scripted frame registers info: invalid thread.", 
error,
+          LLDBLog::Thread);
+
+    ProcessSP process_sp = thread_sp->GetProcess();
+    if (!process_sp || !process_sp->IsValid())
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION,
+          "Failed to get scripted frame registers info: invalid process.",
+          error, LLDBLog::Thread);
+
+    m_register_info_sp = DynamicRegisterInfo::Create(
+        *reg_info, process_sp->GetTarget().GetArchitecture());
+  }
+
+  return m_register_info_sp;
+}

diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h 
b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
new file mode 100644
index 0000000000000..6e01e2fd7653e
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SCRIPTED_FRAME_H
+#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
+
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "ScriptedThread.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
+#include "lldb/Target/StackFrame.h"
+#include <string>
+
+namespace lldb_private {
+class ScriptedThread;
+}
+
+namespace lldb_private {
+
+class ScriptedFrame : public lldb_private::StackFrame {
+
+public:
+  ScriptedFrame(ScriptedThread &thread,
+                lldb::ScriptedFrameInterfaceSP interface_sp,
+                lldb::user_id_t frame_idx, lldb::addr_t pc,
+                SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp,
+                std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+                StructuredData::GenericSP script_object_sp = nullptr);
+
+  ~ScriptedFrame() override;
+
+  static llvm::Expected<std::shared_ptr<ScriptedFrame>>
+  Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp,
+         StructuredData::Generic *script_object = nullptr);
+
+  bool IsInlined() override;
+  bool IsArtificial() const override;
+  bool IsHidden() override;
+  const char *GetFunctionName() override;
+  const char *GetDisplayFunctionName() override;
+
+private:
+  void CheckInterpreterAndScriptObject() const;
+  lldb::ScriptedFrameInterfaceSP GetInterface() const;
+
+  ScriptedFrame(const ScriptedFrame &) = delete;
+  const ScriptedFrame &operator=(const ScriptedFrame &) = delete;
+
+  std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo();
+
+  lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp;
+  lldb_private::StructuredData::GenericSP m_script_object_sp;
+  std::shared_ptr<DynamicRegisterInfo> m_register_info_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H

diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 45ad83e4ed671..491efac5aadef 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "ScriptedThread.h"
+#include "ScriptedFrame.h"
 
 #include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
 #include "Plugins/Process/Utility/StopInfoMachException.h"
@@ -173,27 +174,31 @@ bool ScriptedThread::LoadArtificialStackFrames() {
             .str(),
         error, LLDBLog::Thread);
 
-  StackFrameListSP frames = GetStackFrameList();
-
-  for (size_t idx = 0; idx < arr_size; idx++) {
+  auto create_frame_from_dict =
+      [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+    Status error;
     std::optional<StructuredData::Dictionary *> maybe_dict =
         arr_sp->GetItemAtIndexAsDictionary(idx);
-    if (!maybe_dict)
-      return ScriptedInterface::ErrorWithMessage<bool>(
+    if (!maybe_dict) {
+      ScriptedInterface::ErrorWithMessage<bool>(
           LLVM_PRETTY_FUNCTION,
           llvm::Twine(
               "Couldn't get artificial stackframe dictionary at index (" +
               llvm::Twine(idx) + llvm::Twine(") from stackframe array."))
               .str(),
           error, LLDBLog::Thread);
+      return error.ToError();
+    }
     StructuredData::Dictionary *dict = *maybe_dict;
 
     lldb::addr_t pc;
-    if (!dict->GetValueForKeyAsInteger("pc", pc))
-      return ScriptedInterface::ErrorWithMessage<bool>(
+    if (!dict->GetValueForKeyAsInteger("pc", pc)) {
+      ScriptedInterface::ErrorWithMessage<bool>(
           LLVM_PRETTY_FUNCTION,
           "Couldn't find value for key 'pc' in stackframe dictionary.", error,
           LLDBLog::Thread);
+      return error.ToError();
+    }
 
     Address symbol_addr;
     symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
@@ -205,10 +210,65 @@ bool ScriptedThread::LoadArtificialStackFrames() {
     SymbolContext sc;
     symbol_addr.CalculateSymbolContext(&sc);
 
-    StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
-        this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
-        StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame,
-        &sc);
+    return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, 
cfa,
+                                        cfa_is_valid, pc,
+                                        StackFrame::Kind::Synthetic, 
artificial,
+                                        behaves_like_zeroth_frame, &sc);
+  };
+
+  auto create_frame_from_script_object =
+      [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+    Status error;
+    StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx);
+    if (!object_sp || !object_sp->GetAsGeneric()) {
+      ScriptedInterface::ErrorWithMessage<bool>(
+          LLVM_PRETTY_FUNCTION,
+          llvm::Twine("Couldn't get artificial stackframe object at index (" +
+                      llvm::Twine(idx) +
+                      llvm::Twine(") from stackframe array."))
+              .str(),
+          error, LLDBLog::Thread);
+      return error.ToError();
+    }
+
+    auto frame_or_error =
+        ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric());
+
+    if (!frame_or_error) {
+      ScriptedInterface::ErrorWithMessage<bool>(
+          LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error);
+      return error.ToError();
+    }
+
+    StackFrameSP frame_sp = frame_or_error.get();
+    lldbassert(frame_sp && "Couldn't initialize scripted frame.");
+
+    return frame_sp;
+  };
+
+  StackFrameListSP frames = GetStackFrameList();
+
+  for (size_t idx = 0; idx < arr_size; idx++) {
+    StackFrameSP synth_frame_sp = nullptr;
+
+    auto frame_from_dict_or_err = create_frame_from_dict(idx);
+    if (!frame_from_dict_or_err) {
+      auto frame_from_script_obj_or_err = create_frame_from_script_object(idx);
+
+      if (!frame_from_script_obj_or_err) {
+        return ScriptedInterface::ErrorWithMessage<bool>(
+            LLVM_PRETTY_FUNCTION,
+            llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) +
+                        llvm::Twine(") to ScriptedThread StackFrameList."))
+                .str(),
+            error, LLDBLog::Thread);
+      } else {
+        llvm::consumeError(frame_from_dict_or_err.takeError());
+        synth_frame_sp = *frame_from_script_obj_or_err;
+      }
+    } else {
+      synth_frame_sp = *frame_from_dict_or_err;
+    }
 
     if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
       return ScriptedInterface::ErrorWithMessage<bool>(

diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h 
b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
index cd224d60ceef8..ee5ace4059673 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
@@ -15,11 +15,12 @@
 
 #include "Plugins/Process/Utility/RegisterContextMemory.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
-#include "lldb/Target//DynamicRegisterInfo.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
 #include "lldb/Target/Thread.h"
 
 namespace lldb_private {
 class ScriptedProcess;
+class ScriptedFrame;
 }
 
 namespace lldb_private {
@@ -61,6 +62,8 @@ class ScriptedThread : public lldb_private::Thread {
   StructuredData::ObjectSP FetchThreadExtendedInfo() override;
 
 private:
+  friend class ScriptedFrame;
+
   void CheckInterpreterAndScriptObject() const;
   lldb::ScriptedThreadInterfaceSP GetInterface() const;
 

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 04370940423ae..09103573b89c5 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
 add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
   OperatingSystemPythonInterface.cpp
   ScriptInterpreterPythonInterfaces.cpp
+  ScriptedFramePythonInterface.cpp
   ScriptedPlatformPythonInterface.cpp
   ScriptedProcessPythonInterface.cpp
   ScriptedPythonInterface.cpp

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 02dc06507caf2..3814f46615078 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -17,6 +17,7 @@
 
 #include "OperatingSystemPythonInterface.h"
 #include "ScriptedBreakpointPythonInterface.h"
+#include "ScriptedFramePythonInterface.h"
 #include "ScriptedPlatformPythonInterface.h"
 #include "ScriptedProcessPythonInterface.h"
 #include "ScriptedStopHookPythonInterface.h"

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
new file mode 100644
index 0000000000000..20ca7a2c01356
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Host/Config.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "../lldb-python.h"
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedFramePythonInterface.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedFramePythonInterface::ScriptedFramePythonInterface(
+    ScriptInterpreterPythonImpl &interpreter)
+    : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedFramePythonInterface::CreatePluginObject(
+    const llvm::StringRef class_name, ExecutionContext &exe_ctx,
+    StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) 
{
+  ExecutionContextRefSP exe_ctx_ref_sp =
+      std::make_shared<ExecutionContextRef>(exe_ctx);
+  StructuredDataImpl sd_impl(args_sp);
+  return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj,
+                                                     exe_ctx_ref_sp, sd_impl);
+}
+
+lldb::user_id_t ScriptedFramePythonInterface::GetID() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_id", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return LLDB_INVALID_FRAME_ID;
+
+  return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID);
+}
+
+lldb::addr_t ScriptedFramePythonInterface::GetPC() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_pc", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return LLDB_INVALID_ADDRESS;
+
+  return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS);
+}
+
+std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() {
+  Status error;
+  auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error);
+
+  if (error.Fail()) {
+    return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION,
+                                           error.AsCString(), error);
+  }
+
+  return sym_ctx;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_function_name", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+std::optional<std::string>
+ScriptedFramePythonInterface::GetDisplayFunctionName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+bool ScriptedFramePythonInterface::IsInlined() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_inlined", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsArtificial() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_artificial", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsHidden() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_hidden", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() {
+  Status error;
+  StructuredData::DictionarySP dict =
+      Dispatch<StructuredData::DictionarySP>("get_register_info", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
+                                                    error))
+    return {};
+
+  return dict;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_register_context", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetAsString()->GetValue().str();
+}
+
+#endif

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
new file mode 100644
index 0000000000000..3aff237ae65d5
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+#define 
LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedPythonInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include <optional>
+
+namespace lldb_private {
+class ScriptedFramePythonInterface : public ScriptedFrameInterface,
+                                     public ScriptedPythonInterface {
+public:
+  ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter);
+
+  llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+                     StructuredData::DictionarySP args_sp,
+                     StructuredData::Generic *script_obj = nullptr) override;
+
+  llvm::SmallVector<AbstractMethodRequirement>
+  GetAbstractMethodRequirements() const override {
+    return llvm::SmallVector<AbstractMethodRequirement>({{"get_id"}});
+  }
+
+  lldb::user_id_t GetID() override;
+
+  lldb::addr_t GetPC() override;
+
+  std::optional<SymbolContext> GetSymbolContext() override;
+
+  std::optional<std::string> GetFunctionName() override;
+
+  std::optional<std::string> GetDisplayFunctionName() override;
+
+  bool IsInlined() override;
+
+  bool IsArtificial() override;
+
+  bool IsHidden() override;
+
+  StructuredData::DictionarySP GetRegisterInfo() override;
+
+  std::optional<std::string> GetRegisterContext() override;
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // 
LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index b49d1d82fe535..8083ccae04026 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
 
   if (!sb_mem_reg_info) {
     error = Status::FromErrorStringWithFormat(
-        "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP.");
+        "Couldn't cast lldb::SBMemoryRegionInfo to "
+        "lldb_private::MemoryRegionInfo.");
     return {};
   }
 

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
index 8af89d761764b..fd4d231a747fe 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
@@ -144,4 +144,21 @@ StructuredData::ArraySP 
ScriptedThreadPythonInterface::GetExtendedInfo() {
   return arr;
 }
 
+std::optional<std::string>
+ScriptedThreadPythonInterface::GetScriptedFramePluginName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+lldb::ScriptedFrameInterfaceSP
+ScriptedThreadPythonInterface::CreateScriptedFrameInterface() {
+  return m_interpreter.CreateScriptedFrameInterface();
+}
+
 #endif

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
index 1fb23b39c7076..043557a827461 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
@@ -51,6 +51,11 @@ class ScriptedThreadPythonInterface : public 
ScriptedThreadInterface,
   std::optional<std::string> GetRegisterContext() override;
 
   StructuredData::ArraySP GetExtendedInfo() override;
+
+  std::optional<std::string> GetScriptedFramePluginName() override;
+
+protected:
+  lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
 };
 } // namespace lldb_private
 

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 9330a634489a2..73c5c72932ff1 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1521,6 +1521,11 @@ 
ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
   return std::make_shared<ScriptedThreadPythonInterface>(*this);
 }
 
+ScriptedFrameInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() {
+  return std::make_shared<ScriptedFramePythonInterface>(*this);
+}
+
 ScriptedThreadPlanInterfaceSP
 ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() {
   return std::make_shared<ScriptedThreadPlanPythonInterface>(*this);

diff  --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 83b64b85faebd..dedac280788f4 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -99,6 +99,8 @@ class ScriptInterpreterPythonImpl : public 
ScriptInterpreterPython {
 
   lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
 
+  lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
+
   lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() override;
 

diff  --git 
a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py 
b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
index 2721d961bcb9d..835267221ddb4 100644
--- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
+++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
@@ -5,6 +5,7 @@
 import lldb
 from lldb.plugins.scripted_process import ScriptedProcess
 from lldb.plugins.scripted_process import ScriptedThread
+from lldb.plugins.scripted_process import ScriptedFrame
 
 
 class DummyStopHook:
@@ -22,7 +23,7 @@ class DummyScriptedProcess(ScriptedProcess):
 
     def __init__(self, exe_ctx: lldb.SBExecutionContext, args: 
lldb.SBStructuredData):
         super().__init__(exe_ctx, args)
-        self.threads[0] = DummyScriptedThread(self, None)
+        self.threads[0] = DummyScriptedThread(self, args)
         self.memory = {}
         addr = 0x500000000
         debugger = self.target.GetDebugger()
@@ -69,6 +70,9 @@ class DummyScriptedThread(ScriptedThread):
     def __init__(self, process, args):
         super().__init__(process, args)
         self.frames.append({"pc": 0x0100001B00})
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"baz123"))
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"bar"))
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"foo"))
 
     def get_thread_id(self) -> int:
         return 0x19
@@ -109,6 +113,65 @@ def get_register_context(self) -> str:
         )
 
 
+class DummyScriptedFrame(ScriptedFrame):
+    def __init__(self, thread, args, id, name, sym_ctx=None):
+        super().__init__(thread, args)
+        self.id = id
+        self.name = name
+        self.sym_ctx = sym_ctx
+
+    def get_id(self):
+        return self.id
+
+    def get_function_name(self):
+        return self.name
+
+    def get_register_context(self) -> str:
+        return struct.pack(
+            "21Q",
+            0x10001,
+            0x10002,
+            0x10003,
+            0x10004,
+            0x10005,
+            0x10006,
+            0x10007,
+            0x10008,
+            0x10009,
+            0x100010,
+            0x100011,
+            0x100012,
+            0x100013,
+            0x100014,
+            0x100015,
+            0x100016,
+            0x100017,
+            0x100018,
+            0x100019,
+            0x100020,
+            0x100021,
+        )
+
+    def get_symbol_context(self):
+        def get_symbol_context_for_function(func_name):
+            module = self.target.FindModule(self.target.GetExecutable())
+            if not module.IsValid():
+                return None
+
+            sym_ctx_list = module.FindFunctions(func_name)
+            if not sym_ctx_list.IsValid() or sym_ctx_list.GetSize() == 0:
+                return None
+
+            return sym_ctx_list.GetContextAtIndex(0)
+
+        return (
+            self.sym_ctx if self.sym_ctx else 
get_symbol_context_for_function(self.name)
+        )
+
+    def get_scripted_frame_plugin(self):
+        return DummyScriptedFrame.__module__ + "." + 
DummyScriptedFrame.__name__
+
+
 def __lldb_init_module(debugger, dict):
     # This is used when loading the script in an interactive debug session to
     # automatically, register the stop-hook and launch the scripted process.


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to