[Lldb-commits] [lldb] [lldb] Trim and show embedded zeros in `charN_t` arrays (PR #195514)
via lldb-commits
lldb-commits at lists.llvm.org
Sun May 3 03:24:22 PDT 2026
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/195514
>From 581ad0090434ac9a534f656c6ad56fdd59f28a01 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sun, 3 May 2026 11:55:47 +0200
Subject: [PATCH] [lldb] Trim and show embedded zeros in `char*_t` arrays
---
.../lldb/DataFormatters/StringPrinter.h | 7 +++++++
lldb/source/DataFormatters/StringPrinter.cpp | 12 +++++++++++
.../Language/CPlusPlus/CxxStringTypes.cpp | 20 +++++++++++++++++++
.../API/lang/cpp/char1632_t/TestChar1632T.py | 6 ++++++
lldb/test/API/lang/cpp/char1632_t/main.cpp | 8 +++++++-
.../API/lang/cpp/char8_t/TestCxxChar8_t.py | 12 +++++++++++
lldb/test/API/lang/cpp/char8_t/main.cpp | 3 +++
.../API/lang/cpp/wchar_t/TestCxxWCharT.py | 3 +++
lldb/test/API/lang/cpp/wchar_t/main.cpp | 8 ++++++--
9 files changed, 76 insertions(+), 3 deletions(-)
diff --git a/lldb/include/lldb/DataFormatters/StringPrinter.h b/lldb/include/lldb/DataFormatters/StringPrinter.h
index 4169f53e63f38..0f013564c5999 100644
--- a/lldb/include/lldb/DataFormatters/StringPrinter.h
+++ b/lldb/include/lldb/DataFormatters/StringPrinter.h
@@ -62,6 +62,10 @@ class StringPrinter {
bool GetBinaryZeroIsTerminator() const { return m_zero_is_terminator; }
+ void SetTrimTrailingZeros(bool v) { m_trim_trailing_zeros = v; }
+
+ bool GetTrimTrailingZeros() const { return m_trim_trailing_zeros; }
+
void SetEscapeNonPrintables(bool e) { m_escape_non_printables = e; }
bool GetEscapeNonPrintables() const { return m_escape_non_printables; }
@@ -95,6 +99,9 @@ class StringPrinter {
/// True iff a zero bytes ('\0') should terminate the memory region that
/// is being dumped.
bool m_zero_is_terminator = true;
+ /// True iff trailing zero bytes should be stripped from the string.
+ /// Mutually exclusive with `m_zero_is_terminator`.
+ bool m_trim_trailing_zeros = false;
/// The language-specific style for escaping special characters.
EscapeStyle m_escape_style = EscapeStyle::CXX;
};
diff --git a/lldb/source/DataFormatters/StringPrinter.cpp b/lldb/source/DataFormatters/StringPrinter.cpp
index 60cb0fc5d6876..a61042b34d29b 100644
--- a/lldb/source/DataFormatters/StringPrinter.cpp
+++ b/lldb/source/DataFormatters/StringPrinter.cpp
@@ -280,6 +280,11 @@ static bool DumpEncodedBufferToStream(
const SourceDataType *data_end_ptr = data_ptr + source_size;
const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator();
+ const bool trim_trailing_zeros = dump_options.GetTrimTrailingZeros();
+
+ assert((!zero_is_terminator || !trim_trailing_zeros) &&
+ "BinaryZeroIsTerminator and "
+ "TrimTrailingZeros are mutually exclusive");
if (zero_is_terminator) {
while (data_ptr < data_end_ptr) {
@@ -291,6 +296,12 @@ static bool DumpEncodedBufferToStream(
}
data_ptr = (const SourceDataType *)data.GetDataStart();
+ } else if (trim_trailing_zeros) {
+ while (data_end_ptr != data_ptr) {
+ if (*(data_end_ptr - 1))
+ break;
+ data_end_ptr--;
+ }
}
lldb::WritableDataBufferSP utf8_data_buffer_sp;
@@ -385,6 +396,7 @@ lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::
SetQuote(options.GetQuote());
SetEscapeNonPrintables(options.GetEscapeNonPrintables());
SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator());
+ SetTrimTrailingZeros(options.GetTrimTrailingZeros());
SetEscapeStyle(options.GetEscapeStyle());
}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
index bf8c393445908..f78613861424b 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
@@ -60,6 +60,16 @@ static bool CharStringSummaryProvider(ValueObject &valobj, Stream &stream) {
options.SetStream(&stream);
options.SetPrefixToken(getElementTraits(ElemType).first);
+ CompilerType ty = valobj.GetCompilerType();
+ uint64_t size = 0;
+ if (ty.IsArrayType(nullptr, &size) && size > 0) {
+ options.SetSourceSize(size);
+ options.SetHasSourceSize(true);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetNeedsZeroTermination(false);
+ options.SetTrimTrailingZeros(true);
+ }
+
if (!StringPrinter::ReadStringAndDumpToStream<ElemType>(options))
stream.Printf("Summary Unavailable");
@@ -127,6 +137,16 @@ bool lldb_private::formatters::WCharStringSummaryProvider(
options.SetStream(&stream);
options.SetPrefixToken("L");
+ CompilerType ty = valobj.GetCompilerType();
+ uint64_t arr_size = 0;
+ if (ty.IsArrayType(nullptr, &arr_size) && arr_size > 0) {
+ options.SetSourceSize(arr_size);
+ options.SetHasSourceSize(true);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetNeedsZeroTermination(false);
+ options.SetTrimTrailingZeros(true);
+ }
+
switch (wchar_size) {
case 1:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>(
diff --git a/lldb/test/API/lang/cpp/char1632_t/TestChar1632T.py b/lldb/test/API/lang/cpp/char1632_t/TestChar1632T.py
index e2521d88258bc..cd4dc9a406f5c 100644
--- a/lldb/test/API/lang/cpp/char1632_t/TestChar1632T.py
+++ b/lldb/test/API/lang/cpp/char1632_t/TestChar1632T.py
@@ -80,6 +80,12 @@ def test(self):
substrs=['u"ﺸﺵۻ"', 'U"ЕЙРГЖО"'],
)
+ # Check that embedded zeros show up in arrays
+ self.expect_var_path("aZero16", summary='u"I\\0have\\0zeros"')
+ self.expect_var_path("cZero16", summary='u"I"')
+ self.expect_var_path("aZero32", summary='U"I\\0have\\0zeros"')
+ self.expect_var_path("cZero32", summary='U"I"')
+
self.runCmd("next") # step to after the string is nullified
# check that we don't crash on NULL
diff --git a/lldb/test/API/lang/cpp/char1632_t/main.cpp b/lldb/test/API/lang/cpp/char1632_t/main.cpp
index dc37df27fceb5..e38188cf85dee 100644
--- a/lldb/test/API/lang/cpp/char1632_t/main.cpp
+++ b/lldb/test/API/lang/cpp/char1632_t/main.cpp
@@ -10,7 +10,7 @@ void copy_char_seq (T (&arr)[N], const T* src)
assert(src_len < N);
std::char_traits<T>::copy(arr, src, src_len);
- arr[src_len] = 0;
+ memset(&arr[src_len], 0, (N - src_len) * sizeof(T));
}
int main (int argc, char const *argv[])
@@ -25,6 +25,12 @@ int main (int argc, char const *argv[])
char32_t *s32 = (char32_t *)U"ЕЙРГЖО";
copy_char_seq(as16, s16);
copy_char_seq(as32, s32);
+
+ char16_t aZero16[32] = u"I\0have\0zeros";
+ const char16_t *cZero16 = u"I\0have\0zeros";
+ char32_t aZero32[32] = U"I\0have\0zeros";
+ const char32_t *cZero32 = U"I\0have\0zeros";
+
s32 = nullptr; // breakpoint1
s32 = (char32_t *)U"෴";
s16 = (char16_t *)u"色ハ匂ヘト散リヌルヲ";
diff --git a/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py b/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py
index 08f09b317b217..c727ae9cf6ab6 100644
--- a/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py
+++ b/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py
@@ -21,10 +21,18 @@ def test_without_process(self):
self.expect("target variable a", substrs=["char8_t", "0x61 u8'a'"])
self.expect("target variable ab", substrs=["const char8_t *", 'u8"你好"'])
self.expect("target variable abc", substrs=["char8_t[9]", 'u8"你好"'])
+ self.expect(
+ "target variable aZero", substrs=["char8_t[32]", 'u8"I\\0have\\0zeros"']
+ )
+ self.expect("target variable cZero", substrs=["const char8_t *", 'u8"I"'])
self.expect_expr("a", result_type="char8_t", result_summary="0x61 u8'a'")
self.expect_expr("ab", result_type="const char8_t *", result_summary='u8"你好"')
self.expect_expr("abc", result_type="char8_t[9]", result_summary='u8"你好"')
+ self.expect_expr(
+ "aZero", result_type="char8_t[32]", result_summary='u8"I\\0have\\0zeros"'
+ )
+ self.expect_expr("cZero", result_type="const char8_t *", result_summary='u8"I"')
@skipIf(compiler="clang", compiler_version=["<", "7.0"])
def test_with_process(self):
@@ -38,3 +46,7 @@ def test_with_process(self):
self.expect_expr("a", result_type="char8_t", result_summary="0x61 u8'a'")
self.expect_expr("ab", result_type="const char8_t *", result_summary='u8"你好"')
self.expect_expr("abc", result_type="char8_t[9]", result_summary='u8"你好"')
+ self.expect_expr(
+ "aZero", result_type="char8_t[32]", result_summary='u8"I\\0have\\0zeros"'
+ )
+ self.expect_expr("cZero", result_type="const char8_t *", result_summary='u8"I"')
diff --git a/lldb/test/API/lang/cpp/char8_t/main.cpp b/lldb/test/API/lang/cpp/char8_t/main.cpp
index d109751cc2314..36a2bbef95e2b 100644
--- a/lldb/test/API/lang/cpp/char8_t/main.cpp
+++ b/lldb/test/API/lang/cpp/char8_t/main.cpp
@@ -2,6 +2,9 @@ char8_t a = u8'a';
const char8_t* ab = u8"你好";
char8_t abc[9] = u8"你好";
+char8_t aZero[32] = u8"I\0have\0zeros";
+const char8_t *cZero = u8"I\0have\0zeros";
+
int main (int argc, char const *argv[]) {
return 0; // break here
}
diff --git a/lldb/test/API/lang/cpp/wchar_t/TestCxxWCharT.py b/lldb/test/API/lang/cpp/wchar_t/TestCxxWCharT.py
index 35cb512142ca0..f17d5a707c9e3 100644
--- a/lldb/test/API/lang/cpp/wchar_t/TestCxxWCharT.py
+++ b/lldb/test/API/lang/cpp/wchar_t/TestCxxWCharT.py
@@ -48,3 +48,6 @@ def test(self):
self.expect("frame variable wchar_zero", substrs=["L'\\0'"])
self.expect("expression wchar_zero", substrs=["L'\\0'"])
+
+ self.expect_var_path("aZero", summary='L"I\\0have\\0zeros"')
+ self.expect_var_path("cZero", summary='L"I"')
diff --git a/lldb/test/API/lang/cpp/wchar_t/main.cpp b/lldb/test/API/lang/cpp/wchar_t/main.cpp
index 778642e0133f0..d479e2daf0697 100644
--- a/lldb/test/API/lang/cpp/wchar_t/main.cpp
+++ b/lldb/test/API/lang/cpp/wchar_t/main.cpp
@@ -19,8 +19,12 @@ int main (int argc, char const *argv[])
const wchar_t *mazeltov = L"מזל טוב";
wchar_t *ws_NULL = nullptr;
wchar_t *ws_empty = L"";
- wchar_t array[200], * array_source = L"Hey, I'm a super wchar_t string, éõñž";
+ wchar_t array[200], * array_source = L"Hey, I'm a super wchar_t string, éõñž";
wchar_t wchar_zero = (wchar_t)0;
- memcpy(array, array_source, 39 * sizeof(wchar_t));
+ memcpy(array, array_source, 39 * sizeof(wchar_t));
+
+ wchar_t aZero[32] = L"I\0have\0zeros";
+ const wchar_t *cZero = L"I\0have\0zeros";
+
return 0; // break here
}
More information about the lldb-commits
mailing list