https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/143177
>From 938bc84e55cff4a5e852f0ea093e95418e9b4374 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Fri, 6 Jun 2025 19:23:04 +0200 Subject: [PATCH] [LLDB] Add type summaries for MSVC STL strings --- .../lldb/DataFormatters/StringPrinter.h | 15 ++ lldb/packages/Python/lldbsuite/test/dotest.py | 21 +++ .../Python/lldbsuite/test/test_categories.py | 1 + .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 114 +++++++++---- .../Language/CPlusPlus/CxxStringTypes.cpp | 158 +++++++++++++++++- .../Language/CPlusPlus/CxxStringTypes.h | 35 ++++ .../Plugins/Language/CPlusPlus/LibCxx.cpp | 121 +------------- .../Plugins/Language/CPlusPlus/MsvcStl.cpp | 140 ++++++++++++++++ .../Plugins/Language/CPlusPlus/MsvcStl.h | 33 ++++ .../string/TestDataFormatterLibcxxString.py | 8 +- .../TestDataFormatterLibcxxStringView.py | 8 +- .../msvcstl/string/Makefile | 4 + .../string/TestDataFormatterStdString.py | 117 +++++++++++++ .../msvcstl/string/main.cpp | 38 +++++ .../msvcstl/u8string/Makefile | 4 + .../u8string/TestDataFormatterStdU8String.py | 31 ++++ .../msvcstl/u8string/main.cpp | 14 ++ 18 files changed, 706 insertions(+), 157 deletions(-) create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp diff --git a/lldb/include/lldb/DataFormatters/StringPrinter.h b/lldb/include/lldb/DataFormatters/StringPrinter.h index 4169f53e63f38..4ebe712be60e1 100644 --- a/lldb/include/lldb/DataFormatters/StringPrinter.h +++ b/lldb/include/lldb/DataFormatters/StringPrinter.h @@ -152,6 +152,21 @@ class StringPrinter { template <StringElementType element_type> static bool ReadBufferAndDumpToStream(const ReadBufferAndDumpToStreamOptions &options); + + template <StringElementType element_type> + static constexpr uint64_t ElementByteSize() { + switch (element_type) { + case StringElementType::ASCII: + case StringElementType::UTF8: + return 1; + case StringElementType::UTF16: + return 2; + case StringElementType::UTF32: + return 3; + default: + return 0; + } + } }; } // namespace formatters diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index d7f274ac4f60e..7f15be206acb8 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -831,6 +831,26 @@ def checkLibstdcxxSupport(): configuration.skip_categories.append("libstdcxx") +def canRunMsvcStlTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + if platform == "windows": + return True, "MSVC STL is present on Windows" + return False, f"Don't know how to build with MSVC's STL on {platform}" + + +def checkMsvcStlSupport(): + result, reason = canRunMsvcStlTests() + if result: + return # msvcstl supported + if "msvcstl" in configuration.categories_list: + return # msvcstl category explicitly requested, let it run. + if configuration.verbose: + print(f"msvcstl tests will not be run because: {reason}") + configuration.skip_categories.append("msvcstl") + + def canRunWatchpointTests(): from lldbsuite.test import lldbplatformutil @@ -1044,6 +1064,7 @@ def run_suite(): checkLibcxxSupport() checkLibstdcxxSupport() + checkMsvcStlSupport() checkWatchpointSupport() checkDebugInfoSupport() checkDebugServerSupport() diff --git a/lldb/packages/Python/lldbsuite/test/test_categories.py b/lldb/packages/Python/lldbsuite/test/test_categories.py index b585f695adeab..1f6e8a78e0c0d 100644 --- a/lldb/packages/Python/lldbsuite/test/test_categories.py +++ b/lldb/packages/Python/lldbsuite/test/test_categories.py @@ -33,6 +33,7 @@ "lldb-server": "Tests related to lldb-server", "lldb-dap": "Tests for the Debug Adapter Protocol with lldb-dap", "llgs": "Tests for the gdb-server functionality of lldb-server", + "msvcstl": "Test for MSVC STL data formatters", "pexpect": "Tests requiring the pexpect library to be available", "objc": "Tests related to the Objective-C programming language support", "pyapi": "Tests related to the Python API", diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 5ba2567c80cc3..bbfc31a722f27 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -32,6 +32,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibStdcpp.cpp LibStdcppTuple.cpp LibStdcppUniquePointer.cpp + MsvcStl.cpp MSVCUndecoratedNameParser.cpp LINK_COMPONENTS diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 0f18abb47591d..2f7e65827bc7c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -46,6 +46,7 @@ #include "LibCxxVariant.h" #include "LibStdcpp.h" #include "MSVCUndecoratedNameParser.h" +#include "MsvcStl.h" #include "lldb/lldb-enumerations.h" using namespace lldb; @@ -1372,6 +1373,56 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "${var.__y_} ${var.__m_} ${var.__wdl_}"))); } +template <StringPrinter::StringElementType element_type> +static bool +LibstdcppOrMsvcStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP libstdcpp = valobj.GetChildMemberWithName("_M_dataplus"); + if (libstdcpp) { + ValueObjectSP ptr = libstdcpp->GetChildMemberWithName("_M_p"); + if (!ptr) + return false; + return CharTStringSummaryProvider<element_type>(*ptr, stream); + } + return MsvcStlStringSummaryProvider<element_type>(valobj, stream, options); +} + +static bool +LibstdcppOrMsvcWStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP libstdcpp = valobj.GetChildMemberWithName("_M_dataplus"); + if (libstdcpp) { + ValueObjectSP ptr = libstdcpp->GetChildMemberWithName("_M_p"); + if (!ptr) + return false; + return WCharStringSummaryProvider(*ptr, stream, options); + } + return MsvcStlWStringSummaryProvider(valobj, stream, options); +} + +static void RegisterLibstdcppOrMsvcStringSummaryProvider( + const lldb::TypeCategoryImplSP &category_sp, llvm::StringRef string_ty, + llvm::StringRef char_ty, const lldb::TypeSummaryImplSP &summary_sp) { + category_sp->AddTypeSummary(string_ty, eFormatterMatchExact, summary_sp); + + // std::basic_string<char> + category_sp->AddTypeSummary( + (llvm::Twine("std::basic_string<") + char_ty + ">").str(), + eFormatterMatchExact, summary_sp); + // std::basic_string<char,std::char_traits<char>,std::allocator<char> > + category_sp->AddTypeSummary((llvm::Twine("std::basic_string<") + char_ty + + ",std::char_traits<" + char_ty + + ">,std::allocator<" + char_ty + "> >") + .str(), + eFormatterMatchExact, summary_sp); + // std::basic_string<char, std::char_traits<char>, std::allocator<char> > + category_sp->AddTypeSummary((llvm::Twine("std::basic_string<") + char_ty + + ", std::char_traits<" + char_ty + + ">, std::allocator<" + char_ty + "> >") + .str(), + eFormatterMatchExact, summary_sp); +} + static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) return; @@ -1385,9 +1436,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { .SetShowMembersOneLiner(false) .SetHideItemNames(false); - lldb::TypeSummaryImplSP std_string_summary_sp( - new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}")); - lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat( stl_summary_flags, LibStdcppStringSummaryProvider, "libstdc++ c++11 std::string summary provider")); @@ -1395,16 +1443,13 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_summary_flags, LibStdcppWStringSummaryProvider, "libstdc++ c++11 std::wstring summary provider")); - cpp_category_sp->AddTypeSummary("std::string", eFormatterMatchExact, - std_string_summary_sp); - cpp_category_sp->AddTypeSummary("std::basic_string<char>", - eFormatterMatchExact, std_string_summary_sp); - cpp_category_sp->AddTypeSummary( - "std::basic_string<char,std::char_traits<char>,std::allocator<char> >", - eFormatterMatchExact, std_string_summary_sp); - cpp_category_sp->AddTypeSummary( - "std::basic_string<char, std::char_traits<char>, std::allocator<char> >", - eFormatterMatchExact, std_string_summary_sp); + RegisterLibstdcppOrMsvcStringSummaryProvider( + cpp_category_sp, "std::string", "char", + lldb::TypeSummaryImplSP(new CXXFunctionSummaryFormat( + stl_summary_flags, + LibstdcppOrMsvcStringSummaryProvider< + StringPrinter::StringElementType::ASCII>, + "libstdc++/MSVC STL std::string summary provider"))); cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, cxx11_string_summary_sp); @@ -1418,22 +1463,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { eFormatterMatchExact, cxx11_string_summary_sp); - // making sure we force-pick the summary for printing wstring (_M_p is a - // wchar_t*) - lldb::TypeSummaryImplSP std_wstring_summary_sp( - new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}")); - - cpp_category_sp->AddTypeSummary("std::wstring", eFormatterMatchExact, - std_wstring_summary_sp); - cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t>", - eFormatterMatchExact, std_wstring_summary_sp); - cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t,std::char_traits<" - "wchar_t>,std::allocator<wchar_t> >", - eFormatterMatchExact, std_wstring_summary_sp); - cpp_category_sp->AddTypeSummary( - "std::basic_string<wchar_t, std::char_traits<wchar_t>, " - "std::allocator<wchar_t> >", - eFormatterMatchExact, std_wstring_summary_sp); + RegisterLibstdcppOrMsvcStringSummaryProvider( + cpp_category_sp, "std::wstring", "wchar_t", + lldb::TypeSummaryImplSP(new CXXFunctionSummaryFormat( + stl_summary_flags, LibstdcppOrMsvcWStringSummaryProvider, + "libstdc++/MSVC STL std::wstring summary provider"))); cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, cxx11_wstring_summary_sp); @@ -1442,6 +1476,28 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "std::allocator<wchar_t> >", eFormatterMatchExact, cxx11_wstring_summary_sp); + RegisterLibstdcppOrMsvcStringSummaryProvider( + cpp_category_sp, "std::u8string", "char8_t", + lldb::TypeSummaryImplSP(new CXXFunctionSummaryFormat( + stl_summary_flags, + LibstdcppOrMsvcStringSummaryProvider< + StringPrinter::StringElementType::UTF8>, + "libstdc++/MSVC STL std::u8string summary provider"))); + RegisterLibstdcppOrMsvcStringSummaryProvider( + cpp_category_sp, "std::u16string", "char16_t", + lldb::TypeSummaryImplSP(new CXXFunctionSummaryFormat( + stl_summary_flags, + LibstdcppOrMsvcStringSummaryProvider< + StringPrinter::StringElementType::UTF16>, + "libstdc++/MSVC STL std::u16string summary provider"))); + RegisterLibstdcppOrMsvcStringSummaryProvider( + cpp_category_sp, "std::u32string", "char32_t", + lldb::TypeSummaryImplSP(new CXXFunctionSummaryFormat( + stl_summary_flags, + LibstdcppOrMsvcStringSummaryProvider< + StringPrinter::StringElementType::UTF32>, + "libstdc++/MSVC STL std::u32string summary provider"))); + SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( false); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp index fc17b76804d9f..c3d5b7bc1ba26 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp @@ -37,6 +37,8 @@ using StringElementType = StringPrinter::StringElementType; static constexpr std::pair<const char *, Format> getElementTraits(StringElementType ElemType) { switch (ElemType) { + case StringElementType::ASCII: + return std::make_pair("", lldb::eFormatUnicode8); case StringElementType::UTF8: return std::make_pair("u8", lldb::eFormatUnicode8); case StringElementType::UTF16: @@ -49,7 +51,8 @@ getElementTraits(StringElementType ElemType) { } template <StringElementType ElemType> -static bool CharStringSummaryProvider(ValueObject &valobj, Stream &stream) { +bool lldb_private::formatters::CharTStringSummaryProvider(ValueObject &valobj, + Stream &stream) { Address valobj_addr = GetArrayAddressOrPointerValue(valobj); if (!valobj_addr.IsValid()) return false; @@ -66,6 +69,11 @@ static bool CharStringSummaryProvider(ValueObject &valobj, Stream &stream) { return true; } +// explicit instantiation for ASCII strings +template bool +lldb_private::formatters::CharTStringSummaryProvider<StringElementType::ASCII>( + ValueObject &, Stream &); + template <StringElementType ElemType> static bool CharSummaryProvider(ValueObject &valobj, Stream &stream) { DataExtractor data; @@ -96,17 +104,17 @@ static bool CharSummaryProvider(ValueObject &valobj, Stream &stream) { bool lldb_private::formatters::Char8StringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { - return CharStringSummaryProvider<StringElementType::UTF8>(valobj, stream); + return CharTStringSummaryProvider<StringElementType::UTF8>(valobj, stream); } bool lldb_private::formatters::Char16StringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { - return CharStringSummaryProvider<StringElementType::UTF16>(valobj, stream); + return CharTStringSummaryProvider<StringElementType::UTF16>(valobj, stream); } bool lldb_private::formatters::Char32StringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { - return CharStringSummaryProvider<StringElementType::UTF32>(valobj, stream); + return CharTStringSummaryProvider<StringElementType::UTF32>(valobj, stream); } bool lldb_private::formatters::WCharStringSummaryProvider( @@ -183,7 +191,7 @@ bool lldb_private::formatters::WCharSummaryProvider( if (!wchar_compiler_type) return false; - // Safe to pass nullptr for exe_scope here. + // Safe to pass nullptr for exe_scope here. std::optional<uint64_t> size = llvm::expectedToOptional(wchar_compiler_type.GetBitSize(nullptr)); if (!size) @@ -214,3 +222,143 @@ bool lldb_private::formatters::WCharSummaryProvider( } return true; } + +template <StringPrinter::StringElementType element_type> +bool lldb_private::formatters::StdStringSummaryProviderImpl( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + ValueObjectSP location_sp, uint64_t size) { + + if (size == 0) { + stream.PutCString(prefix_token); + stream.PutCString("\"\""); + 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; + + 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); +} + +bool lldb_private::formatters::StdStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size) { + return StdStringSummaryProviderImpl<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} + +bool lldb_private::formatters::StdU8StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size) { + return StdStringSummaryProviderImpl<StringPrinter::StringElementType::UTF8>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} + +bool lldb_private::formatters::StdU16StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size) { + return StdStringSummaryProviderImpl<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} + +bool lldb_private::formatters::StdU32StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size) { + return StdStringSummaryProviderImpl<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} + +bool lldb_private::formatters::StdWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_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; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h index a2b606d28cac1..c4e938e89535d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h @@ -10,12 +10,17 @@ #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" namespace lldb_private { namespace formatters { + +template <StringPrinter::StringElementType element_type> +bool CharTStringSummaryProvider(ValueObject &valobj, Stream &stream); + bool Char8StringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // char8_t* @@ -43,6 +48,36 @@ bool Char32SummaryProvider(ValueObject &valobj, Stream &stream, bool WCharSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // wchar_t +template <StringPrinter::StringElementType element_type> +bool StdStringSummaryProviderImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, + uint64_t size); + +bool StdStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size); +bool StdU8StringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size); +bool StdU16StringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, + uint64_t size); +bool StdU32StringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, + uint64_t size); +bool StdWStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, + lldb::ValueObjectSP location_sp, uint64_t size); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index 358cf7d78fa21..f640bc8f5ab69 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -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,8 @@ 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) - 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; - - 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 lldb_private::formatters::StdWStringSummaryProvider( + valobj, stream, summary_options, "L", location_sp, size); } template <StringPrinter::StringElementType element_type> @@ -669,7 +562,7 @@ LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, ValueObjectSP location_sp; std::tie(size, location_sp) = *string_info; - return LibcxxStringSummaryProvider<element_type>( + return StdStringSummaryProviderImpl<element_type>( valobj, stream, summary_options, prefix_token, location_sp, size); } template <StringPrinter::StringElementType element_type> @@ -742,7 +635,7 @@ static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, return true; } - return LibcxxStringSummaryProvider<element_type>( + return StdStringSummaryProviderImpl<element_type>( valobj, stream, summary_options, prefix_token, dataobj, size); } @@ -781,8 +674,8 @@ bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( return true; } - return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, - dataobj, size); + return StdWStringSummaryProvider(valobj, stream, summary_options, "L", + dataobj, size); } static bool diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp new file mode 100644 index 0000000000000..0632ced09e5ab --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp @@ -0,0 +1,140 @@ +//===-- MsvcStl.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MsvcStl.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FormatEntity.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/ValueObject/ValueObject.h" + +#include "Plugins/Language/CPlusPlus/CxxStringTypes.h" + +#include "lldb/lldb-forward.h" +#include <optional> +#include <tuple> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +using StringElementType = StringPrinter::StringElementType; + +static ValueObjectSP ExtractMsvcStlStringData(ValueObject &valobj) { + auto pair = valobj.GetChildMemberWithName("_Mypair"); + if (!pair) + return nullptr; + return pair->GetChildMemberWithName("_Myval2"); +} + +/// Determine the size in bytes of \p valobj (a MSVC STL std::string object) and +/// extract its data payload. Return the size + payload pair. +static std::optional<std::pair<uint64_t, ValueObjectSP>> +ExtractMsvcStlStringInfo(ValueObject &valobj, uint64_t element_size) { + ValueObjectSP valobj_pair_sp = ExtractMsvcStlStringData(valobj); + if (!valobj_pair_sp || !valobj_pair_sp->GetError().Success()) + return {}; + + ValueObjectSP size_sp = valobj_pair_sp->GetChildMemberWithName("_Mysize"); + ValueObjectSP capacity_sp = valobj_pair_sp->GetChildMemberWithName("_Myres"); + ValueObjectSP bx_sp = valobj_pair_sp->GetChildMemberWithName("_Bx"); + if (!size_sp || !capacity_sp || !bx_sp) + return {}; + + bool success = false; + uint64_t size = size_sp->GetValueAsUnsigned(0, &success); + if (!success) + return {}; + uint64_t capacity = capacity_sp->GetValueAsUnsigned(0, &success); + if (!success) + return {}; + + size_t bufSize = std::max<size_t>(16 / element_size, 1); + bool isShortString = capacity < bufSize; + + if (isShortString) { + ValueObjectSP buf_sp = bx_sp->GetChildMemberWithName("_Buf"); + if (buf_sp) + return std::make_pair(size, buf_sp); + return {}; + } + ValueObjectSP ptr_sp = bx_sp->GetChildMemberWithName("_Ptr"); + if (ptr_sp) + return std::make_pair(size, ptr_sp); + return {}; +} + +template <StringPrinter::StringElementType element_type> +static bool +MsvcStlStringSummaryProviderImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + auto string_info = ExtractMsvcStlStringInfo( + valobj, StringPrinter::ElementByteSize<element_type>()); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return StdStringSummaryProviderImpl<element_type>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} +template <StringPrinter::StringElementType element_type> +static bool formatStringImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + StreamString scratch_stream; + const bool success = MsvcStlStringSummaryProviderImpl<element_type>( + valobj, scratch_stream, summary_options, prefix_token); + if (success) + stream << scratch_stream.GetData(); + else + stream << "Summary Unavailable"; + return true; +} + +bool lldb_private::formatters::MsvcStlWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringElementType::UTF16>(valobj, stream, + summary_options, "L"); +} + +template <> +bool lldb_private::formatters::MsvcStlStringSummaryProvider< + StringElementType::ASCII>(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return MsvcStlStringSummaryProviderImpl<StringElementType::ASCII>( + valobj, stream, summary_options, {}); +} +template <> +bool lldb_private::formatters::MsvcStlStringSummaryProvider< + StringElementType::UTF8>(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return MsvcStlStringSummaryProviderImpl<StringElementType::UTF8>( + valobj, stream, summary_options, "u8"); +} +template <> +bool lldb_private::formatters::MsvcStlStringSummaryProvider< + StringElementType::UTF16>(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return MsvcStlStringSummaryProviderImpl<StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} +template <> +bool lldb_private::formatters::MsvcStlStringSummaryProvider< + StringElementType::UTF32>(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return MsvcStlStringSummaryProviderImpl<StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h new file mode 100644 index 0000000000000..c8f7b4027b94c --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -0,0 +1,33 @@ +//===-- MsvcStl.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_MSVCSTL_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_MSVCSTL_H + +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" +#include "lldb/ValueObject/ValueObject.h" + +namespace lldb_private { +namespace formatters { + +template <StringPrinter::StringElementType element_type> +bool MsvcStlStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &summary_options); // VC 2015+ std::string,u8string,u16string,u32string + +bool MsvcStlWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // VC 2015+ std::wstring + +} // namespace formatters +} // namespace lldb_private + +#endif diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py index 5c5cf4ca16b98..32764629d65a7 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -65,11 +65,9 @@ def cleanup(): '(%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 @@ def cleanup(): % 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, ], ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py index f8fc8ae66405b..3883395f23924 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py @@ -81,11 +81,11 @@ def cleanup(): 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 @@ def cleanup(): 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( diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile new file mode 100644 index 0000000000000..f64db8d081fbe --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp + +CXXFLAGS_EXTRAS := -std=c++14 -O0 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py new file mode 100644 index 0000000000000..911313e0d7f3a --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py @@ -0,0 +1,117 @@ +# coding=utf8 +""" +Test std::*string summaries with MSVC's STL. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class MsvcStlStringDataFormatterTestCase(TestBase): + @add_test_categories(["msvcstl"]) + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + + main_spec = lldb.SBFileSpec("main.cpp") + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set break point at this line.", main_spec + ) + frame = thread.frames[0] + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd("type format clear", check=False) + self.runCmd("type summary clear", check=False) + self.runCmd("type filter clear", check=False) + self.runCmd("type synth clear", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect( + "frame variable", + substrs=[ + '(std::wstring) wempty = L""', + '(std::wstring) s = L"hello world! מזל טוב!"', + '(std::wstring) S = L"!!!!"', + "(const wchar_t *) mazeltov = 0x", + 'L"מזל טוב"', + '(std::string) empty = ""', + '(std::string) q = "hello world"', + '(std::string) Q = "quite a long std::strin with lots of info inside it"', + '(std::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"', + '(std::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"', + '(std::u16string) u16_string = u"ß水氶"', + '(std::u16string) u16_empty = u""', + '(std::u32string) u32_string = U"🍄🍅🍆🍌"', + '(std::u32string) u32_empty = U""', + "(std::string *) null_str = nullptr", + ], + ) + + thread.StepOver() + + TheVeryLongOne = frame.FindVariable("TheVeryLongOne") + summaryOptions = lldb.SBTypeSummaryOptions() + summaryOptions.SetCapping(lldb.eTypeSummaryUncapped) + uncappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(uncappedSummaryStream, summaryOptions) + uncappedSummary = uncappedSummaryStream.GetData() + self.assertGreater( + uncappedSummary.find("someText"), + 0, + "uncappedSummary does not include the full string", + ) + summaryOptions.SetCapping(lldb.eTypeSummaryCapped) + cappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(cappedSummaryStream, summaryOptions) + cappedSummary = cappedSummaryStream.GetData() + self.assertLessEqual( + cappedSummary.find("someText"), 0, "cappedSummary includes the full string" + ) + + self.expect_expr( + "s", result_type="std::wstring", result_summary='L"hello world! מזל טוב!"' + ) + + self.expect_expr("q", result_type="std::string", result_summary='"hello world"') + + self.expect_expr( + "Q", + result_type="std::string", + result_summary='"quite a long std::strin with lots of info inside it"', + ) + + self.expect( + "frame variable", + substrs=[ + '(std::wstring) S = L"!!!!!"', + "(const wchar_t *) mazeltov = 0x", + 'L"מזל טוב"', + '(std::string) q = "hello world"', + '(std::string) Q = "quite a long std::strin with lots of info inside it"', + '(std::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"', + '(std::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"', + '(std::u16string) u16_string = u"ß水氶"', + '(std::u32string) u32_string = U"🍄🍅🍆🍌"', + '(std::u32string) u32_empty = U""', + "(std::string *) null_str = nullptr", + ], + ) + + # Finally, make sure that if the string is not readable, we give an error: + bkpt_2 = target.BreakpointCreateBySourceRegex( + "Break here to look at bad string", main_spec + ) + self.assertEqual(bkpt_2.GetNumLocations(), 1, "Got one location") + threads = lldbutil.continue_to_breakpoint(process, bkpt_2) + self.assertEqual(len(threads), 1, "Stopped at second breakpoint") + frame = threads[0].frames[0] + var = frame.FindVariable("in_str") + self.assertTrue(var.GetError().Success(), "Made variable") + self.assertIsNone(var.GetSummary()) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp new file mode 100644 index 0000000000000..6f8dbd9c51b10 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp @@ -0,0 +1,38 @@ +#include <stdint.h> + +#include <string> + +#ifndef _MSVC_STL_VERSION +// this is more of a sanity check that the categories work as expected +#error Not using MSVC STL +#endif + +static size_t touch_string(std::string &in_str) { + return in_str.size(); // Break here to look at bad string +} + +int main() { + std::wstring wempty(L""); + std::wstring s(L"hello world! מזל טוב!"); + std::wstring S(L"!!!!"); + const wchar_t *mazeltov = L"מזל טוב"; + std::string empty(""); + std::string q("hello world"); + std::string Q("quite a long std::strin with lots of info inside it"); + // clang-format off + std::string TheVeryLongOne("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890someText1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + // clang-format on + std::string IHaveEmbeddedZeros("a\0b\0c\0d", 7); + std::wstring IHaveEmbeddedZerosToo( + L"hello world!\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監", 38); + std::u16string u16_string(u"ß水氶"); + std::u16string u16_empty(u""); + std::u32string u32_string(U"🍄🍅🍆🍌"); + std::u32string u32_empty(U""); + std::string *null_str = nullptr; + + S.assign(L"!!!!!"); // Set break point at this line. + std::string *not_a_string = (std::string *)0x0; + touch_string(*not_a_string); + return 0; +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile new file mode 100644 index 0000000000000..58558e6e15f78 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp + +CXXFLAGS_EXTRAS := -std=c++20 -O0 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py new file mode 100644 index 0000000000000..9864cc1bae8ac --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py @@ -0,0 +1,31 @@ +# coding=utf8 +""" +Test std::u8string summary with MSVC's STL. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class MsvcStlU8StringDataFormatterTestCase(TestBase): + @add_test_categories(["msvcstl"]) + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + + lldbutil.run_to_source_breakpoint( + self, "Set break point at this line.", lldb.SBFileSpec("main.cpp") + ) + + self.expect( + "frame variable", + substrs=[ + '(std::u8string) u8_string_small = u8"🍄"', + '(std::u8string) u8_string = u8"❤️👍📄📁😃🧑🌾"', + '(std::u8string) u8_empty = u8""', + '(std::u8string) u8_text = u8"ABC"', + ], + ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp new file mode 100644 index 0000000000000..e01af9fa08e7e --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp @@ -0,0 +1,14 @@ +#include <string> + +#ifndef _MSVC_STL_VERSION +// this is more of a sanity check that the categories work as expected +#error Not using MSVC STL +#endif + +int main() { + std::u8string u8_string_small(u8"🍄"); + std::u8string u8_string(u8"❤️👍📄📁😃🧑🌾"); + std::u8string u8_empty(u8""); + std::u8string u8_text(u8"ABC"); + u8_text.assign(u8"ABCd"); // Set break point at this line. +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits