[Lldb-commits] [lldb] [lldb] Introduce ScriptedFrameProvider (PR #161870)

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 3 09:29:45 PDT 2025


https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/161870

>From 9521324b1c7381eb6601170652828b682f6a73f1 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Fri, 3 Oct 2025 17:28:59 +0100
Subject: [PATCH] [lldb] Introduce ScritedFrameProvider

Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
 lldb/include/lldb/API/SBThread.h              |  2 +
 .../ScriptedFrameProviderInterface.h          | 27 ++++++
 .../lldb/Interpreter/ScriptInterpreter.h      |  5 +
 .../lldb/Interpreter/ScriptedFrameProvider.h  | 51 ++++++++++
 lldb/include/lldb/Target/Thread.h             |  9 ++
 lldb/include/lldb/lldb-forward.h              |  6 ++
 lldb/source/API/SBThread.cpp                  | 27 ++++++
 lldb/source/Commands/CommandObjectFrame.cpp   | 95 +++++++++++++++++++
 lldb/source/Commands/CommandObjectThread.cpp  |  1 +
 lldb/source/Interpreter/CMakeLists.txt        |  1 +
 .../Interpreter/ScriptedFrameProvider.cpp     | 86 +++++++++++++++++
 .../Python/Interfaces/CMakeLists.txt          |  1 +
 .../ScriptInterpreterPythonInterfaces.h       |  1 +
 .../ScriptedFrameProviderPythonInterface.cpp  | 57 +++++++++++
 .../ScriptedFrameProviderPythonInterface.h    | 41 ++++++++
 .../Interfaces/ScriptedPythonInterface.h      |  4 +
 .../Python/ScriptInterpreterPython.cpp        |  5 +
 .../Python/ScriptInterpreterPythonImpl.h      |  3 +
 lldb/source/Target/Thread.cpp                 | 30 ++++++
 19 files changed, 452 insertions(+)
 create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
 create mode 100644 lldb/include/lldb/Interpreter/ScriptedFrameProvider.h
 create mode 100644 lldb/source/Interpreter/ScriptedFrameProvider.cpp
 create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
 create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h

diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index e9fe5858d125e..7b91228528bc7 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -229,6 +229,8 @@ class LLDB_API SBThread {
 
   SBValue GetSiginfo();
 
+  void RegisterFrameProvider(const char *class_name, SBStructuredData &args_data);
+
 private:
   friend class SBBreakpoint;
   friend class SBBreakpointLocation;
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
new file mode 100644
index 0000000000000..7618d5e15d563
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
@@ -0,0 +1,27 @@
+//===-- ScriptedFrameProviderInterface.h ------------------------*- C++ -*-===//
+//
+// 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_SCRIPTEDFRAMEPROVIDERINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
+
+#include "lldb/lldb-private.h"
+
+#include "ScriptedInterface.h"
+
+namespace lldb_private {
+class ScriptedFrameProviderInterface : public ScriptedInterface {
+public:
+  virtual llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, lldb::ThreadSP thread_sp,
+                     StructuredData::DictionarySP args_sp) = 0;
+
+  virtual StructuredData::ArraySP GetStackFrames() { return {}; }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 024bbc90a9a39..76ade002089bb 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -27,6 +27,7 @@
 #include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -536,6 +537,10 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
+  virtual lldb::ScriptedFrameProviderInterfaceSP CreateScriptedFrameProviderInterface() {
+    return {};
+  }
+
   virtual lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() {
     return {};
diff --git a/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h b/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h
new file mode 100644
index 0000000000000..6c4053f11eeb3
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/ScriptedFrameProvider.h
@@ -0,0 +1,51 @@
+//===-- ScriptedFrameProvider.h --------------------------------*- C++ -*-===//
+//
+// 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_SCRIPTEDFRAMEPROVIDER_H
+#define LLDB_INTERPRETER_SCRIPTEDFRAMEPROVIDER_H
+
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+
+class ScriptedFrameProvider {
+public:
+  /// Constructor that initializes the scripted frame provider.
+  ///
+  /// \param[in] thread_sp
+  ///     The thread for which to provide scripted frames.
+  ///
+  /// \param[in] scripted_metadata
+  ///     The metadata containing the class name and arguments for the
+  ///     scripted frame provider.
+  ///
+  /// \param[out] error
+  ///     Status object to report any errors during initialization.
+  ScriptedFrameProvider(lldb::ThreadSP thread_sp,
+                        const ScriptedMetadata &scripted_metadata,
+                        Status &error);
+  ~ScriptedFrameProvider();
+
+  /// Get the stack frames from the scripted frame provider.
+  ///
+  /// \return
+  ///     An Expected containing the StackFrameListSP if successful,
+  ///     otherwise an error describing what went wrong.
+  llvm::Expected<lldb::StackFrameListSP> GetStackFrames();
+
+private:
+  lldb::ThreadSP m_thread_sp;
+  lldb::ScriptedFrameProviderInterfaceSP m_interface_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_SCRIPTEDFRAMEPROVIDER_H
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 688c056da2633..d0c33f557b12b 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -1294,6 +1294,10 @@ class Thread : public std::enable_shared_from_this<Thread>,
   ///     The PC value before execution was resumed.  May not be available;
   ///     an empty std::optional is returned in that case.
   std::optional<lldb::addr_t> GetPreviousFrameZeroPC();
+                 
+   void SetScriptedFrameProvider(const ScriptedMetadata &scripted_metadata);
+
+   void ClearScriptedFrameProvider();
 
 protected:
   friend class ThreadPlan;
@@ -1338,6 +1342,8 @@ class Thread : public std::enable_shared_from_this<Thread>,
 
   lldb::StackFrameListSP GetStackFrameList();
 
+  llvm::Expected<lldb::StackFrameListSP> GetScriptedFrameList();
+
   void SetTemporaryResumeState(lldb::StateType new_state) {
     m_temporary_resume_state = new_state;
   }
@@ -1400,6 +1406,9 @@ class Thread : public std::enable_shared_from_this<Thread>,
   /// The Thread backed by this thread, if any.
   lldb::ThreadWP m_backed_thread;
 
+  /// The Scripted Frame Provider, if any.
+  lldb::ScriptedFrameProviderSP m_frame_provider_sp;
+
 private:
   bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info
                                 // for this thread?
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index af5656b3dcad1..85045a803b07a 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -188,6 +188,8 @@ class Scalar;
 class ScriptInterpreter;
 class ScriptInterpreterLocker;
 class ScriptedFrameInterface;
+class ScriptedFrameProvider;
+class ScriptedFrameProviderInterface;
 class ScriptedMetadata;
 class ScriptedBreakpointInterface;
 class ScriptedPlatformInterface;
@@ -411,6 +413,10 @@ typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
 typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
 typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
     ScriptedFrameInterfaceSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameProvider>
+    ScriptedFrameProviderSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameProviderInterface>
+    ScriptedFrameProviderInterfaceSP;
 typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
 typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
     ScriptedPlatformInterfaceUP;
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 4e4aa48bc9a2e..8610dee840767 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -39,6 +39,7 @@
 #include "lldb/Target/ThreadPlanStepOut.h"
 #include "lldb/Target/ThreadPlanStepRange.h"
 #include "lldb/Utility/Instrumentation.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StructuredData.h"
@@ -1324,3 +1325,29 @@ SBValue SBThread::GetSiginfo() {
     return SBValue();
   return thread_sp->GetSiginfoValue();
 }
+
+void SBThread::RegisterFrameProvider(const char *class_name,
+                                     SBStructuredData &dict) {
+  LLDB_INSTRUMENT_VA(this, class_name, dict);
+
+  ThreadSP thread_sp = m_opaque_sp->GetThreadSP();
+  if (!thread_sp)
+    return;
+  
+  if (!dict.IsValid() || !dict.m_impl_up)
+    return;
+
+  StructuredData::ObjectSP obj_sp = dict.m_impl_up->GetObjectSP();
+
+  if (!obj_sp)
+    return;
+
+  StructuredData::DictionarySP dict_sp =
+      std::make_shared<StructuredData::Dictionary>(obj_sp);
+  if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+    return;
+
+
+  ScriptedMetadata metadata(class_name, dict_sp);
+  thread_sp->SetScriptedFrameProvider(metadata);
+}
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 88a02dce35b9d..02d62aa9249d1 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -16,6 +16,7 @@
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Interpreter/OptionArgParser.h"
 #include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
 #include "lldb/Interpreter/OptionGroupVariable.h"
 #include "lldb/Interpreter/Options.h"
@@ -29,6 +30,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Utility/Args.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/ValueObject/ValueObject.h"
 
 #include <memory>
@@ -1223,6 +1225,97 @@ class CommandObjectFrameRecognizer : public CommandObjectMultiword {
   ~CommandObjectFrameRecognizer() override = default;
 };
 
+#pragma mark CommandObjectFrameProvider
+
+#define LLDB_OPTIONS_frame_provider_register
+#include "CommandOptions.inc"
+
+class CommandObjectFrameProviderRegister : public CommandObjectParsed {
+public:
+  CommandObjectFrameProviderRegister(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "frame provider register",
+            "Register frame provider into current thread.",
+            nullptr, eCommandRequiresThread),
+
+        m_class_options("frame provider", true, 'C', 'k', 'v', 0) {
+    m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+                         LLDB_OPT_SET_ALL);
+    m_all_options.Finalize();
+
+    AddSimpleArgumentList(eArgTypeRunArgs, eArgRepeatOptional);
+  }
+
+  ~CommandObjectFrameProviderRegister() override = default;
+
+  Options *GetOptions() override { return &m_all_options; }
+
+  std::optional<std::string> GetRepeatCommand(Args &current_command_args,
+                                              uint32_t index) override {
+    // No repeat for "process launch"...
+    return std::string("");
+  }
+
+protected:
+  void DoExecute(Args &launch_args, CommandReturnObject &result) override {
+    ScriptedMetadata metadata(m_class_options.GetName(), m_class_options.GetStructuredData());
+
+    Thread *thread = m_exe_ctx.GetThreadPtr();
+    if (!thread) {
+      result.AppendError("invalid thread");
+      return;
+    }
+
+    thread->SetScriptedFrameProvider(metadata);
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    result.AppendMessageWithFormat(
+        "Successfully registered scripted frame provider '%s'\n",
+        m_class_options.GetName().c_str());
+  }
+
+  OptionGroupPythonClassWithDict m_class_options;
+  OptionGroupOptions m_all_options;
+};
+
+class CommandObjectFrameProviderClear : public CommandObjectParsed {
+public:
+  CommandObjectFrameProviderClear(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "frame provider clear",
+                            "Delete registered frame provider.", nullptr) {}
+
+  ~CommandObjectFrameProviderClear() override = default;
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    Thread *thread = m_exe_ctx.GetThreadPtr();
+    if (!thread) {
+      result.AppendError("invalid thread");
+      return;
+    }
+    
+    thread->ClearScriptedFrameProvider();
+    
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+  }
+};
+
+class CommandObjectFrameProvider : public CommandObjectMultiword {
+public:
+  CommandObjectFrameProvider(CommandInterpreter &interpreter)
+      : CommandObjectMultiword(
+            interpreter, "frame provider",
+            "Commands for registering and viewing frame providers.",
+            "frame provider [<sub-command-options>] ") {
+    LoadSubCommand("register", CommandObjectSP(new CommandObjectFrameProviderRegister(
+                              interpreter)));
+    LoadSubCommand(
+        "clear",
+        CommandObjectSP(new CommandObjectFrameProviderClear(interpreter)));
+  }
+
+  ~CommandObjectFrameProvider() override = default;
+};
+
 #pragma mark CommandObjectMultiwordFrame
 
 // CommandObjectMultiwordFrame
@@ -1243,6 +1336,8 @@ CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
   LoadSubCommand("variable",
                  CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
 #if LLDB_ENABLE_PYTHON
+  LoadSubCommand("provider", CommandObjectSP(new CommandObjectFrameProvider(
+                                   interpreter)));
   LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(
                                    interpreter)));
 #endif
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index bbec714642ec9..0092151a13dd8 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -35,6 +35,7 @@
 #include "lldb/Target/ThreadPlanStepInRange.h"
 #include "lldb/Target/Trace.h"
 #include "lldb/Target/TraceDumper.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/State.h"
 #include "lldb/ValueObject/ValueObject.h"
 
diff --git a/lldb/source/Interpreter/CMakeLists.txt b/lldb/source/Interpreter/CMakeLists.txt
index 8af7373702c38..ab877ddeecba8 100644
--- a/lldb/source/Interpreter/CMakeLists.txt
+++ b/lldb/source/Interpreter/CMakeLists.txt
@@ -53,6 +53,7 @@ add_lldb_library(lldbInterpreter NO_PLUGIN_DEPENDENCIES
   OptionGroupWatchpoint.cpp
   Options.cpp
   Property.cpp
+  ScriptedFrameProvider.cpp
   ScriptInterpreter.cpp
 
   ADDITIONAL_HEADER_DIRS
diff --git a/lldb/source/Interpreter/ScriptedFrameProvider.cpp b/lldb/source/Interpreter/ScriptedFrameProvider.cpp
new file mode 100644
index 0000000000000..b35ed5d20f6ba
--- /dev/null
+++ b/lldb/source/Interpreter/ScriptedFrameProvider.cpp
@@ -0,0 +1,86 @@
+//===-- ScriptedFrameProvider.cpp ----------------------------------------===//
+//
+// 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/Interpreter/ScriptedFrameProvider.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ScriptedMetadata.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ScriptedFrameProvider::ScriptedFrameProvider(
+    ThreadSP thread_sp, const ScriptedMetadata &scripted_metadata,
+    Status &error)
+    : m_thread_sp(thread_sp), m_interface_sp(nullptr) {
+  if (!m_thread_sp) {
+    error = Status::FromErrorString(
+        "cannot create scripted frame provider: Invalid thread");
+    return;
+  }
+
+  ProcessSP process_sp = m_thread_sp->GetProcess();
+  if (!process_sp) {
+    error = Status::FromErrorString(
+        "cannot create scripted frame provider: Invalid process");
+    return;
+  }
+
+  ScriptInterpreter *script_interp =
+      process_sp->GetTarget().GetDebugger().GetScriptInterpreter();
+  if (!script_interp) {
+    error = Status::FromErrorString("cannot create scripted frame provider: No "
+                                    "script interpreter installed");
+    return;
+  }
+
+  m_interface_sp = script_interp->CreateScriptedFrameProviderInterface();
+  if (!m_interface_sp) {
+    error = Status::FromErrorString(
+        "cannot create scripted frame provider: Script interpreter couldn't "
+        "create Scripted Frame Provider Interface");
+    return;
+  }
+
+  auto obj_or_err = m_interface_sp->CreatePluginObject(
+      scripted_metadata.GetClassName(), m_thread_sp,
+      scripted_metadata.GetArgsSP());
+  if (!obj_or_err) {
+    error = Status::FromError(obj_or_err.takeError());
+    return;
+  }
+
+  StructuredData::ObjectSP object_sp = *obj_or_err;
+  if (!object_sp || !object_sp->IsValid()) {
+    error = Status::FromErrorString(
+        "cannot create scripted frame provider: Failed to create valid script "
+        "object");
+    return;
+  }
+
+  error.Clear();
+}
+
+ScriptedFrameProvider::~ScriptedFrameProvider() = default;
+
+llvm::Expected<StackFrameListSP> ScriptedFrameProvider::GetStackFrames() {
+  if (!m_interface_sp)
+    return llvm::createStringError(
+        "cannot get stack frames: Scripted frame provider not initialized");
+
+  auto frames = m_interface_sp->GetStackFrames();
+
+  // TODO: Convert StructuredData::ArraySP to StackFrameListSP
+  // This is a placeholder for now
+  return nullptr;
+}
\ No newline at end of file
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 09103573b89c5..50569cdefaafa 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -23,6 +23,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
   OperatingSystemPythonInterface.cpp
   ScriptInterpreterPythonInterfaces.cpp
   ScriptedFramePythonInterface.cpp
+  ScriptedFrameProviderPythonInterface.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 3814f46615078..0b9c7eb107bf5 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -18,6 +18,7 @@
 #include "OperatingSystemPythonInterface.h"
 #include "ScriptedBreakpointPythonInterface.h"
 #include "ScriptedFramePythonInterface.h"
+#include "ScriptedFrameProviderPythonInterface.h"
 #include "ScriptedPlatformPythonInterface.h"
 #include "ScriptedProcessPythonInterface.h"
 #include "ScriptedStopHookPythonInterface.h"
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
new file mode 100644
index 0000000000000..b9a659c44e6f1
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
@@ -0,0 +1,57 @@
+//===-- ScriptedFrameProviderPythonInterface.cpp -------------------------===//
+//
+// 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/Thread.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 "ScriptedFrameProviderPythonInterface.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedFrameProviderPythonInterface::ScriptedFrameProviderPythonInterface(
+    ScriptInterpreterPythonImpl &interpreter)
+    : ScriptedFrameProviderInterface(), ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedFrameProviderPythonInterface::CreatePluginObject(
+    const llvm::StringRef class_name, lldb::ThreadSP thread_sp,
+    StructuredData::DictionarySP args_sp) {
+  if (!thread_sp)
+    return llvm::createStringError("Invalid thread");
+
+  StructuredDataImpl sd_impl(args_sp);
+  return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
+                                                     thread_sp, sd_impl);
+}
+
+StructuredData::ArraySP ScriptedFrameProviderPythonInterface::GetStackFrames() {
+  Status error;
+  StructuredData::ArraySP arr =
+      Dispatch<StructuredData::ArraySP>("get_stackframes", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
+                                                    error))
+    return {};
+
+  return arr;
+}
+
+#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
new file mode 100644
index 0000000000000..202769c094f06
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h
@@ -0,0 +1,41 @@
+//===-- ScriptedFrameProviderPythonInterface.h -----------------*- C++ -*-===//
+//
+// 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_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedPythonInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
+#include <optional>
+
+namespace lldb_private {
+class ScriptedFrameProviderPythonInterface
+    : public ScriptedFrameProviderInterface,
+      public ScriptedPythonInterface {
+public:
+  ScriptedFrameProviderPythonInterface(ScriptInterpreterPythonImpl &interpreter);
+
+  llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, lldb::ThreadSP thread_sp,
+                     StructuredData::DictionarySP args_sp) override;
+
+  llvm::SmallVector<AbstractMethodRequirement>
+  GetAbstractMethodRequirements() const override {
+    return llvm::SmallVector<AbstractMethodRequirement>({{"get_stackframes"}});
+  }
+
+  StructuredData::ArraySP GetStackFrames() override;
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index f769d3d29add7..7f7446cc00423 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -439,6 +439,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
   python::PythonObject Transform(lldb::ProcessSP arg) {
     return python::SWIGBridge::ToSWIGWrapper(arg);
   }
+  
+  python::PythonObject Transform(lldb::ThreadSP arg) {
+    return python::SWIGBridge::ToSWIGWrapper(arg);
+  }
 
   python::PythonObject Transform(lldb::ThreadPlanSP arg) {
     return python::SWIGBridge::ToSWIGWrapper(arg);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 73c5c72932ff1..9ef5ac4acb6a3 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1526,6 +1526,11 @@ ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() {
   return std::make_shared<ScriptedFramePythonInterface>(*this);
 }
 
+ScriptedFrameProviderInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedFrameProviderInterface() {
+  return std::make_shared<ScriptedFrameProviderPythonInterface>(*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 dedac280788f4..0dd5ae52c8955 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -101,6 +101,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
 
   lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
 
+  lldb::ScriptedFrameProviderInterfaceSP
+  CreateScriptedFrameProviderInterface() override;
+
   lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() override;
 
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 8c3e19725f8cb..acf126a548691 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -13,9 +13,13 @@
 #include "lldb/Core/Module.h"
 #include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Host/Host.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
 #include "lldb/Interpreter/OptionValueFileSpecList.h"
 #include "lldb/Interpreter/OptionValueProperties.h"
 #include "lldb/Interpreter/Property.h"
+#include "lldb/Interpreter/ScriptedFrameProvider.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Symbol/Function.h"
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/DynamicLoader.h"
@@ -45,6 +49,7 @@
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
@@ -1446,6 +1451,31 @@ StackFrameListSP Thread::GetStackFrameList() {
   return m_curr_frames_sp;
 }
 
+llvm::Expected<StackFrameListSP> Thread::GetScriptedFrameList() {
+  std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+
+  if (!m_frame_provider_sp)
+    return llvm::createStringError("No scripted frame provider has been set");
+
+  return m_frame_provider_sp->GetStackFrames();
+}
+
+void Thread::SetScriptedFrameProvider(const ScriptedMetadata &scripted_metadata) {
+  std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+
+  Status error;
+  m_frame_provider_sp = std::make_shared<ScriptedFrameProvider>(
+      shared_from_this(), scripted_metadata, error);
+
+  if (error.Fail())
+    m_frame_provider_sp.reset();
+}
+
+void Thread::ClearScriptedFrameProvider() {
+  std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+  m_frame_provider_sp.reset();
+}
+
 std::optional<addr_t> Thread::GetPreviousFrameZeroPC() {
   return m_prev_framezero_pc;
 }



More information about the lldb-commits mailing list