[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