https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/143177
>From 519f0899e79a08c5a3548923e188747af8c6f764 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Fri, 6 Jun 2025 19:23:04 +0200 Subject: [PATCH] [LLDB] Add type summaries for MSVC STL strings --- .../lldb/DataFormatters/StringPrinter.h | 15 ++ .../include/lldb/DataFormatters/TypeSummary.h | 99 +++++++++-- .../lldb/DataFormatters/ValueObjectPrinter.h | 2 +- lldb/packages/Python/lldbsuite/test/dotest.py | 21 +++ .../Python/lldbsuite/test/test_categories.py | 1 + lldb/source/API/SBTypeSummary.cpp | 10 +- lldb/source/Commands/CommandObjectType.cpp | 7 +- lldb/source/DataFormatters/TypeSummary.cpp | 147 ++++++++++++---- .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 146 ++++++++++++---- .../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 ++ 23 files changed, 951 insertions(+), 209 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/include/lldb/DataFormatters/TypeSummary.h b/lldb/include/lldb/DataFormatters/TypeSummary.h index 589f68c2ce314..95d7a2a079e07 100644 --- a/lldb/include/lldb/DataFormatters/TypeSummary.h +++ b/lldb/include/lldb/DataFormatters/TypeSummary.h @@ -48,7 +48,14 @@ class TypeSummaryOptions { class TypeSummaryImpl { public: - enum class Kind { eSummaryString, eScript, eBytecode, eCallback, eInternal }; + enum class Kind { + eSummaryString, + eScript, + eBytecode, + eCallback, + eCascading, + eInternal + }; virtual ~TypeSummaryImpl() = default; @@ -292,18 +299,29 @@ class TypeSummaryImpl { const TypeSummaryImpl &operator=(const TypeSummaryImpl &) = delete; }; -// simple string-based summaries, using ${var to show data -struct StringSummaryFormat : public TypeSummaryImpl { +struct StringSummaryData { std::string m_format_str; FormatEntity::Entry m_format; Status m_error; + StringSummaryData(const char *f); + void SetFormat(const char *f); + + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options, + const TypeSummaryImpl &summary); +}; + +// simple string-based summaries, using ${var to show data +struct StringSummaryFormat : public TypeSummaryImpl { + StringSummaryData m_data; + StringSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *f, uint32_t ptr_match_depth = 1); ~StringSummaryFormat() override = default; - const char *GetSummaryString() const { return m_format_str.c_str(); } + const char *GetSummaryString() const { return m_data.m_format_str.c_str(); } void SetSummaryString(const char *f); @@ -323,15 +341,23 @@ struct StringSummaryFormat : public TypeSummaryImpl { const StringSummaryFormat &operator=(const StringSummaryFormat &) = delete; }; -// summaries implemented via a C++ function -struct CXXFunctionSummaryFormat : public TypeSummaryImpl { +struct CXXFunctionSummaryData { // we should convert these to SBValue and SBStream if we ever cross the // boundary towards the external world - typedef std::function<bool(ValueObject &, Stream &, - const TypeSummaryOptions &)> - Callback; + using Callback = + std::function<bool(ValueObject &, Stream &, const TypeSummaryOptions &)>; Callback m_impl; + + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options, + const TypeSummaryImpl &summary); +}; + +// summaries implemented via a C++ function +struct CXXFunctionSummaryFormat : public TypeSummaryImpl { + using Callback = CXXFunctionSummaryData::Callback; + CXXFunctionSummaryData m_data; std::string m_description; CXXFunctionSummaryFormat(const TypeSummaryImpl::Flags &flags, Callback impl, @@ -340,11 +366,13 @@ struct CXXFunctionSummaryFormat : public TypeSummaryImpl { ~CXXFunctionSummaryFormat() override = default; - Callback GetBackendFunction() const { return m_impl; } + Callback GetBackendFunction() const { return m_data.m_impl; } const char *GetTextualInfo() const { return m_description.c_str(); } - void SetBackendFunction(Callback cb_func) { m_impl = std::move(cb_func); } + void SetBackendFunction(Callback cb_func) { + m_data.m_impl = std::move(cb_func); + } void SetTextualInfo(const char *descr) { if (descr) @@ -372,6 +400,55 @@ struct CXXFunctionSummaryFormat : public TypeSummaryImpl { operator=(const CXXFunctionSummaryFormat &) = delete; }; +// Multiple summaries for the same type name but different type layouts. +// A validator function checks the layout. +struct CXXCascadingSummaryFormat : public TypeSummaryImpl { + using Validator = bool(ValueObject &valobj); + using Impl = std::variant<CXXFunctionSummaryData, StringSummaryData>; + using ImplEntry = std::pair<Validator *, Impl>; + using ImplList = llvm::SmallVector<ImplEntry, 2>; + + ImplList m_impls; + std::string m_description; + + CXXCascadingSummaryFormat(const TypeSummaryImpl::Flags &flags, + const char *description, ImplList impls = {}, + uint32_t ptr_match_depth = 1); + + ~CXXCascadingSummaryFormat() override = default; + + const char *GetTextualInfo() const { return m_description.c_str(); } + + CXXCascadingSummaryFormat *Append(Validator *validator, Impl impl); + + void SetTextualInfo(const char *descr) { + if (descr) + m_description.assign(descr); + else + m_description.clear(); + } + + ImplList CopyImpls() const; + + bool FormatObject(ValueObject *valobj, std::string &dest, + const TypeSummaryOptions &options) override; + + std::string GetDescription() override; + + static bool classof(const TypeSummaryImpl *S) { + return S->GetKind() == Kind::eCascading; + } + + std::string GetName() override; + + using SharedPointer = std::shared_ptr<CXXCascadingSummaryFormat>; + +private: + CXXCascadingSummaryFormat(const CXXCascadingSummaryFormat &) = delete; + const CXXCascadingSummaryFormat & + operator=(const CXXCascadingSummaryFormat &) = delete; +}; + // Python-based summaries, running script code to show data struct ScriptSummaryFormat : public TypeSummaryImpl { std::string m_function_name; diff --git a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h index f9deb6ebf8d6d..1f20c3180d37a 100644 --- a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h +++ b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h @@ -165,7 +165,7 @@ class ValueObjectPrinter { std::string m_error; bool m_val_summary_ok; - friend struct StringSummaryFormat; + friend struct StringSummaryData; ValueObjectPrinter(const ValueObjectPrinter &) = delete; const ValueObjectPrinter &operator=(const ValueObjectPrinter &) = delete; 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/API/SBTypeSummary.cpp b/lldb/source/API/SBTypeSummary.cpp index 58ec068ab9600..ef7e22b1a79af 100644 --- a/lldb/source/API/SBTypeSummary.cpp +++ b/lldb/source/API/SBTypeSummary.cpp @@ -370,6 +370,9 @@ bool SBTypeSummary::IsEqualTo(lldb::SBTypeSummary &rhs) { if (IsSummaryString() != rhs.IsSummaryString()) return false; return GetOptions() == rhs.GetOptions(); + case TypeSummaryImpl::Kind::eCascading: + return llvm::dyn_cast<CXXCascadingSummaryFormat>(m_opaque_sp.get()) == + llvm::dyn_cast<CXXCascadingSummaryFormat>(rhs.m_opaque_sp.get()); case TypeSummaryImpl::Kind::eInternal: return (m_opaque_sp.get() == rhs.m_opaque_sp.get()); } @@ -406,7 +409,7 @@ bool SBTypeSummary::CopyOnWrite_Impl() { if (CXXFunctionSummaryFormat *current_summary_ptr = llvm::dyn_cast<CXXFunctionSummaryFormat>(m_opaque_sp.get())) { new_sp = TypeSummaryImplSP(new CXXFunctionSummaryFormat( - GetOptions(), current_summary_ptr->m_impl, + GetOptions(), current_summary_ptr->m_data.m_impl, current_summary_ptr->m_description.c_str())); } else if (ScriptSummaryFormat *current_summary_ptr = llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get())) { @@ -417,6 +420,11 @@ bool SBTypeSummary::CopyOnWrite_Impl() { llvm::dyn_cast<StringSummaryFormat>(m_opaque_sp.get())) { new_sp = TypeSummaryImplSP(new StringSummaryFormat( GetOptions(), current_summary_ptr->GetSummaryString())); + } else if (CXXCascadingSummaryFormat *current_summary_ptr = + llvm::dyn_cast<CXXCascadingSummaryFormat>(m_opaque_sp.get())) { + new_sp = TypeSummaryImplSP(new CXXCascadingSummaryFormat( + GetOptions(), current_summary_ptr->GetTextualInfo(), + current_summary_ptr->CopyImpls())); } SetSP(new_sp); diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 19cd3ff2972e9..bd6138a860caa 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -1397,9 +1397,10 @@ bool CommandObjectTypeSummaryAdd::Execute_StringSummary( result.AppendError("summary creation failed"); return false; } - if (string_format->m_error.Fail()) { - result.AppendErrorWithFormat("syntax error: %s", - string_format->m_error.AsCString("<unknown>")); + if (string_format->m_data.m_error.Fail()) { + result.AppendErrorWithFormat( + "syntax error: %s", + string_format->m_data.m_error.AsCString("<unknown>")); return false; } lldb::TypeSummaryImplSP entry(string_format.release()); diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp index 6aa290698cd12..532e14f6f0aba 100644 --- a/lldb/source/DataFormatters/TypeSummary.cpp +++ b/lldb/source/DataFormatters/TypeSummary.cpp @@ -57,33 +57,40 @@ std::string TypeSummaryImpl::GetSummaryKindName() { return "python"; case Kind::eInternal: return "c++"; + case Kind::eCascading: + return "cascading"; case Kind::eBytecode: return "bytecode"; } llvm_unreachable("Unknown type kind name"); } -StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags &flags, - const char *format_cstr, - uint32_t ptr_match_depth) - : TypeSummaryImpl(Kind::eSummaryString, flags, ptr_match_depth), - m_format_str() { - SetSummaryString(format_cstr); -} +StringSummaryData::StringSummaryData(const char *f) { SetFormat(f); } -void StringSummaryFormat::SetSummaryString(const char *format_cstr) { +void StringSummaryData::SetFormat(const char *f) { m_format.Clear(); - if (format_cstr && format_cstr[0]) { - m_format_str = format_cstr; - m_error = FormatEntity::Parse(format_cstr, m_format); + if (f && f[0]) { + m_format_str = f; + m_error = FormatEntity::Parse(f, m_format); } else { m_format_str.clear(); m_error.Clear(); } } -bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, - const TypeSummaryOptions &options) { +StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags &flags, + const char *format_cstr, + uint32_t ptr_match_depth) + : TypeSummaryImpl(Kind::eSummaryString, flags, ptr_match_depth), + m_data(format_cstr) {} + +void StringSummaryFormat::SetSummaryString(const char *format_cstr) { + m_data.SetFormat(format_cstr); +} + +bool StringSummaryData::FormatObject(ValueObject *valobj, std::string &retval, + const TypeSummaryOptions &options, + const TypeSummaryImpl &summary) { if (!valobj) { retval.assign("NULL ValueObject"); return false; @@ -96,12 +103,12 @@ bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, if (frame) sc = frame->GetSymbolContext(lldb::eSymbolContextEverything); - if (IsOneLiner()) { + if (summary.IsOneLiner()) { // We've already checked the case of a NULL valobj above. Let's put in an // assert here to make sure someone doesn't take that out: assert(valobj && "Must have a valid ValueObject to summarize"); ValueObjectPrinter printer(*valobj, &s, DumpValueObjectOptions()); - printer.PrintChildrenOneLiner(HideNames(valobj)); + printer.PrintChildrenOneLiner(summary.HideNames(valobj)); retval = std::string(s.GetString()); return true; } else { @@ -117,40 +124,52 @@ bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, } } +bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, + const TypeSummaryOptions &options) { + return m_data.FormatObject(valobj, retval, options, *this); +} + std::string StringSummaryFormat::GetDescription() { StreamString sstr; - sstr.Printf("`%s`%s%s%s%s%s%s%s%s%s ptr-match-depth=%u", m_format_str.c_str(), - m_error.Fail() ? " error: " : "", - m_error.Fail() ? m_error.AsCString() : "", - Cascades() ? "" : " (not cascading)", - !DoesPrintChildren(nullptr) ? "" : " (show children)", - !DoesPrintValue(nullptr) ? " (hide value)" : "", - IsOneLiner() ? " (one-line printout)" : "", - SkipsPointers() ? " (skip pointers)" : "", - SkipsReferences() ? " (skip references)" : "", - HideNames(nullptr) ? " (hide member names)" : "", - GetPtrMatchDepth()); + sstr.Printf( + "`%s`%s%s%s%s%s%s%s%s%s ptr-match-depth=%u", m_data.m_format_str.c_str(), + m_data.m_error.Fail() ? " error: " : "", + m_data.m_error.Fail() ? m_data.m_error.AsCString() : "", + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : "", GetPtrMatchDepth()); return std::string(sstr.GetString()); } -std::string StringSummaryFormat::GetName() { return m_format_str; } +std::string StringSummaryFormat::GetName() { return m_data.m_format_str; } + +bool CXXFunctionSummaryData::FormatObject(ValueObject *valobj, + std::string &dest, + const TypeSummaryOptions &options, + const TypeSummaryImpl &summary) { + dest.clear(); + StreamString stream; + if (!m_impl || !m_impl(*valobj, stream, options)) + return false; + dest = std::string(stream.GetString()); + return true; +} CXXFunctionSummaryFormat::CXXFunctionSummaryFormat( const TypeSummaryImpl::Flags &flags, Callback impl, const char *description, uint32_t ptr_match_depth) - : TypeSummaryImpl(Kind::eCallback, flags, ptr_match_depth), m_impl(impl), + : TypeSummaryImpl(Kind::eCallback, flags, ptr_match_depth), m_data{impl}, m_description(description ? description : "") {} bool CXXFunctionSummaryFormat::FormatObject(ValueObject *valobj, std::string &dest, const TypeSummaryOptions &options) { - dest.clear(); - StreamString stream; - if (!m_impl || !m_impl(*valobj, stream, options)) - return false; - dest = std::string(stream.GetString()); - return true; + return m_data.FormatObject(valobj, dest, options, *this); } std::string CXXFunctionSummaryFormat::GetDescription() { @@ -169,6 +188,66 @@ std::string CXXFunctionSummaryFormat::GetDescription() { std::string CXXFunctionSummaryFormat::GetName() { return m_description; } +CXXCascadingSummaryFormat::CXXCascadingSummaryFormat( + const TypeSummaryImpl::Flags &flags, const char *description, + ImplList impls, uint32_t ptr_match_depth) + : TypeSummaryImpl(Kind::eCascading, flags, ptr_match_depth), + m_impls(std::move(impls)) {} + +CXXCascadingSummaryFormat * +CXXCascadingSummaryFormat::Append(Validator *validator, Impl impl) { + m_impls.emplace_back(validator, std::move(impl)); + return this; +} + +CXXCascadingSummaryFormat::ImplList +CXXCascadingSummaryFormat::CopyImpls() const { + auto copy_impl = [](const ImplEntry &entry) -> ImplEntry { + return {entry.first, + std::visit( + llvm::makeVisitor( + [](const CXXFunctionSummaryData &fn) -> Impl { return fn; }, + [](const StringSummaryData &string) -> Impl { + return StringSummaryData(string.m_format_str.c_str()); + }), + entry.second)}; + }; + return {llvm::map_iterator(m_impls.begin(), copy_impl), + llvm::map_iterator(m_impls.end(), copy_impl)}; +} + +bool CXXCascadingSummaryFormat::FormatObject( + ValueObject *valobj, std::string &dest, const TypeSummaryOptions &options) { + if (!valobj) + return false; + + for (auto &[validator, impl] : m_impls) { + if (!validator || validator(*valobj)) + return std::visit( + [&](auto &impl) { + return impl.FormatObject(valobj, dest, options, *this); + }, + impl); + } + return false; +} + +std::string CXXCascadingSummaryFormat::GetDescription() { + StreamString sstr; + sstr.Printf("size=%zu %s%s%s%s%s%s%s ptr-match-depth=%u %s", m_impls.size(), + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : "", + GetPtrMatchDepth(), m_description.c_str()); + return std::string(sstr.GetString()); +} + +std::string CXXCascadingSummaryFormat::GetName() { return m_description; } + ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags, const char *function_name, const char *python_script, 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..b99c0c711ba2f 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,42 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "${var.__y_} ${var.__m_} ${var.__wdl_}"))); } +static bool IsMsvcStdStringType(ValueObject &valobj) { + std::vector<uint32_t> indexes; + return valobj.GetCompilerType().GetIndexOfChildMemberWithName("_Mypair", true, + indexes) > 0; +} + +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 +1422,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 +1429,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 +1441,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 +1635,82 @@ 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", + TypeSummaryImplSP{ + (new CXXCascadingSummaryFormat( + stl_summary_flags, + "MSVC/libstdc++ std::string summary provider")) + ->Append( + IsMsvcStdStringType, + CXXFunctionSummaryData{ + MsvcStlStringSummaryProvider<StringElementType::ASCII>}) + // libstdc++ (fallback) + ->Append(nullptr, StringSummaryData("${var._M_dataplus._M_p}")), + }); + RegisterStdStringSummaryProvider( + cpp_category_sp, "std::wstring", "wchar_t", + TypeSummaryImplSP{ + (new CXXCascadingSummaryFormat( + stl_summary_flags, + "MSVC/libstdc++ std::wstring summary provider")) + ->Append(IsMsvcStdStringType, + CXXFunctionSummaryData{MsvcStlWStringSummaryProvider}) + // libstdc++: making sure we force-pick the summary for printing + // wstring (_M_p is a wchar_t*) + ->Append(nullptr, StringSummaryData("${var._M_dataplus._M_p%S}")), + }); +} + +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 +1825,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/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