https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/143177
>From ad7f551c73e713da43b20c8908b517e9e02c50e2 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Wed, 18 Jun 2025 21:49:16 +0200 Subject: [PATCH] [LLDB] Add type summaries for MSVC STL strings --- .../lldb/DataFormatters/StringPrinter.h | 15 ++ lldb/packages/Python/lldbsuite/test/dotest.py | 40 +++++ .../Python/lldbsuite/test/test_categories.py | 1 + .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 138 +++++++++++++---- .../Plugins/Language/CPlusPlus/LibStdcpp.cpp | 127 ++------------- .../Plugins/Language/CPlusPlus/LibStdcpp.h | 4 +- .../Plugins/Language/CPlusPlus/MsvcStl.cpp | 146 ++++++++++++++++++ .../Plugins/Language/CPlusPlus/MsvcStl.h | 35 +++++ .../msvcstl/string/Makefile | 3 + .../string/TestDataFormatterStdString.py | 118 ++++++++++++++ .../msvcstl/string/main.cpp | 40 +++++ .../msvcstl/u8string/Makefile | 4 + .../u8string/TestDataFormatterStdU8String.py | 31 ++++ .../msvcstl/u8string/main.cpp | 14 ++ 15 files changed, 572 insertions(+), 145 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..768fdb390cede 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -831,6 +831,45 @@ def checkLibstdcxxSupport(): configuration.skip_categories.append("libstdcxx") +def canRunMsvcStlTests(): + from lldbsuite.test import lldbplatformutil + + platform = lldbplatformutil.getPlatform() + if platform != "windows": + return False, f"Don't know how to build with MSVC's STL on {platform}" + + with tempfile.NamedTemporaryFile() as f: + cmd = [configuration.compiler, "-xc++", "-o", f.name, "-E", "-"] + p = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + _, stderr = p.communicate( + """ + #include <yvals_core.h> + #ifndef _MSVC_STL_VERSION + #error _MSVC_STL_VERSION not defined + #endif + """ + ) + if not p.returncode: + return True, "Compiling with MSVC STL" + return (False, f"Not compiling with MSVC STL: {stderr}") + +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 +1083,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..63cb937cb5b08 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,36 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "${var.__y_} ${var.__m_} ${var.__wdl_}"))); } +static void RegisterStdStringSummaryProvider( + const lldb::TypeCategoryImplSP &category_sp, llvm::StringRef string_ty, + llvm::StringRef char_ty, lldb::TypeSummaryImplSP summary_sp) { + auto makeSpecifier = [](llvm::StringRef name) { + return std::make_shared<lldb_private::TypeNameSpecifierImpl>( + name, eFormatterMatchExact); + }; + + category_sp->AddTypeSummary(makeSpecifier(string_ty), summary_sp); + + // std::basic_string<char> + category_sp->AddTypeSummary( + makeSpecifier((llvm::Twine("std::basic_string<") + char_ty + ">").str()), + summary_sp); + // std::basic_string<char,std::char_traits<char>,std::allocator<char> > + category_sp->AddTypeSummary( + makeSpecifier((llvm::Twine("std::basic_string<") + char_ty + + ",std::char_traits<" + char_ty + ">,std::allocator<" + + char_ty + "> >") + .str()), + summary_sp); + // std::basic_string<char, std::char_traits<char>, std::allocator<char> > + category_sp->AddTypeSummary( + makeSpecifier((llvm::Twine("std::basic_string<") + char_ty + + ", std::char_traits<" + char_ty + ">, std::allocator<" + + char_ty + "> >") + .str()), + summary_sp); +} + static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) return; @@ -1385,9 +1416,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,17 +1423,6 @@ 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); - cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, cxx11_string_summary_sp); cpp_category_sp->AddTypeSummary( @@ -1418,23 +1435,6 @@ 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); - cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, cxx11_wstring_summary_sp); cpp_category_sp->AddTypeSummary( @@ -1629,6 +1629,80 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::optional<.+>(( )?&)?$", stl_summary_flags, true); } +static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + using StringElementType = StringPrinter::StringElementType; + + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::string", "char", + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + [](ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (IsMsvcStdStringType(valobj)) + return MsvcStlStringSummaryProvider<StringElementType::ASCII>( + valobj, stream, options); + return LibStdcppStringSummaryProvider(valobj, stream, options); + }, + "MSVC STL/libstdc++ std::string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::wstring", "wchar_t", + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + [](ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (IsMsvcStdStringType(valobj)) + return MsvcStlWStringSummaryProvider(valobj, stream, options); + return LibStdcppWStringSummaryProvider(valobj, stream, options); + }, + "MSVC STL/libstdc++ std::wstring summary provider")); +} + +static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + using StringElementType = StringPrinter::StringElementType; + + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u8string", "char8_t", + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::UTF8>, + "MSVC STL std::u8string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u16string", "char16_t", + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::UTF16>, + "MSVC STL std::u16string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u32string", "char32_t", + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::UTF32>, + "MSVC STL std::u32string summary provider")); +} + static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) return; @@ -1743,6 +1817,8 @@ lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() { // LLDB prioritizes the last loaded matching formatter. LoadLibCxxFormatters(g_category); LoadLibStdcppFormatters(g_category); + LoadMsvcStlFormatters(g_category); + LoadCommonStlFormatters(g_category); LoadSystemFormatters(g_category); } }); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index 77e0755607a06..5be83bf525b77 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "LibStdcpp.h" +#include "CxxStringTypes.h" #include "LibCxx.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" @@ -239,122 +240,24 @@ VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { bool lldb_private::formatters::LibStdcppStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { - const bool scalar_is_load_addr = true; - auto [addr_of_string, addr_type] = - valobj.IsPointerOrReferenceType() - ? valobj.GetPointerValue() - : valobj.GetAddressOf(scalar_is_load_addr); - if (addr_of_string != LLDB_INVALID_ADDRESS) { - switch (addr_type) { - case eAddressTypeLoad: { - ProcessSP process_sp(valobj.GetProcessSP()); - if (!process_sp) - return false; - - StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); - Status error; - lldb::addr_t addr_of_data = - process_sp->ReadPointerFromMemory(addr_of_string, error); - if (error.Fail() || addr_of_data == 0 || - addr_of_data == LLDB_INVALID_ADDRESS) - return false; - options.SetLocation(addr_of_data); - options.SetTargetSP(valobj.GetTargetSP()); - options.SetStream(&stream); - options.SetNeedsZeroTermination(false); - options.SetBinaryZeroIsTerminator(true); - lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( - addr_of_string + process_sp->GetAddressByteSize(), error); - if (error.Fail()) - return false; - options.SetSourceSize(size_of_data); - options.SetHasSourceSize(true); - - if (!StringPrinter::ReadStringAndDumpToStream< - StringPrinter::StringElementType::UTF8>(options)) { - stream.Printf("Summary Unavailable"); - return true; - } else - return true; - } break; - case eAddressTypeHost: - break; - case eAddressTypeInvalid: - case eAddressTypeFile: - break; - } - } - return false; + ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"}); + if (!ptr) + return false; + + stream << ptr->GetSummaryAsCString(); + return true; } bool lldb_private::formatters::LibStdcppWStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { - const bool scalar_is_load_addr = true; - auto [addr_of_string, addr_type] = valobj.GetAddressOf(scalar_is_load_addr); - if (addr_of_string != LLDB_INVALID_ADDRESS) { - switch (addr_type) { - case eAddressTypeLoad: { - ProcessSP process_sp(valobj.GetProcessSP()); - if (!process_sp) - return false; - - 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)); - if (!size) - return false; - const uint32_t wchar_size = *size; - - StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); - Status error; - lldb::addr_t addr_of_data = - process_sp->ReadPointerFromMemory(addr_of_string, error); - if (error.Fail() || addr_of_data == 0 || - addr_of_data == LLDB_INVALID_ADDRESS) - return false; - options.SetLocation(addr_of_data); - options.SetTargetSP(valobj.GetTargetSP()); - options.SetStream(&stream); - options.SetNeedsZeroTermination(false); - options.SetBinaryZeroIsTerminator(false); - lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( - addr_of_string + process_sp->GetAddressByteSize(), error); - if (error.Fail()) - return false; - options.SetSourceSize(size_of_data); - options.SetHasSourceSize(true); - options.SetPrefixToken("L"); - - switch (wchar_size) { - case 8: - return StringPrinter::ReadStringAndDumpToStream< - StringPrinter::StringElementType::UTF8>(options); - case 16: - return StringPrinter::ReadStringAndDumpToStream< - StringPrinter::StringElementType::UTF16>(options); - case 32: - return StringPrinter::ReadStringAndDumpToStream< - StringPrinter::StringElementType::UTF32>(options); - default: - stream.Printf("size for wchar_t is not valid"); - return true; - } - return true; - } break; - case eAddressTypeHost: - break; - case eAddressTypeInvalid: - case eAddressTypeFile: - break; - } - } - return false; + ValueObjectSP dataplus = valobj.GetChildMemberWithName("_M_dataplus"); + if (!dataplus) + return false; + ValueObjectSP ptr = dataplus->GetChildMemberWithName("_M_p"); + if (!ptr) + return false; + + return WCharStringSummaryProvider(*ptr, stream, options); } LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd( diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index c7b2130275ad8..97974ab1731e9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -18,11 +18,11 @@ namespace lldb_private { namespace formatters { bool LibStdcppStringSummaryProvider( ValueObject &valobj, Stream &stream, - const TypeSummaryOptions &options); // libcstdc++ c++11 std::string + const TypeSummaryOptions &options); // libcstdc++ std::string bool LibStdcppWStringSummaryProvider( ValueObject &valobj, Stream &stream, - const TypeSummaryOptions &options); // libcstdc++ c++11 std::wstring + const TypeSummaryOptions &options); // libcstdc++ std::wstring bool LibStdcppSmartPointerSummaryProvider( ValueObject &valobj, Stream &stream, diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp new file mode 100644 index 0000000000000..9f56b030250c2 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp @@ -0,0 +1,146 @@ +//===-- 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 StringBufferSummaryProvider<element_type>( + stream, summary_options, location_sp, size, prefix_token); +} +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::IsMsvcStdStringType(ValueObject &valobj) { + std::vector<uint32_t> indexes; + return valobj.GetCompilerType().GetIndexOfChildMemberWithName("_Mypair", true, + indexes) > 0; +} + +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..612e78336dd1d --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -0,0 +1,35 @@ +//===-- 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 { + +bool IsMsvcStdStringType(ValueObject &valobj); + +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/msvcstl/string/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +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..91bc5e84c8048 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py @@ -0,0 +1,118 @@ +# 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) overwritten_zero = "abc"', + '(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..fcfb5d48e9bb1 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp @@ -0,0 +1,40 @@ +#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"); + std::string overwritten_zero("abc"); + const_cast<char *>(overwritten_zero.data())[3] = 'd'; + // 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