[Lldb-commits] [lldb] [LLDB] WIP: Add type summaries for MSVC STL strings (PR #143177)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Jun 14 12:51:47 PDT 2025
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/143177
>From d953276e4c1b4d48d233c12f535b6e1330886497 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at 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 | 141 +++++++++++++----
.../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 +
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 146 +++++++++++++----
.../Language/CPlusPlus/CxxStringTypes.cpp | 121 ++++++++++----
.../Language/CPlusPlus/CxxStringTypes.h | 16 ++
.../Plugins/Language/CPlusPlus/LibCxx.cpp | 147 ++++--------------
.../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 | 118 ++++++++++++++
.../msvcstl/string/main.cpp | 40 +++++
.../msvcstl/u8string/Makefile | 4 +
.../u8string/TestDataFormatterStdU8String.py | 31 ++++
.../msvcstl/u8string/main.cpp | 14 ++
23 files changed, 899 insertions(+), 228 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..c4437f6f24561 100644
--- a/lldb/source/DataFormatters/TypeSummary.cpp
+++ b/lldb/source/DataFormatters/TypeSummary.cpp
@@ -57,21 +57,17 @@ 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 *format_cstr) {
m_format.Clear();
if (format_cstr && format_cstr[0]) {
m_format_str = format_cstr;
@@ -82,8 +78,19 @@ void StringSummaryFormat::SetSummaryString(const char *format_cstr) {
}
}
-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..b26a7661d5022 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(
@@ -116,15 +124,7 @@ bool lldb_private::formatters::WCharStringSummaryProvider(
return false;
// Get a wchar_t basic type from the current type system
- CompilerType wchar_compiler_type =
- valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
-
- if (!wchar_compiler_type)
- return false;
-
- // Safe to pass nullptr for exe_scope here.
- std::optional<uint64_t> size =
- llvm::expectedToOptional(wchar_compiler_type.GetBitSize(nullptr));
+ std::optional<uint64_t> size = GetWCharByteSize(*valobj.GetTargetSP());
if (!size)
return false;
const uint32_t wchar_size = *size;
@@ -136,13 +136,13 @@ bool lldb_private::formatters::WCharStringSummaryProvider(
options.SetPrefixToken("L");
switch (wchar_size) {
- case 8:
+ case 1:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>(
options);
- case 16:
+ case 2:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>(
options);
- case 32:
+ case 4:
return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>(
options);
default:
@@ -177,15 +177,7 @@ bool lldb_private::formatters::WCharSummaryProvider(
return false;
// Get a wchar_t basic type from the current type system
- CompilerType wchar_compiler_type =
- valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
-
- if (!wchar_compiler_type)
- return false;
-
- // Safe to pass nullptr for exe_scope here.
- std::optional<uint64_t> size =
- llvm::expectedToOptional(wchar_compiler_type.GetBitSize(nullptr));
+ std::optional<uint64_t> size = GetWCharByteSize(*valobj.GetTargetSP());
if (!size)
return false;
const uint32_t wchar_size = *size;
@@ -199,13 +191,13 @@ bool lldb_private::formatters::WCharSummaryProvider(
options.SetBinaryZeroIsTerminator(false);
switch (wchar_size) {
- case 8:
+ case 1:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>(
options);
- case 16:
+ case 2:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>(
options);
- case 32:
+ case 4:
return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>(
options);
default:
@@ -214,3 +206,76 @@ bool lldb_private::formatters::WCharSummaryProvider(
}
return true;
}
+
+std::optional<uint64_t>
+lldb_private::formatters::GetWCharByteSize(Target &target) {
+ TypeSystemClangSP scratch_ts_sp =
+ ScratchTypeSystemClang::GetForTarget(target);
+ if (!scratch_ts_sp)
+ return {};
+
+ return llvm::expectedToOptional(
+ scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr));
+}
+
+template <StringPrinter::StringElementType element_type>
+bool lldb_private::formatters::StringBufferSummaryProvider(
+ Stream &stream, const TypeSummaryOptions &summary_options,
+ lldb::ValueObjectSP location_sp, uint64_t size, std::string prefix_token) {
+
+ if (size == 0) {
+ stream.PutCString(prefix_token);
+ stream.PutCString("\"\"");
+ return true;
+ }
+
+ if (!location_sp)
+ return false;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(*location_sp);
+
+ if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
+ const auto max_size =
+ location_sp->GetTargetSP()->GetMaximumSizeOfStringSummary();
+ if (size > max_size) {
+ size = max_size;
+ options.SetIsTruncated(true);
+ }
+ }
+
+ {
+ DataExtractor extractor;
+ const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
+ if (bytes_read < size)
+ return false;
+
+ options.SetData(std::move(extractor));
+ }
+ options.SetStream(&stream);
+ if (prefix_token.empty())
+ options.SetPrefixToken(nullptr);
+ else
+ options.SetPrefixToken(prefix_token);
+ options.SetQuote('"');
+ options.SetSourceSize(size);
+ options.SetBinaryZeroIsTerminator(false);
+ return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
+}
+
+// explicit instantiations for all string element types
+template bool
+lldb_private::formatters::StringBufferSummaryProvider<StringElementType::ASCII>(
+ Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
+ std::string);
+template bool
+lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF8>(
+ Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
+ std::string);
+template bool
+lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF16>(
+ Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
+ std::string);
+template bool
+lldb_private::formatters::StringBufferSummaryProvider<StringElementType::UTF32>(
+ Stream &, const TypeSummaryOptions &, lldb::ValueObjectSP, uint64_t,
+ std::string);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
index a2b606d28cac1..787d4b870f41f 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,17 @@ bool Char32SummaryProvider(ValueObject &valobj, Stream &stream,
bool WCharSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options); // wchar_t
+std::optional<uint64_t> GetWCharByteSize(Target &target);
+
+/// Print a summary for a string buffer to \a stream.
+///
+/// The buffer consists of a data pointer, \a location_sp, and a known \a size.
+template <StringPrinter::StringElementType element_type>
+bool StringBufferSummaryProvider(Stream &stream,
+ const TypeSummaryOptions &summary_options,
+ lldb::ValueObjectSP location_sp, uint64_t size,
+ std::string prefix_token);
+
} // namespace formatters
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index 358cf7d78fa21..4fe6626a3cf8a 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,22 @@ bool lldb_private::formatters::LibcxxWStringSummaryProvider(
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
- return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
- location_sp, size);
-}
-
-template <StringPrinter::StringElementType element_type>
-static bool
-LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
- const TypeSummaryOptions &summary_options,
- std::string prefix_token, ValueObjectSP location_sp,
- uint64_t size) {
-
- if (size == 0) {
- stream.Printf("\"\"");
- return true;
- }
-
- if (!location_sp)
+ auto wchar_t_size = GetWCharByteSize(*valobj.GetTargetSP());
+ if (!wchar_t_size)
return false;
- StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
-
- if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
- const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
- if (size > max_size) {
- size = max_size;
- options.SetIsTruncated(true);
- }
- }
-
- {
- DataExtractor extractor;
- const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size);
- if (bytes_read < size)
- return false;
-
- options.SetData(std::move(extractor));
+ switch (*wchar_t_size) {
+ case 1:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
+ stream, summary_options, location_sp, size, "L");
+ case 2:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
+ stream, summary_options, location_sp, size, "L");
+ case 4:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
+ stream, summary_options, location_sp, size, "L");
}
- options.SetStream(&stream);
- if (prefix_token.empty())
- options.SetPrefixToken(nullptr);
- else
- options.SetPrefixToken(prefix_token);
- options.SetQuote('"');
- options.SetSourceSize(size);
- options.SetBinaryZeroIsTerminator(false);
- return StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
+ return false;
}
template <StringPrinter::StringElementType element_type>
@@ -669,8 +576,8 @@ LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
- return LibcxxStringSummaryProvider<element_type>(
- valobj, stream, summary_options, prefix_token, location_sp, size);
+ return StringBufferSummaryProvider<element_type>(
+ stream, summary_options, location_sp, size, prefix_token);
}
template <StringPrinter::StringElementType element_type>
static bool formatStringImpl(ValueObject &valobj, Stream &stream,
@@ -742,8 +649,8 @@ static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
return true;
}
- return LibcxxStringSummaryProvider<element_type>(
- valobj, stream, summary_options, prefix_token, dataobj, size);
+ return StringBufferSummaryProvider<element_type>(stream, summary_options,
+ dataobj, size, prefix_token);
}
bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
@@ -781,8 +688,22 @@ bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
return true;
}
- return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options,
- dataobj, size);
+ auto wchar_t_size = GetWCharByteSize(*valobj.GetTargetSP());
+ if (!wchar_t_size)
+ return false;
+
+ switch (*wchar_t_size) {
+ case 1:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
+ stream, summary_options, dataobj, size, "L");
+ case 2:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
+ stream, summary_options, dataobj, size, "L");
+ case 4:
+ return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
+ stream, summary_options, dataobj, size, "L");
+ }
+ return false;
}
static bool
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.cpp
new file mode 100644
index 0000000000000..8a1932cc0475f
--- /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 StringBufferSummaryProvider<element_type>(
+ stream, summary_options, location_sp, size, prefix_token);
+}
+template <StringPrinter::StringElementType element_type>
+static bool formatStringImpl(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options,
+ std::string prefix_token) {
+ StreamString scratch_stream;
+ const bool success = MsvcStlStringSummaryProviderImpl<element_type>(
+ valobj, scratch_stream, summary_options, prefix_token);
+ if (success)
+ stream << scratch_stream.GetData();
+ else
+ stream << "Summary Unavailable";
+ return true;
+}
+
+bool lldb_private::formatters::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..91bc5e84c8048
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/TestDataFormatterStdString.py
@@ -0,0 +1,118 @@
+# coding=utf8
+"""
+Test std::*string summaries with MSVC's STL.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class MsvcStlStringDataFormatterTestCase(TestBase):
+ @add_test_categories(["msvcstl"])
+ def test_with_run_command(self):
+ """Test that that file and class static variables display correctly."""
+ self.build()
+
+ main_spec = lldb.SBFileSpec("main.cpp")
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Set break point at this line.", main_spec
+ )
+ frame = thread.frames[0]
+
+ # This is the function to remove the custom formats in order to have a
+ # clean slate for the next test case.
+ def cleanup():
+ self.runCmd("type format clear", check=False)
+ self.runCmd("type summary clear", check=False)
+ self.runCmd("type filter clear", check=False)
+ self.runCmd("type synth clear", check=False)
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ self.expect(
+ "frame variable",
+ substrs=[
+ '(std::wstring) wempty = L""',
+ '(std::wstring) s = L"hello world! מזל טוב!"',
+ '(std::wstring) S = L"!!!!"',
+ "(const wchar_t *) mazeltov = 0x",
+ 'L"מזל טוב"',
+ '(std::string) empty = ""',
+ '(std::string) q = "hello world"',
+ '(std::string) Q = "quite a long std::strin with lots of info inside it"',
+ '(std::string) overwritten_zero = "abc"',
+ '(std::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"',
+ '(std::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"',
+ '(std::u16string) u16_string = u"ß水氶"',
+ '(std::u16string) u16_empty = u""',
+ '(std::u32string) u32_string = U"🍄🍅🍆🍌"',
+ '(std::u32string) u32_empty = U""',
+ "(std::string *) null_str = nullptr",
+ ],
+ )
+
+ thread.StepOver()
+
+ TheVeryLongOne = frame.FindVariable("TheVeryLongOne")
+ summaryOptions = lldb.SBTypeSummaryOptions()
+ summaryOptions.SetCapping(lldb.eTypeSummaryUncapped)
+ uncappedSummaryStream = lldb.SBStream()
+ TheVeryLongOne.GetSummary(uncappedSummaryStream, summaryOptions)
+ uncappedSummary = uncappedSummaryStream.GetData()
+ self.assertGreater(
+ uncappedSummary.find("someText"),
+ 0,
+ "uncappedSummary does not include the full string",
+ )
+ summaryOptions.SetCapping(lldb.eTypeSummaryCapped)
+ cappedSummaryStream = lldb.SBStream()
+ TheVeryLongOne.GetSummary(cappedSummaryStream, summaryOptions)
+ cappedSummary = cappedSummaryStream.GetData()
+ self.assertLessEqual(
+ cappedSummary.find("someText"), 0, "cappedSummary includes the full string"
+ )
+
+ self.expect_expr(
+ "s", result_type="std::wstring", result_summary='L"hello world! מזל טוב!"'
+ )
+
+ self.expect_expr("q", result_type="std::string", result_summary='"hello world"')
+
+ self.expect_expr(
+ "Q",
+ result_type="std::string",
+ result_summary='"quite a long std::strin with lots of info inside it"',
+ )
+
+ self.expect(
+ "frame variable",
+ substrs=[
+ '(std::wstring) S = L"!!!!!"',
+ "(const wchar_t *) mazeltov = 0x",
+ 'L"מזל טוב"',
+ '(std::string) q = "hello world"',
+ '(std::string) Q = "quite a long std::strin with lots of info inside it"',
+ '(std::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"',
+ '(std::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"',
+ '(std::u16string) u16_string = u"ß水氶"',
+ '(std::u32string) u32_string = U"🍄🍅🍆🍌"',
+ '(std::u32string) u32_empty = U""',
+ "(std::string *) null_str = nullptr",
+ ],
+ )
+
+ # Finally, make sure that if the string is not readable, we give an error:
+ bkpt_2 = target.BreakpointCreateBySourceRegex(
+ "Break here to look at bad string", main_spec
+ )
+ self.assertEqual(bkpt_2.GetNumLocations(), 1, "Got one location")
+ threads = lldbutil.continue_to_breakpoint(process, bkpt_2)
+ self.assertEqual(len(threads), 1, "Stopped at second breakpoint")
+ frame = threads[0].frames[0]
+ var = frame.FindVariable("in_str")
+ self.assertTrue(var.GetError().Success(), "Made variable")
+ self.assertIsNone(var.GetSummary())
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp
new file mode 100644
index 0000000000000..fcfb5d48e9bb1
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/string/main.cpp
@@ -0,0 +1,40 @@
+#include <stdint.h>
+
+#include <string>
+
+#ifndef _MSVC_STL_VERSION
+// this is more of a sanity check that the categories work as expected
+#error Not using MSVC STL
+#endif
+
+static size_t touch_string(std::string &in_str) {
+ return in_str.size(); // Break here to look at bad string
+}
+
+int main() {
+ std::wstring wempty(L"");
+ std::wstring s(L"hello world! מזל טוב!");
+ std::wstring S(L"!!!!");
+ const wchar_t *mazeltov = L"מזל טוב";
+ std::string empty("");
+ std::string q("hello world");
+ std::string Q("quite a long std::strin with lots of info inside it");
+ std::string overwritten_zero("abc");
+ const_cast<char *>(overwritten_zero.data())[3] = 'd';
+ // clang-format off
+ std::string TheVeryLongOne("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890someText1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
+ // clang-format on
+ std::string IHaveEmbeddedZeros("a\0b\0c\0d", 7);
+ std::wstring IHaveEmbeddedZerosToo(
+ L"hello world!\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監", 38);
+ std::u16string u16_string(u"ß水氶");
+ std::u16string u16_empty(u"");
+ std::u32string u32_string(U"🍄🍅🍆🍌");
+ std::u32string u32_empty(U"");
+ std::string *null_str = nullptr;
+
+ S.assign(L"!!!!!"); // Set break point at this line.
+ std::string *not_a_string = (std::string *)0x0;
+ touch_string(*not_a_string);
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile
new file mode 100644
index 0000000000000..58558e6e15f78
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+
+CXXFLAGS_EXTRAS := -std=c++20 -O0
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py
new file mode 100644
index 0000000000000..9864cc1bae8ac
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/TestDataFormatterStdU8String.py
@@ -0,0 +1,31 @@
+# coding=utf8
+"""
+Test std::u8string summary with MSVC's STL.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class MsvcStlU8StringDataFormatterTestCase(TestBase):
+ @add_test_categories(["msvcstl"])
+ def test_with_run_command(self):
+ """Test that that file and class static variables display correctly."""
+ self.build()
+
+ lldbutil.run_to_source_breakpoint(
+ self, "Set break point at this line.", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect(
+ "frame variable",
+ substrs=[
+ '(std::u8string) u8_string_small = u8"🍄"',
+ '(std::u8string) u8_string = u8"❤️👍📄📁😃🧑🌾"',
+ '(std::u8string) u8_empty = u8""',
+ '(std::u8string) u8_text = u8"ABC"',
+ ],
+ )
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp
new file mode 100644
index 0000000000000..e01af9fa08e7e
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvcstl/u8string/main.cpp
@@ -0,0 +1,14 @@
+#include <string>
+
+#ifndef _MSVC_STL_VERSION
+// this is more of a sanity check that the categories work as expected
+#error Not using MSVC STL
+#endif
+
+int main() {
+ std::u8string u8_string_small(u8"🍄");
+ std::u8string u8_string(u8"❤️👍📄📁😃🧑🌾");
+ std::u8string u8_empty(u8"");
+ std::u8string u8_text(u8"ABC");
+ u8_text.assign(u8"ABCd"); // Set break point at this line.
+}
More information about the lldb-commits
mailing list