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; } _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
