Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lldb/bindings/lua/lua-typemaps.swig
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,27 @@ LLDB_NUMBER_TYPEMAP(enum SWIGTYPE);
$1 = (char *)malloc($2);
}

// Disable default type checking for this method to avoid SWIG dispatch issues.
//
// Problem: SBThread::GetStopDescription has two overloads:
// 1. GetStopDescription(char* dst_or_null, size_t dst_len)
// 2. GetStopDescription(lldb::SBStream& stream)
//
// SWIG generates a dispatch function to select the correct overload based on argument types.
// see https://www.swig.org/Doc4.0/SWIGDocumentation.html#Typemaps_overloading.
// However, this dispatcher doesn't consider typemaps that transform function signatures.
//
// In lua, our typemap converts GetStopDescription(char*, size_t) to GetStopDescription(int).
// The dispatcher still checks against the original (char*, size_t) signature instead of
// the transformed (int) signature, causing type matching to fail.
// This only affects SBThread::GetStopDescription since the type check also matches
// the argument name, which is unique to this function.
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) (char *dst_or_null, size_t dst_len) ""

%typemap(argout) (char *dst_or_null, size_t dst_len) {
lua_pop(L, 1); // Blow away the previous result
lua_pushlstring(L, (const char *)$1, $result);
llvm::StringRef ref($1);
lua_pushlstring(L, (const char *)$1, ref.size());
free($1);
// SWIG_arg was already incremented
}
Expand Down
18 changes: 18 additions & 0 deletions lldb/bindings/python/python-typemaps.swig
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,24 @@ AND call SWIG_fail at the same time, because it will result in a double free.
}
$1 = (char *)malloc($2);
}

// Disable default type checking for this method to avoid SWIG dispatch issues.
//
// Problem: SBThread::GetStopDescription has two overloads:
// 1. GetStopDescription(char* dst_or_null, size_t dst_len)
// 2. GetStopDescription(lldb::SBStream& stream)
//
// SWIG generates a dispatch function to select the correct overload based on argument types.
// see https://www.swig.org/Doc4.0/SWIGDocumentation.html#Typemaps_overloading.
// However, this dispatcher doesn't consider typemaps that transform function signatures.
//
// In Python, our typemap converts GetStopDescription(char*, size_t) to GetStopDescription(int).
// The dispatcher still checks against the original (char*, size_t) signature instead of
// the transformed (int) signature, causing type matching to fail.
// This only affects SBThread::GetStopDescription since the type check also matches
// the argument name, which is unique to this function.
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) (char *dst_or_null, size_t dst_len) ""

%typemap(argout) (char *dst_or_null, size_t dst_len) {
Py_XDECREF($result); /* Blow away any previous result */
llvm::StringRef ref($1);
Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ class LLDB_API SBThread {
SBThreadCollection
GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type);

/// Gets a human-readable description of why the thread stopped.
///
/// \param stream Output stream to receive the stop description text
/// \return
/// true if obtained and written to the stream,
// false if there was an error retrieving the description.
bool GetStopDescription(lldb::SBStream &stream) const;

size_t GetStopDescription(char *dst_or_null, size_t dst_len);

SBValue GetStopReturnValue();
Expand Down
35 changes: 29 additions & 6 deletions lldb/source/API/SBThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,34 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) {
return threads;
}

size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
LLDB_INSTRUMENT_VA(this, dst, dst_len);
bool SBThread::GetStopDescription(lldb::SBStream &stream) const {
LLDB_INSTRUMENT_VA(this, stream);

if (!m_opaque_sp)
return false;

llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
if (!exe_ctx) {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return false;
}

if (!exe_ctx->HasThreadScope())
return false;

Stream &strm = stream.ref();
const std::string stop_desc = exe_ctx->GetThreadPtr()->GetStopDescription();
strm.PutCString(stop_desc);

return true;
}

size_t SBThread::GetStopDescription(char *dst_or_null, size_t dst_len) {
LLDB_INSTRUMENT_VA(this, dst_or_null, dst_len);

if (dst)
*dst = 0;
if (dst_or_null)
*dst_or_null = 0;

llvm::Expected<StoppedExecutionContext> exe_ctx =
GetStoppedExecutionContext(m_opaque_sp);
Expand All @@ -259,8 +282,8 @@ size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
if (thread_stop_desc.empty())
return 0;

if (dst)
return ::snprintf(dst, dst_len, "%s", thread_stop_desc.c_str()) + 1;
if (dst_or_null)
return ::snprintf(dst_or_null, dst_len, "%s", thread_stop_desc.c_str()) + 1;

// NULL dst passed in, return the length needed to contain the
// description.
Expand Down
25 changes: 25 additions & 0 deletions lldb/test/API/lua_api/TestThreadAPI.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
_T = require('lua_lldb_test').create_test('TestThreadAPI')

function _T:TestGetStopDescription()
local target = self:create_target()
local breakpoint = target:BreakpointCreateByName("main", "a.out")
assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)

local process = target:LaunchSimple({ 'arg1', 'arg2' }, nil, nil)
local thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
assertNotNil(thread)
assertTrue(thread:IsValid())

assertEqual("breakpoint", thread:GetStopDescription(string.len("breakpoint") + 1))
assertEqual("break", thread:GetStopDescription(string.len("break") + 1))
assertEqual("b", thread:GetStopDescription(string.len("b") + 1))
assertEqual("breakpoint 1.1", thread:GetStopDescription(string.len("breakpoint 1.1") + 100))

-- Test stream variation
local stream = lldb.SBStream()
assertTrue(thread:GetStopDescription(stream))
assertNotNil(stream)
assertEqual("breakpoint 1.1", stream:GetData())
end

os.exit(_T:run())
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def fuzz_obj(obj):
obj.GetStopReasonDataCount()
obj.GetStopReasonDataAtIndex(100)
obj.GetStopDescription(256)
obj.GetStopDescription(lldb.SBStream())
obj.GetThreadID()
obj.GetIndexID()
obj.GetName()
Expand Down
5 changes: 5 additions & 0 deletions lldb/test/API/python_api/thread/TestThreadAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ def get_stop_description(self):
"breakpoint 1.1", thread.GetStopDescription(len("breakpoint 1.1") + 100)
)

# Test the stream variation
stream = lldb.SBStream()
self.assertTrue(thread.GetStopDescription(stream))
self.assertEqual("breakpoint 1.1", stream.GetData())

def step_out_of_malloc_into_function_b(self, exe_name):
"""Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""
exe = self.getBuildArtifact(exe_name)
Expand Down