Author: Med Ismail Bennani
Date: 2025-11-05T16:02:02-08:00
New Revision: d584d00ed250e547c9910e0a93b7f9d07f2e71c3

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

LOG: [lldb] Introduce SBFrameList for lazy frame iteration (#166651)

This patch introduces `SBFrameList`, a new SBAPI class that allows
iterating over stack frames lazily without calling
`SBThread::GetFrameAtIndex` in a loop.

The new `SBThread::GetFrames()` method returns an `SBFrameList` that
supports Python iteration (`for frame in frame_list:`), indexing
(`frame_list[0]`, `frame_list[-1]`), and length queries (`len()`).

The implementation uses `StackFrameListSP` as the opaque pointer,
sharing the thread's underlying frame list to ensure frames are
materialized on-demand.

This is particularly useful for ScriptedFrameProviders, where user
scripts will be to iterate, filter, and replace frames lazily without
materializing the entire stack upfront.

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

Added: 
    lldb/bindings/interface/SBFrameListExtensions.i
    lldb/include/lldb/API/SBFrameList.h
    lldb/source/API/SBFrameList.cpp
    lldb/test/API/python_api/frame_list/Makefile
    lldb/test/API/python_api/frame_list/TestSBFrameList.py
    lldb/test/API/python_api/frame_list/main.cpp

Modified: 
    lldb/bindings/interface/SBThreadExtensions.i
    lldb/bindings/interfaces.swig
    lldb/include/lldb/API/LLDB.h
    lldb/include/lldb/API/SBDefines.h
    lldb/include/lldb/API/SBFrame.h
    lldb/include/lldb/API/SBStream.h
    lldb/include/lldb/API/SBThread.h
    lldb/include/lldb/Target/StackFrameList.h
    lldb/include/lldb/Target/Thread.h
    lldb/source/API/CMakeLists.txt
    lldb/source/API/SBThread.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBFrameListExtensions.i 
b/lldb/bindings/interface/SBFrameListExtensions.i
new file mode 100644
index 0000000000000..1c6ac8d50a54c
--- /dev/null
+++ b/lldb/bindings/interface/SBFrameListExtensions.i
@@ -0,0 +1,41 @@
+%extend lldb::SBFrameList {
+
+#ifdef SWIGPYTHON
+       %nothreadallow;
+#endif
+       std::string lldb::SBFrameList::__str__ (){
+           lldb::SBStream description;
+           if (!$self->GetDescription(description))
+               return std::string("<empty> lldb.SBFrameList()");
+           const char *desc = description.GetData();
+           size_t desc_len = description.GetSize();
+           if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] 
== '\r'))
+               --desc_len;
+           return std::string(desc, desc_len);
+       }
+#ifdef SWIGPYTHON
+       %clearnothreadallow;
+#endif
+
+#ifdef SWIGPYTHON
+    %pythoncode %{
+        def __iter__(self):
+            '''Iterate over all frames in a lldb.SBFrameList object.'''
+            return lldb_iter(self, 'GetSize', 'GetFrameAtIndex')
+
+        def __len__(self):
+            return int(self.GetSize())
+
+        def __getitem__(self, key):
+            if type(key) is not int:
+                return None
+            if key < 0:
+                count = len(self)
+                if -count <= key < count:
+                    key %= count
+
+            frame = self.GetFrameAtIndex(key)
+            return frame if frame.IsValid() else None
+    %}
+#endif
+}

diff  --git a/lldb/bindings/interface/SBThreadExtensions.i 
b/lldb/bindings/interface/SBThreadExtensions.i
index 4ec9f10b1a256..c9ae4103d7b60 100644
--- a/lldb/bindings/interface/SBThreadExtensions.i
+++ b/lldb/bindings/interface/SBThreadExtensions.i
@@ -41,7 +41,8 @@ STRING_EXTENSION_OUTSIDE(SBThread)
         def get_thread_frames(self):
             '''An accessor function that returns a list() that contains all 
frames in a lldb.SBThread object.'''
             frames = []
-            for frame in self:
+            frame_list = self.GetFrames()
+            for frame in frame_list:
                 frames.append(frame)
             return frames
 

diff  --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig
index b3d44979c916c..fddbedf02e835 100644
--- a/lldb/bindings/interfaces.swig
+++ b/lldb/bindings/interfaces.swig
@@ -119,6 +119,7 @@
 %include "lldb/API/SBFileSpecList.h"
 %include "lldb/API/SBFormat.h"
 %include "lldb/API/SBFrame.h"
+%include "lldb/API/SBFrameList.h"
 %include "lldb/API/SBFunction.h"
 %include "lldb/API/SBHostOS.h"
 %include "lldb/API/SBInstruction.h"
@@ -193,6 +194,7 @@
 %include "./interface/SBFileSpecExtensions.i"
 %include "./interface/SBFileSpecListExtensions.i"
 %include "./interface/SBFrameExtensions.i"
+%include "./interface/SBFrameListExtensions.i"
 %include "./interface/SBFunctionExtensions.i"
 %include "./interface/SBInstructionExtensions.i"
 %include "./interface/SBInstructionListExtensions.i"

diff  --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h
index 6485f35302a1c..6ac35bb4a364b 100644
--- a/lldb/include/lldb/API/LLDB.h
+++ b/lldb/include/lldb/API/LLDB.h
@@ -37,6 +37,7 @@
 #include "lldb/API/SBFileSpecList.h"
 #include "lldb/API/SBFormat.h"
 #include "lldb/API/SBFrame.h"
+#include "lldb/API/SBFrameList.h"
 #include "lldb/API/SBFunction.h"
 #include "lldb/API/SBHostOS.h"
 #include "lldb/API/SBInstruction.h"

diff  --git a/lldb/include/lldb/API/SBDefines.h 
b/lldb/include/lldb/API/SBDefines.h
index 85f6bbeea5bf9..5fcc685050c0b 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -76,6 +76,7 @@ class LLDB_API SBFileSpec;
 class LLDB_API SBFileSpecList;
 class LLDB_API SBFormat;
 class LLDB_API SBFrame;
+class LLDB_API SBFrameList;
 class LLDB_API SBFunction;
 class LLDB_API SBHostOS;
 class LLDB_API SBInstruction;

diff  --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 92917e57fc125..5283cdfe53faa 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -222,6 +222,7 @@ class LLDB_API SBFrame {
 protected:
   friend class SBBlock;
   friend class SBExecutionContext;
+  friend class SBFrameList;
   friend class SBInstruction;
   friend class SBThread;
   friend class SBValue;

diff  --git a/lldb/include/lldb/API/SBFrameList.h 
b/lldb/include/lldb/API/SBFrameList.h
new file mode 100644
index 0000000000000..dba1c1de5d191
--- /dev/null
+++ b/lldb/include/lldb/API/SBFrameList.h
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_API_SBFRAMELIST_H
+#define LLDB_API_SBFRAMELIST_H
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+
+/// Represents a list of SBFrame objects.
+///
+/// SBFrameList provides a way to iterate over stack frames lazily,
+/// materializing frames on-demand as they are accessed. This is more
+/// efficient than eagerly creating all frames upfront.
+class LLDB_API SBFrameList {
+public:
+  SBFrameList();
+
+  SBFrameList(const lldb::SBFrameList &rhs);
+
+  ~SBFrameList();
+
+  const lldb::SBFrameList &operator=(const lldb::SBFrameList &rhs);
+
+  explicit operator bool() const;
+
+  bool IsValid() const;
+
+  /// Returns the number of frames in the list.
+  uint32_t GetSize() const;
+
+  /// Returns the frame at the given index.
+  ///
+  /// \param[in] idx
+  ///     The index of the frame to retrieve (0-based).
+  ///
+  /// \return
+  ///     An SBFrame object for the frame at the specified index.
+  ///     Returns an invalid SBFrame if idx is out of range.
+  lldb::SBFrame GetFrameAtIndex(uint32_t idx) const;
+
+  /// Get the thread associated with this frame list.
+  ///
+  /// \return
+  ///     An SBThread object representing the thread.
+  lldb::SBThread GetThread() const;
+
+  /// Clear all frames from this list.
+  void Clear();
+
+  /// Get a description of this frame list.
+  ///
+  /// \param[in] description
+  ///     The stream to write the description to.
+  ///
+  /// \return
+  ///     True if the description was successfully written.
+  bool GetDescription(lldb::SBStream &description) const;
+
+protected:
+  friend class SBThread;
+
+private:
+  SBFrameList(const lldb::StackFrameListSP &frame_list_sp);
+
+  void SetFrameList(const lldb::StackFrameListSP &frame_list_sp);
+
+  // This needs to be a shared_ptr since an SBFrameList can be passed to
+  // scripting affordances like ScriptedFrameProviders but also out of
+  // convenience because Thread::GetStackFrameList returns a StackFrameListSP.
+  lldb::StackFrameListSP m_opaque_sp;
+};
+
+} // namespace lldb
+
+#endif // LLDB_API_SBFRAMELIST_H

diff  --git a/lldb/include/lldb/API/SBStream.h 
b/lldb/include/lldb/API/SBStream.h
index d230da6123fb3..21f9d21e0e717 100644
--- a/lldb/include/lldb/API/SBStream.h
+++ b/lldb/include/lldb/API/SBStream.h
@@ -81,6 +81,7 @@ class LLDB_API SBStream {
   friend class SBFileSpec;
   friend class SBFileSpecList;
   friend class SBFrame;
+  friend class SBFrameList;
   friend class SBFunction;
   friend class SBInstruction;
   friend class SBInstructionList;

diff  --git a/lldb/include/lldb/API/SBThread.h 
b/lldb/include/lldb/API/SBThread.h
index 2411dfd376519..f6a6d19935b83 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -186,6 +186,8 @@ class LLDB_API SBThread {
 
   lldb::SBFrame GetFrameAtIndex(uint32_t idx);
 
+  lldb::SBFrameList GetFrames();
+
   lldb::SBFrame GetSelectedFrame();
 
   lldb::SBFrame SetSelectedFrame(uint32_t frame_idx);
@@ -244,6 +246,7 @@ class LLDB_API SBThread {
   friend class SBSaveCoreOptions;
   friend class SBExecutionContext;
   friend class SBFrame;
+  friend class SBFrameList;
   friend class SBProcess;
   friend class SBDebugger;
   friend class SBValue;

diff  --git a/lldb/include/lldb/Target/StackFrameList.h 
b/lldb/include/lldb/Target/StackFrameList.h
index ea9aab86b8ea1..5b0df0ddb3e29 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -101,6 +101,9 @@ class StackFrameList {
   /// Returns whether we have currently fetched all the frames of a stack.
   bool WereAllFramesFetched() const;
 
+  /// Get the thread associated with this frame list.
+  Thread &GetThread() const { return m_thread; }
+
 protected:
   friend class Thread;
   friend class ScriptedThread;

diff  --git a/lldb/include/lldb/Target/Thread.h 
b/lldb/include/lldb/Target/Thread.h
index 688c056da2633..841f80cd1b1eb 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -1295,6 +1295,8 @@ class Thread : public 
std::enable_shared_from_this<Thread>,
   ///     an empty std::optional is returned in that case.
   std::optional<lldb::addr_t> GetPreviousFrameZeroPC();
 
+  lldb::StackFrameListSP GetStackFrameList();
+
 protected:
   friend class ThreadPlan;
   friend class ThreadList;
@@ -1336,8 +1338,6 @@ class Thread : public 
std::enable_shared_from_this<Thread>,
     return StructuredData::ObjectSP();
   }
 
-  lldb::StackFrameListSP GetStackFrameList();
-
   void SetTemporaryResumeState(lldb::StateType new_state) {
     m_temporary_resume_state = new_state;
   }

diff  --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt
index ce59ee505cd3d..ac47580d60840 100644
--- a/lldb/source/API/CMakeLists.txt
+++ b/lldb/source/API/CMakeLists.txt
@@ -69,6 +69,7 @@ add_lldb_library(liblldb SHARED ${option_framework}
   SBFileSpecList.cpp
   SBFormat.cpp
   SBFrame.cpp
+  SBFrameList.cpp
   SBFunction.cpp
   SBHostOS.cpp
   SBInstruction.cpp

diff  --git a/lldb/source/API/SBFrameList.cpp b/lldb/source/API/SBFrameList.cpp
new file mode 100644
index 0000000000000..d5fa955c10f70
--- /dev/null
+++ b/lldb/source/API/SBFrameList.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/API/SBFrameList.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Instrumentation.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFrameList::SBFrameList() : m_opaque_sp() { LLDB_INSTRUMENT_VA(this); }
+
+SBFrameList::SBFrameList(const SBFrameList &rhs)
+    : m_opaque_sp(rhs.m_opaque_sp) {
+  LLDB_INSTRUMENT_VA(this, rhs);
+}
+
+SBFrameList::~SBFrameList() = default;
+
+const SBFrameList &SBFrameList::operator=(const SBFrameList &rhs) {
+  LLDB_INSTRUMENT_VA(this, rhs);
+
+  if (this != &rhs)
+    m_opaque_sp = rhs.m_opaque_sp;
+  return *this;
+}
+
+SBFrameList::SBFrameList(const lldb::StackFrameListSP &frame_list_sp)
+    : m_opaque_sp(frame_list_sp) {}
+
+void SBFrameList::SetFrameList(const lldb::StackFrameListSP &frame_list_sp) {
+  m_opaque_sp = frame_list_sp;
+}
+
+SBFrameList::operator bool() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return m_opaque_sp.get() != nullptr;
+}
+
+bool SBFrameList::IsValid() const {
+  LLDB_INSTRUMENT_VA(this);
+  return this->operator bool();
+}
+
+uint32_t SBFrameList::GetSize() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (m_opaque_sp)
+    return m_opaque_sp->GetNumFrames();
+  return 0;
+}
+
+SBFrame SBFrameList::GetFrameAtIndex(uint32_t idx) const {
+  LLDB_INSTRUMENT_VA(this, idx);
+
+  SBFrame sb_frame;
+  if (m_opaque_sp)
+    sb_frame.SetFrameSP(m_opaque_sp->GetFrameAtIndex(idx));
+  return sb_frame;
+}
+
+SBThread SBFrameList::GetThread() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  SBThread sb_thread;
+  if (m_opaque_sp)
+    sb_thread.SetThread(m_opaque_sp->GetThread().shared_from_this());
+  return sb_thread;
+}
+
+void SBFrameList::Clear() {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (m_opaque_sp)
+    m_opaque_sp->Clear();
+}
+
+bool SBFrameList::GetDescription(SBStream &description) const {
+  LLDB_INSTRUMENT_VA(this, description);
+
+  if (!m_opaque_sp)
+    return false;
+
+  Stream &strm = description.ref();
+  m_opaque_sp->Dump(&strm);
+  return true;
+}

diff  --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index f58a1b52afa91..f32c5c56444cd 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -14,6 +14,7 @@
 #include "lldb/API/SBFileSpec.h"
 #include "lldb/API/SBFormat.h"
 #include "lldb/API/SBFrame.h"
+#include "lldb/API/SBFrameList.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStructuredData.h"
@@ -1102,6 +1103,26 @@ SBFrame SBThread::GetFrameAtIndex(uint32_t idx) {
   return sb_frame;
 }
 
+lldb::SBFrameList SBThread::GetFrames() {
+  LLDB_INSTRUMENT_VA(this);
+
+  SBFrameList sb_frame_list;
+  llvm::Expected<StoppedExecutionContext> exe_ctx =
+      GetStoppedExecutionContext(m_opaque_sp);
+  if (!exe_ctx) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+    return SBFrameList();
+  }
+
+  if (exe_ctx->HasThreadScope()) {
+    StackFrameListSP frame_list_sp =
+        exe_ctx->GetThreadPtr()->GetStackFrameList();
+    sb_frame_list.SetFrameList(frame_list_sp);
+  }
+
+  return sb_frame_list;
+}
+
 lldb::SBFrame SBThread::GetSelectedFrame() {
   LLDB_INSTRUMENT_VA(this);
 

diff  --git a/lldb/test/API/python_api/frame_list/Makefile 
b/lldb/test/API/python_api/frame_list/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/python_api/frame_list/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/python_api/frame_list/TestSBFrameList.py 
b/lldb/test/API/python_api/frame_list/TestSBFrameList.py
new file mode 100644
index 0000000000000..f348ce492e547
--- /dev/null
+++ b/lldb/test/API/python_api/frame_list/TestSBFrameList.py
@@ -0,0 +1,194 @@
+"""
+Test SBFrameList API.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class FrameListAPITestCase(TestBase):
+    def test_frame_list_api(self):
+        """Test SBThread.GetFrames() returns a valid SBFrameList."""
+        self.build()
+        self.frame_list_api()
+
+    def test_frame_list_iterator(self):
+        """Test SBFrameList iterator functionality."""
+        self.build()
+        self.frame_list_iterator()
+
+    def test_frame_list_indexing(self):
+        """Test SBFrameList indexing and length."""
+        self.build()
+        self.frame_list_indexing()
+
+    def test_frame_list_get_thread(self):
+        """Test SBFrameList.GetThread() returns correct thread."""
+        self.build()
+        self.frame_list_get_thread()
+
+    def setUp(self):
+        TestBase.setUp(self)
+        self.main_source = "main.cpp"
+
+    def frame_list_api(self):
+        """Test SBThread.GetFrames() returns a valid SBFrameList."""
+        exe = self.getBuildArtifact("a.out")
+
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Set break point at this line", 
lldb.SBFileSpec(self.main_source)
+        )
+
+        self.assertTrue(
+            thread.IsValid(), "There should be a thread stopped due to 
breakpoint"
+        )
+
+        # Test GetFrames() returns a valid SBFrameList
+        frame_list = thread.GetFrames()
+        self.assertTrue(frame_list.IsValid(), "Frame list should be valid")
+        self.assertGreater(
+            frame_list.GetSize(), 0, "Frame list should have at least one 
frame"
+        )
+
+        # Verify frame list size matches thread frame count
+        self.assertEqual(
+            frame_list.GetSize(),
+            thread.GetNumFrames(),
+            "Frame list size should match thread frame count",
+        )
+
+        # Verify frames are the same
+        for i in range(frame_list.GetSize()):
+            frame_from_list = frame_list.GetFrameAtIndex(i)
+            frame_from_thread = thread.GetFrameAtIndex(i)
+            self.assertTrue(
+                frame_from_list.IsValid(), f"Frame {i} from list should be 
valid"
+            )
+            self.assertEqual(
+                frame_from_list.GetPC(),
+                frame_from_thread.GetPC(),
+                f"Frame {i} PC should match",
+            )
+
+    def frame_list_iterator(self):
+        """Test SBFrameList iterator functionality."""
+        exe = self.getBuildArtifact("a.out")
+
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Set break point at this line", 
lldb.SBFileSpec(self.main_source)
+        )
+
+        self.assertTrue(
+            thread.IsValid(), "There should be a thread stopped due to 
breakpoint"
+        )
+
+        frame_list = thread.GetFrames()
+
+        # Test iteration
+        frame_count = 0
+        for frame in frame_list:
+            self.assertTrue(frame.IsValid(), "Each frame should be valid")
+            frame_count += 1
+
+        self.assertEqual(
+            frame_count,
+            frame_list.GetSize(),
+            "Iterator should visit all frames",
+        )
+
+        # Test that we can iterate multiple times
+        second_count = 0
+        for frame in frame_list:
+            second_count += 1
+
+        self.assertEqual(
+            frame_count, second_count, "Should be able to iterate multiple 
times"
+        )
+
+    def frame_list_indexing(self):
+        """Test SBFrameList indexing and length."""
+        exe = self.getBuildArtifact("a.out")
+
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Set break point at this line", 
lldb.SBFileSpec(self.main_source)
+        )
+
+        self.assertTrue(
+            thread.IsValid(), "There should be a thread stopped due to 
breakpoint"
+        )
+
+        frame_list = thread.GetFrames()
+
+        # Test len()
+        self.assertEqual(
+            len(frame_list), frame_list.GetSize(), "len() should return frame 
count"
+        )
+
+        # Test positive indexing
+        first_frame = frame_list[0]
+        self.assertTrue(first_frame.IsValid(), "First frame should be valid")
+        self.assertEqual(
+            first_frame.GetPC(),
+            thread.GetFrameAtIndex(0).GetPC(),
+            "Indexed frame should match",
+        )
+
+        # Test negative indexing
+        if len(frame_list) > 0:
+            last_frame = frame_list[-1]
+            self.assertTrue(last_frame.IsValid(), "Last frame should be valid")
+            self.assertEqual(
+                last_frame.GetPC(),
+                thread.GetFrameAtIndex(len(frame_list) - 1).GetPC(),
+                "Negative indexing should work",
+            )
+
+        # Test out of bounds returns None
+        out_of_bounds = frame_list[10000]
+        self.assertIsNone(out_of_bounds, "Out of bounds index should return 
None")
+
+        # Test bool conversion
+        self.assertTrue(bool(frame_list), "Non-empty frame list should be 
truthy")
+
+        # Test Clear()
+        frame_list.Clear()
+        # Note: Clear() clears the underlying StackFrameList cache,
+        # but the frame list object itself should still be valid
+        self.assertTrue(
+            frame_list.IsValid(), "Frame list should still be valid after 
Clear()"
+        )
+
+    def frame_list_get_thread(self):
+        """Test SBFrameList.GetThread() returns correct thread."""
+        exe = self.getBuildArtifact("a.out")
+
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Set break point at this line", 
lldb.SBFileSpec(self.main_source)
+        )
+
+        self.assertTrue(
+            thread.IsValid(), "There should be a thread stopped due to 
breakpoint"
+        )
+
+        frame_list = thread.GetFrames()
+        self.assertTrue(frame_list.IsValid(), "Frame list should be valid")
+
+        # Test GetThread() returns the correct thread
+        thread_from_list = frame_list.GetThread()
+        self.assertTrue(
+            thread_from_list.IsValid(), "Thread from frame list should be 
valid"
+        )
+        self.assertEqual(
+            thread_from_list.GetThreadID(),
+            thread.GetThreadID(),
+            "Frame list should return the correct thread",
+        )
+
+        # Verify it's the same thread object
+        self.assertEqual(
+            thread_from_list.GetProcess().GetProcessID(),
+            thread.GetProcess().GetProcessID(),
+            "Thread should belong to same process",
+        )

diff  --git a/lldb/test/API/python_api/frame_list/main.cpp 
b/lldb/test/API/python_api/frame_list/main.cpp
new file mode 100644
index 0000000000000..e39944654a23e
--- /dev/null
+++ b/lldb/test/API/python_api/frame_list/main.cpp
@@ -0,0 +1,22 @@
+#include <stdio.h>
+
+int c(int val) {
+  // Set break point at this line
+  return val + 3;
+}
+
+int b(int val) {
+  int result = c(val);
+  return result;
+}
+
+int a(int val) {
+  int result = b(val);
+  return result;
+}
+
+int main() {
+  int result = a(1);
+  printf("Result: %d\n", result);
+  return 0;
+}


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

Reply via email to