https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/143177
>From 06dbd7cbb51986f5718427cf47535f1815273378 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Wed, 11 Jun 2025 18:11:20 +0200 Subject: [PATCH 1/2] [LLDB] Add optional callback function to `TypeMatcher` --- .../include/lldb/DataFormatters/FormatCache.h | 6 ++- .../lldb/DataFormatters/FormatClasses.h | 7 ++- .../lldb/DataFormatters/FormattersContainer.h | 11 ++++- lldb/include/lldb/DataFormatters/TypeFormat.h | 4 ++ .../include/lldb/DataFormatters/TypeSummary.h | 5 +++ .../lldb/DataFormatters/TypeSynthetic.h | 5 +++ .../lldb/DataFormatters/TypeValidator.h | 20 +++++++++ lldb/source/DataFormatters/FormatCache.cpp | 43 +++++++++++++------ lldb/source/DataFormatters/FormatManager.cpp | 2 +- .../DataFormatters/LanguageCategory.cpp | 2 +- 10 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 lldb/include/lldb/DataFormatters/TypeValidator.h diff --git a/lldb/include/lldb/DataFormatters/FormatCache.h b/lldb/include/lldb/DataFormatters/FormatCache.h index 3f1baa26a5a54..889a555f2c4c6 100644 --- a/lldb/include/lldb/DataFormatters/FormatCache.h +++ b/lldb/include/lldb/DataFormatters/FormatCache.h @@ -13,6 +13,7 @@ #include <map> #include <mutex> +#include "lldb/DataFormatters/FormatClasses.h" #include "lldb/Utility/ConstString.h" #include "lldb/lldb-public.h" @@ -32,7 +33,7 @@ class FormatCache { public: Entry(); - template<typename ImplSP> bool IsCached(); + template <typename ImplSP> bool IsCached(FormattersMatchData &match_data); bool IsFormatCached(); bool IsSummaryCached(); bool IsSyntheticCached(); @@ -54,7 +55,8 @@ class FormatCache { public: FormatCache() = default; - template <typename ImplSP> bool Get(ConstString type, ImplSP &format_impl_sp); + template <typename ImplSP> + bool Get(FormattersMatchData &match_data, ImplSP &format_impl_sp); void Set(ConstString type, lldb::TypeFormatImplSP &format_sp); void Set(ConstString type, lldb::TypeSummaryImplSP &summary_sp); void Set(ConstString type, lldb::SyntheticChildrenSP &synthetic_sp); diff --git a/lldb/include/lldb/DataFormatters/FormatClasses.h b/lldb/include/lldb/DataFormatters/FormatClasses.h index 89d74649ecc64..9131a8919ec57 100644 --- a/lldb/include/lldb/DataFormatters/FormatClasses.h +++ b/lldb/include/lldb/DataFormatters/FormatClasses.h @@ -154,8 +154,8 @@ class TypeNameSpecifierImpl { TypeNameSpecifierImpl() = default; TypeNameSpecifierImpl(llvm::StringRef name, - lldb::FormatterMatchType match_type) - : m_match_type(match_type) { + lldb::FormatterMatchType match_type, uint32_t tag = 0) + : m_match_type(match_type), m_tag(tag) { m_type.m_type_name = std::string(name); } @@ -192,6 +192,8 @@ class TypeNameSpecifierImpl { bool IsRegex() { return m_match_type == lldb::eFormatterMatchRegex; } + uint32_t GetTag() const { return m_tag; } + private: lldb::FormatterMatchType m_match_type = lldb::eFormatterMatchExact; // TODO: Replace this with TypeAndOrName. @@ -200,6 +202,7 @@ class TypeNameSpecifierImpl { CompilerType m_compiler_type; }; TypeOrName m_type; + uint32_t m_tag = 0; TypeNameSpecifierImpl(const TypeNameSpecifierImpl &) = delete; const TypeNameSpecifierImpl & diff --git a/lldb/include/lldb/DataFormatters/FormattersContainer.h b/lldb/include/lldb/DataFormatters/FormattersContainer.h index f7465fc65538d..cec48cf508fb3 100644 --- a/lldb/include/lldb/DataFormatters/FormattersContainer.h +++ b/lldb/include/lldb/DataFormatters/FormattersContainer.h @@ -50,6 +50,7 @@ class TypeMatcher { /// - eFormatterMatchCallback: run the function in m_name to decide if a type /// matches or not. lldb::FormatterMatchType m_match_type; + uint32_t m_tag = 0; // if the user tries to add formatters for, say, "struct Foo" those will not // match any type because of the way we strip qualifiers from typenames this @@ -86,7 +87,8 @@ class TypeMatcher { /// name specifier. TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier) : m_name(type_specifier->GetName()), - m_match_type(type_specifier->GetMatchType()) { + m_match_type(type_specifier->GetMatchType()), + m_tag(type_specifier->GetTag()) { if (m_match_type == lldb::eFormatterMatchRegex) m_type_name_regex = RegularExpression(type_specifier->GetName()); } @@ -134,7 +136,7 @@ class TypeMatcher { /// (lldb) type summary add --summary-string \"A\" -x TypeName /// (lldb) type summary delete TypeName bool CreatedBySameMatchString(TypeMatcher other) const { - return GetMatchString() == other.GetMatchString(); + return GetMatchString() == other.GetMatchString() && m_tag == other.m_tag; } }; @@ -181,6 +183,11 @@ template <typename ValueType> class FormattersContainer { std::lock_guard<std::recursive_mutex> guard(m_map_mutex); for (auto &formatter : llvm::reverse(m_map)) { if (formatter.first.Matches(candidate)) { + if (formatter.second && formatter.second->GetTypeValidator() && + !formatter.second->GetTypeValidator()( + candidate.GetType().GetCompilerType(false))) + continue; + entry = formatter.second; return true; } diff --git a/lldb/include/lldb/DataFormatters/TypeFormat.h b/lldb/include/lldb/DataFormatters/TypeFormat.h index d242f9083d608..ec2b9afcdc9dc 100644 --- a/lldb/include/lldb/DataFormatters/TypeFormat.h +++ b/lldb/include/lldb/DataFormatters/TypeFormat.h @@ -151,10 +151,14 @@ class TypeFormatImpl { virtual std::string GetDescription() = 0; + CxxTypeValidatorFn *GetTypeValidator() const { return m_validator_fn; } + void SetTypeValidator(CxxTypeValidatorFn *value) { m_validator_fn = value; } + protected: Flags m_flags; uint32_t m_my_revision = 0; uint32_t m_ptr_match_depth = 1; + CxxTypeValidatorFn *m_validator_fn = nullptr; private: TypeFormatImpl(const TypeFormatImpl &) = delete; diff --git a/lldb/include/lldb/DataFormatters/TypeSummary.h b/lldb/include/lldb/DataFormatters/TypeSummary.h index 589f68c2ce314..14b4a2c797871 100644 --- a/lldb/include/lldb/DataFormatters/TypeSummary.h +++ b/lldb/include/lldb/DataFormatters/TypeSummary.h @@ -15,6 +15,7 @@ #include <memory> #include <string> +#include "lldb/DataFormatters/TypeValidator.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-public.h" @@ -276,6 +277,9 @@ class TypeSummaryImpl { uint32_t &GetRevision() { return m_my_revision; } + CxxTypeValidatorFn *GetTypeValidator() const { return m_validator_fn; } + void SetTypeValidator(CxxTypeValidatorFn *value) { m_validator_fn = value; } + typedef std::shared_ptr<TypeSummaryImpl> SharedPointer; protected: @@ -288,6 +292,7 @@ class TypeSummaryImpl { private: Kind m_kind; uint32_t m_ptr_match_depth = 1; + CxxTypeValidatorFn *m_validator_fn = nullptr; TypeSummaryImpl(const TypeSummaryImpl &) = delete; const TypeSummaryImpl &operator=(const TypeSummaryImpl &) = delete; }; diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h index 11a4ca2cd8330..a6856efce7df8 100644 --- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h +++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h @@ -277,12 +277,17 @@ class SyntheticChildren { void SetPtrMatchDepth(uint32_t value) { m_ptr_match_depth = value; } + CxxTypeValidatorFn *GetTypeValidator() const { return m_validator_fn; } + void SetTypeValidator(CxxTypeValidatorFn *value) { m_validator_fn = value; } + protected: uint32_t m_my_revision = 0; Flags m_flags; uint32_t m_ptr_match_depth = 1; private: + CxxTypeValidatorFn *m_validator_fn = nullptr; + SyntheticChildren(const SyntheticChildren &) = delete; const SyntheticChildren &operator=(const SyntheticChildren &) = delete; }; diff --git a/lldb/include/lldb/DataFormatters/TypeValidator.h b/lldb/include/lldb/DataFormatters/TypeValidator.h new file mode 100644 index 0000000000000..0981b7452ffca --- /dev/null +++ b/lldb/include/lldb/DataFormatters/TypeValidator.h @@ -0,0 +1,20 @@ +//===-- TypeValidator.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_DATAFORMATTERS_TYPEVALIDATOR_H +#define LLDB_DATAFORMATTERS_TYPEVALIDATOR_H + +#include "lldb/Symbol/CompilerType.h" + +namespace lldb_private { + +using CxxTypeValidatorFn = bool(const CompilerType &); + +} // namespace lldb_private + +#endif // LLDB_DATAFORMATTERS_TYPEVALIDATOR_H diff --git a/lldb/source/DataFormatters/FormatCache.cpp b/lldb/source/DataFormatters/FormatCache.cpp index 6c83b36e79dea..3f9160702afe4 100644 --- a/lldb/source/DataFormatters/FormatCache.cpp +++ b/lldb/source/DataFormatters/FormatCache.cpp @@ -53,23 +53,41 @@ void FormatCache::Entry::Set(lldb::SyntheticChildrenSP synthetic_sp) { namespace lldb_private { -template<> bool FormatCache::Entry::IsCached<lldb::TypeFormatImplSP>() { - return IsFormatCached(); +template <typename ImplSP> +static bool passesValidator(const ImplSP &impl_sp, + FormattersMatchData &match_data) { + if (!impl_sp || !impl_sp->GetTypeValidator()) + return true; + + ValueObject &valobj = match_data.GetValueObject(); + lldb::ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable( + match_data.GetDynamicValueType(), valobj.IsSynthetic()); + return valobj_sp && impl_sp->GetTypeValidator()(valobj_sp->GetCompilerType()); +} + +template <> +bool FormatCache::Entry::IsCached<lldb::TypeFormatImplSP>( + FormattersMatchData &match_data) { + return IsFormatCached() && passesValidator(m_format_sp, match_data); } -template<> bool FormatCache::Entry::IsCached<lldb::TypeSummaryImplSP> () { - return IsSummaryCached(); +template <> +bool FormatCache::Entry::IsCached<lldb::TypeSummaryImplSP>( + FormattersMatchData &match_data) { + return IsSummaryCached() && passesValidator(m_summary_sp, match_data); } -template<> bool FormatCache::Entry::IsCached<lldb::SyntheticChildrenSP>() { - return IsSyntheticCached(); +template <> +bool FormatCache::Entry::IsCached<lldb::SyntheticChildrenSP>( + FormattersMatchData &match_data) { + return IsSyntheticCached() && passesValidator(m_synthetic_sp, match_data); } } // namespace lldb_private template <typename ImplSP> -bool FormatCache::Get(ConstString type, ImplSP &format_impl_sp) { +bool FormatCache::Get(FormattersMatchData &match_data, ImplSP &format_impl_sp) { std::lock_guard<std::recursive_mutex> guard(m_mutex); - auto entry = m_entries[type]; - if (entry.IsCached<ImplSP>()) { + auto entry = m_entries[match_data.GetTypeForCache()]; + if (entry.IsCached<ImplSP>(match_data)) { m_cache_hits++; entry.Get(format_impl_sp); return true; @@ -82,12 +100,13 @@ bool FormatCache::Get(ConstString type, ImplSP &format_impl_sp) { /// Explicit instantiations for the three types. /// \{ template bool -FormatCache::Get<lldb::TypeFormatImplSP>(ConstString, lldb::TypeFormatImplSP &); +FormatCache::Get<lldb::TypeFormatImplSP>(FormattersMatchData &, + lldb::TypeFormatImplSP &); template bool -FormatCache::Get<lldb::TypeSummaryImplSP>(ConstString, +FormatCache::Get<lldb::TypeSummaryImplSP>(FormattersMatchData &, lldb::TypeSummaryImplSP &); template bool -FormatCache::Get<lldb::SyntheticChildrenSP>(ConstString, +FormatCache::Get<lldb::SyntheticChildrenSP>(FormattersMatchData &, lldb::SyntheticChildrenSP &); /// \} diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index 122f2304ead24..baec64cfb9c2d 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -657,7 +657,7 @@ ImplSP FormatManager::GetCached(FormattersMatchData &match_data) { if (match_data.GetTypeForCache()) { LLDB_LOGF(log, "\n\n" FORMAT_LOG("Looking into cache for type %s"), match_data.GetTypeForCache().AsCString("<invalid>")); - if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) { + if (m_format_cache.Get(match_data, retval_sp)) { if (log) { LLDB_LOGF(log, FORMAT_LOG("Cache search success. Returning.")); LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}", diff --git a/lldb/source/DataFormatters/LanguageCategory.cpp b/lldb/source/DataFormatters/LanguageCategory.cpp index 4794186ce9aec..4b6bc294fd711 100644 --- a/lldb/source/DataFormatters/LanguageCategory.cpp +++ b/lldb/source/DataFormatters/LanguageCategory.cpp @@ -40,7 +40,7 @@ bool LanguageCategory::Get(FormattersMatchData &match_data, return false; if (match_data.GetTypeForCache()) { - if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) + if (m_format_cache.Get(match_data, retval_sp)) return (bool)retval_sp; } >From b2f614c8774bb12fbd4f8b2c83278b1fae4639e8 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Fri, 6 Jun 2025 19:23:04 +0200 Subject: [PATCH 2/2] [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 | 134 ++++++++++++--- .../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, 728 insertions(+), 155 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..c03414522e9d0 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; @@ -54,6 +55,9 @@ using namespace lldb_private::formatters; LLDB_PLUGIN_DEFINE(CPlusPlusLanguage) +static constexpr uint32_t LIBSTDCXX_TAG = 1; +static constexpr uint32_t MSVC_STL_TAG = 2; + void CPlusPlusLanguage::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language", CreateInstance, &DebuggerInitialize); @@ -1372,6 +1376,48 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "${var.__y_} ${var.__m_} ${var.__wdl_}"))); } +static bool IsMsvcStdStringType(const CompilerType &type) { + std::vector<uint32_t> indexes; + return type.GetIndexOfChildMemberWithName("_Mypair", true, indexes) > 0; +} + +static bool IsLibstdcppStdStringType(const CompilerType &type) { + std::vector<uint32_t> indexes; + return type.GetIndexOfChildMemberWithName("_M_dataplus", true, indexes) > 0; +} + +static void RegisterStdStringSummaryProvider( + const lldb::TypeCategoryImplSP &category_sp, llvm::StringRef string_ty, + llvm::StringRef char_ty, CxxTypeValidatorFn *validator_fn, uint32_t tag, + lldb::TypeSummaryImplSP summary_sp) { + summary_sp->SetTypeValidator(validator_fn); + auto makeSpecifier = [tag](llvm::StringRef name) { + return std::make_shared<lldb_private::TypeNameSpecifierImpl>( + name, eFormatterMatchExact, tag); + }; + + 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 +1431,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 +1438,11 @@ 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); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::string", "char", IsLibstdcppStdStringType, + LIBSTDCXX_TAG, + std::make_shared<StringSummaryFormat>(stl_summary_flags, + "${var._M_dataplus._M_p}")); cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, cxx11_string_summary_sp); @@ -1420,20 +1458,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_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); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::wstring", "wchar_t", IsLibstdcppStdStringType, + LIBSTDCXX_TAG, + std::make_shared<StringSummaryFormat>(stl_summary_flags, + "${var._M_dataplus._M_p%S}")); cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, cxx11_wstring_summary_sp); @@ -1629,6 +1658,56 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::optional<.+>(( )?&)?$", stl_summary_flags, true); } +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::string", "char", IsMsvcStdStringType, MSVC_STL_TAG, + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::ASCII>, + "MSVC STL std::string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::wstring", "wchar_t", IsMsvcStdStringType, + MSVC_STL_TAG, + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, MsvcStlWStringSummaryProvider, + "MSVC STL std::wstring summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u8string", "char8_t", IsMsvcStdStringType, + MSVC_STL_TAG, + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::UTF8>, + "MSVC STL std::u8string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u16string", "char16_t", IsMsvcStdStringType, + MSVC_STL_TAG, + std::make_shared<CXXFunctionSummaryFormat>( + stl_summary_flags, + MsvcStlStringSummaryProvider<StringElementType::UTF16>, + "MSVC STL std::u16string summary provider")); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::u32string", "char32_t", IsMsvcStdStringType, + MSVC_STL_TAG, + 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 +1822,7 @@ lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() { // LLDB prioritizes the last loaded matching formatter. LoadLibCxxFormatters(g_category); LoadLibStdcppFormatters(g_category); + LoadMsvcStlFormatters(g_category); LoadSystemFormatters(g_category); } }); 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