[llvm-branch-commits] [lldb] 81e0360 - [lldb] Fix SBStructuredData::GetStringValue method (#216875)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 26 02:01:13 PDT 2026
Author: Ebuka Ezike
Date: 2026-08-26T11:01:00+02:00
New Revision: 81e03602463bcfe78fbb0aeda7b7974d5e08b747
URL: https://github.com/llvm/llvm-project/commit/81e03602463bcfe78fbb0aeda7b7974d5e08b747
DIFF: https://github.com/llvm/llvm-project/commit/81e03602463bcfe78fbb0aeda7b7974d5e08b747.diff
LOG: [lldb] Fix SBStructuredData::GetStringValue method (#216875)
In python the method's signature is
```py
class SBStructuredData:
def GetStringValue(self, dst_len: int) -> str:
```
There is no way from python to get the total data from GetStringValue
regardless of how many times you call GetStringValue.
Update the implementation
```py
def GetStringValue(self, len: int = 0) -> str:
```
Fixes crash when input is None in `SBStructuredData.SetStringValue`
Don't trim the result when called with the `dynamic` property.
(cherry picked from commit 93377d703115b45c3ad8f5f218cb85769d24a1b0)
Added:
Modified:
lldb/bindings/interface/SBStructuredDataExtensions.i
lldb/bindings/interfaces.swig
lldb/bindings/macros.swig
lldb/bindings/python/python-typemaps.swig
lldb/include/lldb/Core/StructuredDataImpl.h
lldb/include/lldb/Utility/StructuredData.h
lldb/source/API/SBStructuredData.cpp
lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
lldb/tools/lldb-dap/LLDBUtils.cpp
Removed:
################################################################################
diff --git a/lldb/bindings/interface/SBStructuredDataExtensions.i b/lldb/bindings/interface/SBStructuredDataExtensions.i
index af76cfc5c2db3..23a282d3cd325 100644
--- a/lldb/bindings/interface/SBStructuredDataExtensions.i
+++ b/lldb/bindings/interface/SBStructuredDataExtensions.i
@@ -85,8 +85,7 @@ STRING_EXTENSION_OUTSIDE(SBStructuredData)
elif data_type == eStructuredDataTypeFloat:
return self.GetFloatValue()
elif data_type == eStructuredDataTypeString:
- size = len(self) or 1023
- return self.GetStringValue(size + 1)
+ return self.GetStringValue()
elif data_type == eStructuredDataTypeGeneric:
return self.GetGenericValue()
else:
diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig
index 5cdddd5136ac2..a5d0d7a33da20 100644
--- a/lldb/bindings/interfaces.swig
+++ b/lldb/bindings/interfaces.swig
@@ -91,6 +91,9 @@
%include "./interface/SBWatchpointDocstrings.i"
%include "./interface/SBWatchpointOptionsDocstrings.i"
+/* Method replacements, must be called before the including API headers. */
+REPLACE_BUF_GETTER_WITH_STRING(SBStructuredData, GetStringValue)
+
/* API headers */
%include "lldb/API/SBAddress.h"
%include "lldb/API/SBAddressRange.h"
diff --git a/lldb/bindings/macros.swig b/lldb/bindings/macros.swig
index cb013daa158d9..cd3ea21c3efad 100644
--- a/lldb/bindings/macros.swig
+++ b/lldb/bindings/macros.swig
@@ -27,3 +27,27 @@
}
}
%enddef
+
+
+// Replace the C++ class method (char *dst, size_t dst_len) with
+// std::string (size_t len = 0). So callers do not have to guess the required length.
+// These must be invoked before the class headers are included so that
+// %ignore takes effect.
+%define REPLACE_BUF_GETTER_WITH_STRING(Class, Method)
+%ignore lldb:: ## Class ## :: ## Method ## (char *, size_t) const;
+// Emit a single wrapper for the default-argument overload instead of two.
+%feature("compactdefaultargs") lldb:: ## Class ## :: ## Method;
+%extend lldb:: ## Class ## {
+ std::string Method(size_t len = 0) const {
+ (void)len;
+ const auto required_len = $self->Method(nullptr, 0);
+ if (required_len <= 0)
+ return std::string{};
+
+ std::string buf(required_len - 1, '\0');
+ $self->Method(buf.data(), required_len);
+
+ return buf;
+ }
+}
+%enddef
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 072e688c4bde1..93f780786a19e 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -171,35 +171,52 @@ AND call SWIG_fail at the same time, because it will result in a double free.
// typemap for a char buffer
%typemap(in) (char *dst, size_t dst_len) {
- if (!PyLong_Check($input)) {
- PyErr_SetString(PyExc_ValueError, "Expecting an integer");
+ $1 = NULL;
+ $2 = PyLong_AsSize_t($input);
+ if (PyErr_Occurred()) {
SWIG_fail;
}
- $2 = PyLong_AsLong($input);
- if ($2 <= 0) {
+
+ if ($2 == 0) {
PyErr_SetString(PyExc_ValueError, "Positive integer expected");
SWIG_fail;
}
- $1 = (char *)malloc($2);
+
+ $1 = (char *)::malloc($2);
+ if (!$1) {
+ PyErr_NoMemory();
+ SWIG_fail;
+ }
}
// SBProcess::ReadCStringFromMemory() uses a void*, but needs to be treated
// as char data instead of byte data.
%typemap(in) (void *char_buf, size_t size) = (char *dst, size_t dst_len);
+%typemap(typecheck) (char *dst, size_t dst_len) {
+ $1 = PyLong_Check($input);
+}
+
+%typemap(freearg) (char *dst, size_t dst_len) {
+ /* Free the allocated memory in typemap(in) on failure.*/
+ ::free($1);
+}
+
// Return the char buffer. Discarding any previous return result
%typemap(argout) (char *dst, size_t dst_len) {
Py_XDECREF($result); /* Blow away any previous result */
- if (result == 0) {
- PythonString string("");
- $result = string.release();
- Py_INCREF($result);
- } else {
- llvm::StringRef ref(static_cast<const char *>($1), result);
- PythonString string(ref);
- $result = string.release();
+
+ size_t str_len = result;
+ if (str_len >= $2) {
+ /* We allocated $2 (dst_len) but the result is larger,
+ trim to match the expected length so we don't read random data.*/
+ str_len = $2 - 1;
}
- free($1);
+
+ llvm::StringRef ref(static_cast<const char *>($1), str_len);
+ PythonString string(ref);
+ $result = string.release();
}
+
// SBProcess::ReadCStringFromMemory() uses a void*, but needs to be treated
// as char data instead of byte data.
%typemap(argout) (void *char_buf, size_t size) = (char *dst, size_t dst_len);
diff --git a/lldb/include/lldb/Core/StructuredDataImpl.h b/lldb/include/lldb/Core/StructuredDataImpl.h
index b88962bc774dc..8ff522601858f 100644
--- a/lldb/include/lldb/Core/StructuredDataImpl.h
+++ b/lldb/include/lldb/Core/StructuredDataImpl.h
@@ -108,8 +108,8 @@ class StructuredDataImpl {
m_data_sp = StructuredData::FromBoolean(value);
}
- void SetStringValue(std::string value) {
- m_data_sp = StructuredData::FromString(std::move(value));
+ void SetStringValue(llvm::StringRef value) {
+ m_data_sp = StructuredData::FromString(value);
}
void SetGenericValue(void *value) {
@@ -179,11 +179,15 @@ class StructuredDataImpl {
if (result.empty())
return 0;
- if (!dst || !dst_len) {
- char s[1];
- return (::snprintf(s, 1, "%s", result.data()));
+ const size_t needed_len = result.size() + 1; // for the NULL byte.
+ if (dst && dst_len != 0) {
+ const size_t min_len = std::min(needed_len, dst_len);
+ const size_t copy_len = min_len - 1; // exclude space for NULL byte.
+ ::memcpy(dst, result.data(), copy_len);
+ dst[copy_len] = '\0';
}
- return (::snprintf(dst, dst_len, "%s", result.data()));
+
+ return needed_len;
}
void *GetGenericValue() const {
diff --git a/lldb/include/lldb/Utility/StructuredData.h b/lldb/include/lldb/Utility/StructuredData.h
index 59a2fd60b3989..b90a8d4484d0e 100644
--- a/lldb/include/lldb/Utility/StructuredData.h
+++ b/lldb/include/lldb/Utility/StructuredData.h
@@ -584,7 +584,7 @@ class StructuredData {
static StructuredData::ObjectSP FromBoolean(bool value) {
return std::make_shared<StructuredData::Boolean>(value);
}
- static StructuredData::ObjectSP FromString(std::string value) {
+ static StructuredData::ObjectSP FromString(llvm::StringRef value) {
return std::make_shared<StructuredData::String>(value);
}
static StructuredData::ObjectSP FromGeneric(void *value) {
diff --git a/lldb/source/API/SBStructuredData.cpp b/lldb/source/API/SBStructuredData.cpp
index 971e079723ba4..25e229e972873 100644
--- a/lldb/source/API/SBStructuredData.cpp
+++ b/lldb/source/API/SBStructuredData.cpp
@@ -272,7 +272,7 @@ void SBStructuredData::SetBooleanValue(bool value) {
void SBStructuredData::SetStringValue(const char *value) {
LLDB_INSTRUMENT_VA(this, value);
- m_impl_up->SetStringValue(value);
+ m_impl_up->SetStringValue(llvm::StringRef(value));
}
void SBStructuredData::SetGenericValue(SBScriptObject value) {
diff --git a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
index b12f4daee0c81..9c5ee87b60085 100644
--- a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
+++ b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
@@ -147,9 +147,27 @@ class MyRandomClass:
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeFloat)
self.assertEqual(example.GetFloatValue(), 4.19)
- example.SetStringValue("Bonjour, 123!")
+ bonjour_str = "Bonjour, 123!"
+ example.SetStringValue(bonjour_str)
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
- self.assertEqual(example.GetStringValue(42), "Bonjour, 123!")
+ self.assertEqual(example.GetStringValue(42), bonjour_str)
+ # Verify buffer does not affect the size of returned string.
+ self.assertEqual(example.GetStringValue(8), bonjour_str)
+
+ # Verify StructuredData's string as None doesn't crash.
+ example.SetStringValue(None)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
+ self.assertEqual(example.GetStringValue(20), "")
+ self.assertEqual(example.GetStringValue(), "")
+
+ # Verify writing a large buffer doesn't get
+ # trimmed when using the dynamic property.
+ large_str = "0xdeadbeef430e~~" * 4096
+ example.SetStringValue(large_str)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
+ self.assertEqual(example.dynamic, large_str)
+ self.assertEqual(example.GetStringValue(), large_str)
+ self.assertEqual(example.GetStringValue(len(large_str) + 1), large_str)
value = lldb.SBStructuredData()
example.SetValueForKey("Hello", value)
@@ -228,6 +246,13 @@ def string_struct_test(self, dict_struct):
if not "STRING" in output:
self.fail("wrong output: " + output)
+ self.assertEqual(string_struct.dynamic, "STRING")
+ self.assertEqual(str(string_struct), "STRING")
+
+ # Negative value still fails.
+ with self.assertRaises(Exception):
+ string_struct.GetStringValue(-1)
+
# Calling wrong API on a SBStructuredData
# (e.g. getting an integer from a string type structure)
output = string_struct.GetIntegerValue()
diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp
index 3c968aaa6447b..434565ae06797 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.cpp
+++ b/lldb/tools/lldb-dap/LLDBUtils.cpp
@@ -207,8 +207,8 @@ std::string GetStringValue(const lldb::SBStructuredData &data) {
if (!str_length)
return "";
- std::string str(str_length, 0);
- data.GetStringValue(str.data(), str_length + 1);
+ std::string str(str_length - 1, 0);
+ data.GetStringValue(str.data(), str_length);
return str;
}
More information about the llvm-branch-commits
mailing list