https://github.com/medismailben created https://github.com/llvm/llvm-project/pull/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. >From cb2519d3ce70755e5251a800b1d7b9088f13fc68 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Wed, 5 Nov 2025 12:56:34 -0800 Subject: [PATCH] [lldb] Introduce SBFrameList for lazy frame iteration 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 can now iterate, filter, and replace frames lazily without materializing the entire stack upfront. Signed-off-by: Med Ismail Bennani <[email protected]> --- .../interface/SBFrameListDocstrings.i | 13 ++ .../interface/SBFrameListExtensions.i | 41 ++++ lldb/bindings/interface/SBThreadExtensions.i | 3 +- lldb/bindings/interfaces.swig | 3 + lldb/include/lldb/API/LLDB.h | 1 + lldb/include/lldb/API/SBDefines.h | 1 + lldb/include/lldb/API/SBFrame.h | 1 + lldb/include/lldb/API/SBFrameList.h | 57 +++++ lldb/include/lldb/API/SBStream.h | 1 + lldb/include/lldb/API/SBThread.h | 3 + lldb/include/lldb/Target/StackFrameList.h | 3 + lldb/include/lldb/Target/Thread.h | 4 +- lldb/source/API/CMakeLists.txt | 1 + lldb/source/API/SBFrameList.cpp | 111 ++++++++++ lldb/source/API/SBThread.cpp | 21 ++ lldb/test/API/python_api/frame_list/Makefile | 3 + .../python_api/frame_list/TestSBFrameList.py | 194 ++++++++++++++++++ lldb/test/API/python_api/frame_list/main.cpp | 22 ++ 18 files changed, 480 insertions(+), 3 deletions(-) create mode 100644 lldb/bindings/interface/SBFrameListDocstrings.i create mode 100644 lldb/bindings/interface/SBFrameListExtensions.i create mode 100644 lldb/include/lldb/API/SBFrameList.h create mode 100644 lldb/source/API/SBFrameList.cpp create mode 100644 lldb/test/API/python_api/frame_list/Makefile create mode 100644 lldb/test/API/python_api/frame_list/TestSBFrameList.py create mode 100644 lldb/test/API/python_api/frame_list/main.cpp diff --git a/lldb/bindings/interface/SBFrameListDocstrings.i b/lldb/bindings/interface/SBFrameListDocstrings.i new file mode 100644 index 0000000000000..2ca10b3b4c72b --- /dev/null +++ b/lldb/bindings/interface/SBFrameListDocstrings.i @@ -0,0 +1,13 @@ +%feature("docstring", +"Represents a list of :py:class:`SBFrame` objects." +) lldb::SBFrameList; + +%feature("autodoc", "GetSize(SBFrameList self) -> uint32_t") lldb::SBFrameList::GetSize; +%feature("docstring", " + Returns the number of frames in the list." +) lldb::SBFrameList::GetSize; + +%feature("autodoc", "GetFrameAtIndex(SBFrameList self, uint32_t idx) -> SBFrame") lldb::SBFrameList::GetFrameAtIndex; +%feature("docstring", " + Returns the frame at the given index." +) lldb::SBFrameList::GetFrameAtIndex; \ No newline at end of file diff --git a/lldb/bindings/interface/SBFrameListExtensions.i b/lldb/bindings/interface/SBFrameListExtensions.i new file mode 100644 index 0000000000000..61f05add9c9a5 --- /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 +} \ No newline at end of file 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..5fe058e33e619 100644 --- a/lldb/bindings/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -39,6 +39,7 @@ %include "./interface/SBFileSpecListDocstrings.i" %include "./interface/SBFormatDocstrings.i" %include "./interface/SBFrameDocstrings.i" +%include "./interface/SBFrameListDocstrings.i" %include "./interface/SBFunctionDocstrings.i" %include "./interface/SBHostOSDocstrings.i" %include "./interface/SBInstructionDocstrings.i" @@ -119,6 +120,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 +195,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..4b875f848829e --- /dev/null +++ b/lldb/include/lldb/API/SBFrameList.h @@ -0,0 +1,57 @@ +//===------------------------------------------------------------*- 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_API_SBFRAMELIST_H +#define LLDB_API_SBFRAMELIST_H + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +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; + + uint32_t GetSize() const; + + lldb::SBFrame GetFrameAtIndex(uint32_t idx) const; + + lldb::SBThread GetThread() const; + + void Clear(); + + void Append(const lldb::SBFrame &frame); + + void Append(const lldb::SBFrameList &frame_list); + + 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); + + 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 e9fe5858d125e..3a78026c6687b 100644 --- a/lldb/include/lldb/API/SBThread.h +++ b/lldb/include/lldb/API/SBThread.h @@ -178,6 +178,8 @@ class LLDB_API SBThread { lldb::SBFrame GetFrameAtIndex(uint32_t idx); + lldb::SBFrameList GetFrames(); + lldb::SBFrame GetSelectedFrame(); lldb::SBFrame SetSelectedFrame(uint32_t frame_idx); @@ -236,6 +238,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..77f58e3ffc080 --- /dev/null +++ b/lldb/source/API/SBFrameList.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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(); +} + +void SBFrameList::Append(const SBFrame &frame) { + LLDB_INSTRUMENT_VA(this, frame); + + // Note: StackFrameList doesn't have an Append method, so this is a no-op + // This method is kept for API consistency with other SB*List classes +} + +void SBFrameList::Append(const SBFrameList &frame_list) { + LLDB_INSTRUMENT_VA(this, frame_list); + + // Note: StackFrameList doesn't have an Append method, so this is a no-op + // This method is kept for API consistency with other SB*List classes +} + +bool SBFrameList::GetDescription(SBStream &description) const { + LLDB_INSTRUMENT_VA(this, description); + + Stream &strm = description.ref(); + if (m_opaque_sp) { + m_opaque_sp->Dump(&strm); + return true; + } + return false; +} diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 4e4aa48bc9a2e..2351d787c74e9 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" @@ -1079,6 +1080,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..2bb9ce046a907 --- /dev/null +++ b/lldb/test/API/python_api/frame_list/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules \ No newline at end of file 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..a10ae5cf11f80 --- /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; +} \ No newline at end of file _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
