[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>
This commit is contained in:
parent
8321eaa037
commit
d584d00ed2
41
lldb/bindings/interface/SBFrameListExtensions.i
Normal file
41
lldb/bindings/interface/SBFrameListExtensions.i
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -222,6 +222,7 @@ public:
|
||||
protected:
|
||||
friend class SBBlock;
|
||||
friend class SBExecutionContext;
|
||||
friend class SBFrameList;
|
||||
friend class SBInstruction;
|
||||
friend class SBThread;
|
||||
friend class SBValue;
|
||||
|
||||
82
lldb/include/lldb/API/SBFrameList.h
Normal file
82
lldb/include/lldb/API/SBFrameList.h
Normal file
@ -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
|
||||
@ -81,6 +81,7 @@ protected:
|
||||
friend class SBFileSpec;
|
||||
friend class SBFileSpecList;
|
||||
friend class SBFrame;
|
||||
friend class SBFrameList;
|
||||
friend class SBFunction;
|
||||
friend class SBInstruction;
|
||||
friend class SBInstructionList;
|
||||
|
||||
@ -186,6 +186,8 @@ public:
|
||||
|
||||
lldb::SBFrame GetFrameAtIndex(uint32_t idx);
|
||||
|
||||
lldb::SBFrameList GetFrames();
|
||||
|
||||
lldb::SBFrame GetSelectedFrame();
|
||||
|
||||
lldb::SBFrame SetSelectedFrame(uint32_t frame_idx);
|
||||
@ -244,6 +246,7 @@ private:
|
||||
friend class SBSaveCoreOptions;
|
||||
friend class SBExecutionContext;
|
||||
friend class SBFrame;
|
||||
friend class SBFrameList;
|
||||
friend class SBProcess;
|
||||
friend class SBDebugger;
|
||||
friend class SBValue;
|
||||
|
||||
@ -101,6 +101,9 @@ public:
|
||||
/// 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;
|
||||
|
||||
@ -1295,6 +1295,8 @@ public:
|
||||
/// 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 @@ protected:
|
||||
return StructuredData::ObjectSP();
|
||||
}
|
||||
|
||||
lldb::StackFrameListSP GetStackFrameList();
|
||||
|
||||
void SetTemporaryResumeState(lldb::StateType new_state) {
|
||||
m_temporary_resume_state = new_state;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
97
lldb/source/API/SBFrameList.cpp
Normal file
97
lldb/source/API/SBFrameList.cpp
Normal file
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
3
lldb/test/API/python_api/frame_list/Makefile
Normal file
3
lldb/test/API/python_api/frame_list/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
194
lldb/test/API/python_api/frame_list/TestSBFrameList.py
Normal file
194
lldb/test/API/python_api/frame_list/TestSBFrameList.py
Normal file
@ -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",
|
||||
)
|
||||
22
lldb/test/API/python_api/frame_list/main.cpp
Normal file
22
lldb/test/API/python_api/frame_list/main.cpp
Normal file
@ -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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user