Skip to content

Commit c46bfed

Browse files
authored
[lldb] Add alternative SBThread::GetStopDescription (#165379)
the function signature for `GetStopDescription` is `lldb::SBThread::GetStopDescription(char *dst_or_null, size_t len)`. To get a description you need to call the function first time to get the buffer size. a second time to get the description. This is little worse from the python size as the signature is `lldb.SBThread.GetStopDescription(int: len) -> list[str]` the user has to pass the max size as possible with no way of checking if it is enough. This patch adds a new api `lldb.SBThread.GetStopDescription(desc: lldb.SBStream()) -> bool` `bool lldb::SBThread::GetStopDescription(lldb::SBStream &description)` which handles this case. Adds new Test case for lua.
1 parent 6c1678a commit c46bfed

File tree

7 files changed

+105
-7
lines changed

7 files changed

+105
-7
lines changed

lldb/bindings/lua/lua-typemaps.swig

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,27 @@ LLDB_NUMBER_TYPEMAP(enum SWIGTYPE);
121121
$1 = (char *)malloc($2);
122122
}
123123

124+
// Disable default type checking for this method to avoid SWIG dispatch issues.
125+
//
126+
// Problem: SBThread::GetStopDescription has two overloads:
127+
// 1. GetStopDescription(char* dst_or_null, size_t dst_len)
128+
// 2. GetStopDescription(lldb::SBStream& stream)
129+
//
130+
// SWIG generates a dispatch function to select the correct overload based on argument types.
131+
// see https://www.swig.org/Doc4.0/SWIGDocumentation.html#Typemaps_overloading.
132+
// However, this dispatcher doesn't consider typemaps that transform function signatures.
133+
//
134+
// In lua, our typemap converts GetStopDescription(char*, size_t) to GetStopDescription(int).
135+
// The dispatcher still checks against the original (char*, size_t) signature instead of
136+
// the transformed (int) signature, causing type matching to fail.
137+
// This only affects SBThread::GetStopDescription since the type check also matches
138+
// the argument name, which is unique to this function.
139+
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) (char *dst_or_null, size_t dst_len) ""
140+
124141
%typemap(argout) (char *dst_or_null, size_t dst_len) {
125142
lua_pop(L, 1); // Blow away the previous result
126-
lua_pushlstring(L, (const char *)$1, $result);
143+
llvm::StringRef ref($1);
144+
lua_pushlstring(L, (const char *)$1, ref.size());
127145
free($1);
128146
// SWIG_arg was already incremented
129147
}

lldb/bindings/python/python-typemaps.swig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,24 @@ AND call SWIG_fail at the same time, because it will result in a double free.
224224
}
225225
$1 = (char *)malloc($2);
226226
}
227+
228+
// Disable default type checking for this method to avoid SWIG dispatch issues.
229+
//
230+
// Problem: SBThread::GetStopDescription has two overloads:
231+
// 1. GetStopDescription(char* dst_or_null, size_t dst_len)
232+
// 2. GetStopDescription(lldb::SBStream& stream)
233+
//
234+
// SWIG generates a dispatch function to select the correct overload based on argument types.
235+
// see https://www.swig.org/Doc4.0/SWIGDocumentation.html#Typemaps_overloading.
236+
// However, this dispatcher doesn't consider typemaps that transform function signatures.
237+
//
238+
// In Python, our typemap converts GetStopDescription(char*, size_t) to GetStopDescription(int).
239+
// The dispatcher still checks against the original (char*, size_t) signature instead of
240+
// the transformed (int) signature, causing type matching to fail.
241+
// This only affects SBThread::GetStopDescription since the type check also matches
242+
// the argument name, which is unique to this function.
243+
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) (char *dst_or_null, size_t dst_len) ""
244+
227245
%typemap(argout) (char *dst_or_null, size_t dst_len) {
228246
Py_XDECREF($result); /* Blow away any previous result */
229247
llvm::StringRef ref($1);

lldb/include/lldb/API/SBThread.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ class LLDB_API SBThread {
8181
SBThreadCollection
8282
GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type);
8383

84+
/// Gets a human-readable description of why the thread stopped.
85+
///
86+
/// \param stream Output stream to receive the stop description text
87+
/// \return
88+
/// true if obtained and written to the stream,
89+
// false if there was an error retrieving the description.
90+
bool GetStopDescription(lldb::SBStream &stream) const;
91+
8492
size_t GetStopDescription(char *dst_or_null, size_t dst_len);
8593

8694
SBValue GetStopReturnValue();

lldb/source/API/SBThread.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,34 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) {
239239
return threads;
240240
}
241241

242-
size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
243-
LLDB_INSTRUMENT_VA(this, dst, dst_len);
242+
bool SBThread::GetStopDescription(lldb::SBStream &stream) const {
243+
LLDB_INSTRUMENT_VA(this, stream);
244+
245+
if (!m_opaque_sp)
246+
return false;
247+
248+
llvm::Expected<StoppedExecutionContext> exe_ctx =
249+
GetStoppedExecutionContext(m_opaque_sp);
250+
if (!exe_ctx) {
251+
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
252+
return false;
253+
}
254+
255+
if (!exe_ctx->HasThreadScope())
256+
return false;
257+
258+
Stream &strm = stream.ref();
259+
const std::string stop_desc = exe_ctx->GetThreadPtr()->GetStopDescription();
260+
strm.PutCString(stop_desc);
261+
262+
return true;
263+
}
264+
265+
size_t SBThread::GetStopDescription(char *dst_or_null, size_t dst_len) {
266+
LLDB_INSTRUMENT_VA(this, dst_or_null, dst_len);
244267

245-
if (dst)
246-
*dst = 0;
268+
if (dst_or_null)
269+
*dst_or_null = 0;
247270

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

262-
if (dst)
263-
return ::snprintf(dst, dst_len, "%s", thread_stop_desc.c_str()) + 1;
285+
if (dst_or_null)
286+
return ::snprintf(dst_or_null, dst_len, "%s", thread_stop_desc.c_str()) + 1;
264287

265288
// NULL dst passed in, return the length needed to contain the
266289
// description.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
_T = require('lua_lldb_test').create_test('TestThreadAPI')
2+
3+
function _T:TestGetStopDescription()
4+
local target = self:create_target()
5+
local breakpoint = target:BreakpointCreateByName("main", "a.out")
6+
assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)
7+
8+
local process = target:LaunchSimple({ 'arg1', 'arg2' }, nil, nil)
9+
local thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
10+
assertNotNil(thread)
11+
assertTrue(thread:IsValid())
12+
13+
assertEqual("breakpoint", thread:GetStopDescription(string.len("breakpoint") + 1))
14+
assertEqual("break", thread:GetStopDescription(string.len("break") + 1))
15+
assertEqual("b", thread:GetStopDescription(string.len("b") + 1))
16+
assertEqual("breakpoint 1.1", thread:GetStopDescription(string.len("breakpoint 1.1") + 100))
17+
18+
-- Test stream variation
19+
local stream = lldb.SBStream()
20+
assertTrue(thread:GetStopDescription(stream))
21+
assertNotNil(stream)
22+
assertEqual("breakpoint 1.1", stream:GetData())
23+
end
24+
25+
os.exit(_T:run())

lldb/test/API/python_api/default-constructor/sb_thread.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def fuzz_obj(obj):
1010
obj.GetStopReasonDataCount()
1111
obj.GetStopReasonDataAtIndex(100)
1212
obj.GetStopDescription(256)
13+
obj.GetStopDescription(lldb.SBStream())
1314
obj.GetThreadID()
1415
obj.GetIndexID()
1516
obj.GetName()

lldb/test/API/python_api/thread/TestThreadAPI.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ def get_stop_description(self):
138138
"breakpoint 1.1", thread.GetStopDescription(len("breakpoint 1.1") + 100)
139139
)
140140

141+
# Test the stream variation
142+
stream = lldb.SBStream()
143+
self.assertTrue(thread.GetStopDescription(stream))
144+
self.assertEqual("breakpoint 1.1", stream.GetData())
145+
141146
def step_out_of_malloc_into_function_b(self, exe_name):
142147
"""Test Python SBThread.StepOut() API to step out of a malloc call where the call site is at function b()."""
143148
exe = self.getBuildArtifact(exe_name)

0 commit comments

Comments
 (0)