diff options
| author | Med Ismail Bennani <ismail@bennani.ma> | 2025-11-05 16:02:02 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-05 16:02:02 -0800 |
| commit | d584d00ed250e547c9910e0a93b7f9d07f2e71c3 (patch) | |
| tree | 661d4e93699403290f2528bb9f6c8cb5abb64427 /lldb/test/API/python_api | |
| parent | 8321eaa037b994f22351af67a3e7d8bd4a54ae0c (diff) | |
| download | llvm-d584d00ed250e547c9910e0a93b7f9d07f2e71c3.tar.gz llvm-d584d00ed250e547c9910e0a93b7f9d07f2e71c3.tar.bz2 llvm-d584d00ed250e547c9910e0a93b7f9d07f2e71c3.zip | |
[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 <ismail@bennani.ma>
Diffstat (limited to 'lldb/test/API/python_api')
| -rw-r--r-- | lldb/test/API/python_api/frame_list/Makefile | 3 | ||||
| -rw-r--r-- | lldb/test/API/python_api/frame_list/TestSBFrameList.py | 194 | ||||
| -rw-r--r-- | lldb/test/API/python_api/frame_list/main.cpp | 22 |
3 files changed, 219 insertions, 0 deletions
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 000000000000..99998b20bcb0 --- /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 000000000000..f348ce492e54 --- /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 000000000000..e39944654a23 --- /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; +} |
