[LLDB] Consolidate C++ string buffer summaries (#144258)

As part of https://github.com/llvm/llvm-project/pull/143177, I moved the
non-libc++ specific formatting of `std::string`s out to `CxxStringTypes`
as MSVC's STL `std::string` can also be thought of a pointer+size pair.
I named this kind of string "string buffer".

This PR picks that change, so the MSVC PR can be smaller.
Unfortunately, libstdc++'s `std::string` does not fit this (it also uses
a different string printer function).

This resolves two FIXMEs in the libc++ tests, where empty u16 and u32
strings didn't have any prefix (u/U).
This commit is contained in:
nerix
2025-06-17 18:44:37 +02:00
committed by GitHub
parent 0c608175c1
commit b14e03d855
5 changed files with 148 additions and 146 deletions

View File

@@ -116,15 +116,7 @@ bool lldb_private::formatters::WCharStringSummaryProvider(
return false;
// Get a wchar_t basic type from the current type system
CompilerType wchar_compiler_type =
valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
if (!wchar_compiler_type)
return false;
// Safe to pass nullptr for exe_scope here.
std::optional<uint64_t> size =
llvm::expectedToOptional(wchar_compiler_type.GetBitSize(nullptr));
std::optional<uint64_t> size = GetWCharByteSize(valobj);
if (!size)
return false;
const uint32_t wchar_size = *size;
@@ -136,13 +128,13 @@ bool lldb_private::formatters::WCharStringSummaryProvider(
options.SetPrefixToken("L");
switch (wchar_size) {
case 8:
case 1:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>(
options);
case 16:
case 2:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>(
options);
case 32:
case 4:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>(
options);
default:
@@ -177,15 +169,7 @@ bool lldb_private::formatters::WCharSummaryProvider(
return false;
// Get a wchar_t basic type from the current type system
CompilerType wchar_compiler_type =
valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
if (!wchar_compiler_type)
return false;
// Safe to pass nullptr for exe_scope here.
std::optional<uint64_t> size =
llvm::expectedToOptional(wchar_compiler_type.GetBitSize(nullptr));
std::optional<uint64_t> size = GetWCharByteSize(valobj);
if (!size)
return false;
const uint32_t wchar_size = *size;
@@ -199,13 +183,13 @@ bool lldb_private::formatters::WCharSummaryProvider(
options.SetBinaryZeroIsTerminator(false);
switch (wchar_size) {
case 8:
case 1:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>(
options);
case 16:
case 2:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>(
options);
case 32:
case 4:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>(
options);
default:
@@ -214,3 +198,73 @@ bool lldb_private::formatters::WCharSummaryProvider(
}
return true;
}
std::optional<uint64_t>
lldb_private::formatters::GetWCharByteSize(ValueObject &valobj) {
return llvm::expectedToOptional(
valobj.GetCompilerType()
.GetBasicTypeFromAST(lldb::eBasicTypeWChar)
.GetByteSize(nullptr));
}
template <StringPrinter::StringElementType element_type>
bool lldb_private::formatters::StringBufferSummaryProvider(
Stream &stream, const TypeSummaryOptions &summary_options,
lldb::ValueObjectSP location_sp, uint64_t size, std::string prefix_token) {
if (size == 0) {
stream.PutCString(prefix_token);
stream.PutCString("\"\"");
return true;
}
if (!location_sp)
return false;
StringPrinter::ReadBufferAndDumpToStreamOptions options(*location_sp);
if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
const auto max_size =
location_sp->GetTargetSP()->GetMaximumSizeOfStringSummary();
if (size > max_size) {
size = max_size;
options.SetIsTruncated(true);
}
}
{
DataExtractor extractor;
const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
if (bytes_read < size)
return false;
options.SetData(std::move(extractor));
}
options.SetStream(&stream);
if (prefix_token.empty())
options.SetPrefixToken(nullptr);
else
options.SetPrefixToken(prefix_token);
options.SetQuote('"');
options.SetSourceSize(size);
options.SetBinaryZeroIsTerminator(false);
return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
}
// explicit instantiations for all string element types
template bool
lldb_private::formatters::StringBufferSummaryProvider<StringElementType::ASCII>(
Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
std::string);
template bool
lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF8>(
Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
std::string);
template bool
lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF16>(
Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
std::string);
template bool
lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF32>(
Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
std::string);

View File

@@ -10,6 +10,7 @@
#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H
#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H
#include "lldb/DataFormatters/StringPrinter.h"
#include "lldb/DataFormatters/TypeSummary.h"
#include "lldb/Utility/Stream.h"
#include "lldb/ValueObject/ValueObject.h"
@@ -43,6 +44,34 @@ bool Char32SummaryProvider(ValueObject &valobj, Stream &stream,
bool WCharSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options); // wchar_t
std::optional<uint64_t> GetWCharByteSize(ValueObject &valobj);
/// Print a summary for a string buffer to \a stream.
///
/// \param[in] stream
/// The output stream to print the summary to.
///
/// \param[in] summary_options
/// Options for printing the string contents. This function respects the
/// capping.
///
/// \param[in] location_sp
/// ValueObject of a pointer to the string being printed.
///
/// \param[in] size
/// The size of the buffer pointed to by \a location_sp.
///
/// \param[in] prefix_token
/// A prefix before the double quotes (e.g. 'u' results in u"...").
///
/// \return
/// Returns whether the string buffer was successfully printed.
template <StringPrinter::StringElementType element_type>
bool StringBufferSummaryProvider(Stream &stream,
const TypeSummaryOptions &summary_options,
lldb::ValueObjectSP location_sp, uint64_t size,
std::string prefix_token);
} // namespace formatters
} // namespace lldb_private

View File

@@ -24,6 +24,7 @@
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "Plugins/Language/CPlusPlus/CxxStringTypes.h"
#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/lldb-enumerations.h"
@@ -535,70 +536,6 @@ ExtractLibcxxStringInfo(ValueObject &valobj) {
return std::make_pair(size, location_sp);
}
static bool
LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options,
ValueObjectSP location_sp, size_t size) {
if (size == 0) {
stream.Printf("L\"\"");
return true;
}
if (!location_sp)
return false;
StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
if (size > max_size) {
size = max_size;
options.SetIsTruncated(true);
}
}
DataExtractor extractor;
const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
if (bytes_read < size)
return false;
// std::wstring::size() is measured in 'characters', not bytes
TypeSystemClangSP scratch_ts_sp =
ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP());
if (!scratch_ts_sp)
return false;
auto wchar_t_size =
scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr);
if (!wchar_t_size)
return false;
options.SetData(std::move(extractor));
options.SetStream(&stream);
options.SetPrefixToken("L");
options.SetQuote('"');
options.SetSourceSize(size);
options.SetBinaryZeroIsTerminator(false);
switch (*wchar_t_size) {
case 1:
return StringPrinter::ReadBufferAndDumpToStream<
lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
options);
break;
case 2:
return StringPrinter::ReadBufferAndDumpToStream<
lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
options);
break;
case 4:
return StringPrinter::ReadBufferAndDumpToStream<
lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
options);
}
return false;
}
bool lldb_private::formatters::LibcxxWStringSummaryProvider(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
@@ -609,52 +546,22 @@ bool lldb_private::formatters::LibcxxWStringSummaryProvider(
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
location_sp, size);
}
template <StringPrinter::StringElementType element_type>
static bool
LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options,
std::string prefix_token, ValueObjectSP location_sp,
uint64_t size) {
if (size == 0) {
stream.Printf("\"\"");
return true;
}
if (!location_sp)
auto wchar_t_size = GetWCharByteSize(valobj);
if (!wchar_t_size)
return false;
StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
if (size > max_size) {
size = max_size;
options.SetIsTruncated(true);
}
switch (*wchar_t_size) {
case 1:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
stream, summary_options, location_sp, size, "L");
case 2:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
stream, summary_options, location_sp, size, "L");
case 4:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
stream, summary_options, location_sp, size, "L");
}
{
DataExtractor extractor;
const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
if (bytes_read < size)
return false;
options.SetData(std::move(extractor));
}
options.SetStream(&stream);
if (prefix_token.empty())
options.SetPrefixToken(nullptr);
else
options.SetPrefixToken(prefix_token);
options.SetQuote('"');
options.SetSourceSize(size);
options.SetBinaryZeroIsTerminator(false);
return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
return false;
}
template <StringPrinter::StringElementType element_type>
@@ -669,8 +576,8 @@ LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
return LibcxxStringSummaryProvider<element_type>(
valobj, stream, summary_options, prefix_token, location_sp, size);
return StringBufferSummaryProvider<element_type>(
stream, summary_options, location_sp, size, prefix_token);
}
template <StringPrinter::StringElementType element_type>
static bool formatStringImpl(ValueObject &valobj, Stream &stream,
@@ -742,8 +649,8 @@ static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
return true;
}
return LibcxxStringSummaryProvider<element_type>(
valobj, stream, summary_options, prefix_token, dataobj, size);
return StringBufferSummaryProvider<element_type>(stream, summary_options,
dataobj, size, prefix_token);
}
bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
@@ -781,8 +688,22 @@ bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
return true;
}
return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
dataobj, size);
auto wchar_t_size = GetWCharByteSize(valobj);
if (!wchar_t_size)
return false;
switch (*wchar_t_size) {
case 1:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
stream, summary_options, dataobj, size, "L");
case 2:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
stream, summary_options, dataobj, size, "L");
case 4:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
stream, summary_options, dataobj, size, "L");
}
return false;
}
static bool

View File

@@ -65,11 +65,9 @@ class LibcxxStringDataFormatterTestCase(TestBase):
'(%s::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"'
% ns,
'(%s::u16string) u16_string = u"ß水氶"' % ns,
# FIXME: This should have a 'u' prefix.
'(%s::u16string) u16_empty = ""' % ns,
'(%s::u16string) u16_empty = u""' % ns,
'(%s::u32string) u32_string = U"🍄🍅🍆🍌"' % ns,
# FIXME: This should have a 'U' prefix.
'(%s::u32string) u32_empty = ""' % ns,
'(%s::u32string) u32_empty = U""' % ns,
"(%s::string *) null_str = nullptr" % ns,
],
)
@@ -123,7 +121,7 @@ class LibcxxStringDataFormatterTestCase(TestBase):
% ns,
'(%s::u16string) u16_string = u"ß水氶"' % ns,
'(%s::u32string) u32_string = U"🍄🍅🍆🍌"' % ns,
'(%s::u32string) u32_empty = ""' % ns,
'(%s::u32string) u32_empty = U""' % ns,
"(%s::string *) null_str = nullptr" % ns,
],
)

View File

@@ -81,11 +81,11 @@ class LibcxxStringViewDataFormatterTestCase(TestBase):
summary='L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"',
)
self.expect_var_path("u16_string", type="std::u16string_view", summary='u"ß水氶"')
self.expect_var_path("u16_empty", type="std::u16string_view", summary='""')
self.expect_var_path("u16_empty", type="std::u16string_view", summary='u""')
self.expect_var_path(
"u32_string", type="std::u32string_view", summary='U"🍄🍅🍆🍌"'
)
self.expect_var_path("u32_empty", type="std::u32string_view", summary='""')
self.expect_var_path("u32_empty", type="std::u32string_view", summary='U""')
self.expect_var_path(
"oops", type="std::string_view", summary='"Hellooo World\\n"'
)
@@ -145,11 +145,11 @@ class LibcxxStringViewDataFormatterTestCase(TestBase):
summary='L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"',
)
self.expect_var_path("u16_string", type="std::u16string_view", summary='u"ß水氶"')
self.expect_var_path("u16_empty", type="std::u16string_view", summary='""')
self.expect_var_path("u16_empty", type="std::u16string_view", summary='u""')
self.expect_var_path(
"u32_string", type="std::u32string_view", summary='U"🍄🍅🍆🍌"'
)
self.expect_var_path("u32_empty", type="std::u32string_view", summary='""')
self.expect_var_path("u32_empty", type="std::u32string_view", summary='U""')
self.runCmd("cont")
self.expect(