From e7e47a211ebaaa0f6380810b6573fadde12ca02d Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Mon, 2 Oct 2023 10:53:17 -0700 Subject: [PATCH 1/4] Implement data formatters for LibStdC++ std::variant --- lldb/examples/synthetic/gnu_libstdcpp.py | 89 +++++++++++++++++++ .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 18 +++- .../libstdcpp/variant/Makefile | 5 ++ .../TestDataFormatterLibStdcxxVariant.py | 72 +++++++++++++++ .../libstdcpp/variant/main.cpp | 79 ++++++++++++++++ 5 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index 825b7f3787a010..7462db74467468 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -892,3 +892,92 @@ def update(self): except: pass return False + + +def VariantSummaryProvider(valobj, dict): + raw_obj = valobj.GetNonSyntheticValue() + index_obj = raw_obj.GetChildMemberWithName("_M_index") + data_obj = raw_obj.GetChildMemberWithName("_M_u") + if not ( + index_obj + and index_obj.IsValid() + and data_obj + and data_obj.IsValid() + ): + return "" + + def get_variant_npos_value(index_byte_size): + if index_byte_size == 1: + return 0xFF + elif index_byte_size == 2: + return 0xFFFF + else: + return 0xFFFFFFFF + + npos_value = get_variant_npos_value(index_obj.GetByteSize()) + index = index_obj.GetValueAsUnsigned(0) + if index == npos_value: + return " No Value" + + active_type = data_obj.GetType().GetTemplateArgumentType(index) + return f" Active Type = {active_type.GetDisplayTypeName()} " + + +class VariantSynthProvider: + def __init__(self, valobj, dict): + self.raw_obj = valobj.GetNonSyntheticValue() + self.is_valid = False + self.index = None + self.data_obj = None + + def update(self): + try: + self.index = self.raw_obj.GetChildMemberWithName( + "_M_index" + ).GetValueAsSigned(-1) + self.is_valid = self.index != -1 + self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u") + except: + self.is_valid = False + return False + + def has_children(self): + return True + + def num_children(self): + return 1 if self.is_valid else 0 + + def get_child_index(self, name): + return 0 + + def get_child_at_index(self, index): + if not self.is_valid: + return None + cur = 0 + node = self.data_obj + while cur < self.index: + node = node.GetChildMemberWithName("_M_rest") + cur += 1 + + # _M_storage's type depends on variant field's type "_Type". + # 1. if '_Type' is literal type: _Type _M_storage. + # 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage. + # + # For 2. we have to cast it to underlying template _Type. + + value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName( + "_M_storage" + ) + template_type = value.GetType().GetTemplateArgumentType(0) + + # Literal type will return None for GetTemplateArgumentType(0) + if ( + template_type + and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName() + and template_type.IsValid() + ): + value = value.Cast(template_type) + + if value.IsValid(): + return value.Clone("Value") + return None diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index c1743a5e0a418d..d8a30729b6d02e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -332,11 +332,11 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { // If we can't parse the incoming name, then just check that it contains path. if (m_parse_error) return m_full.GetStringRef().contains(path); - + llvm::StringRef identifier; llvm::StringRef context; std::string path_str = path.str(); - bool success + bool success = CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(), context, identifier); @@ -372,7 +372,7 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { return false; if (haystack.empty() || !isalnum(haystack.back())) return true; - + return false; } @@ -388,7 +388,7 @@ bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) { return true; } -bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, +bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, ConstString demangled) const { MethodName demangled_name(demangled); return demangled_name.ContainsPath(path); @@ -1104,6 +1104,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::variant<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider"))); stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(false); @@ -1148,6 +1153,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { TypeSummaryImplSP(new ScriptSummaryFormat( stl_summary_flags, "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); + cpp_category_sp->AddTypeSummary( + "^std::variant<.+>$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSummaryProvider"))); AddCXXSynthetic( cpp_category_sp, diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile new file mode 100644 index 00000000000000..104f82809c7a35 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile @@ -0,0 +1,5 @@ +CXX_SOURCES := main.cpp + +USE_LIBSTDCPP := 1 +CXXFLAGS_EXTRAS := -std=c++17 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py new file mode 100644 index 00000000000000..88be87a5469e19 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py @@ -0,0 +1,72 @@ +""" +Test lldb data formatter for LibStdC++ std::variant. +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +USE_LIBSTDCPP = "USE_LIBSTDCPP" + + +class LibStdcxxVariantDataFormatterTestCase(TestBase): + @add_test_categories(["libstdcxx"]) + def test_with_run_command(self): + """Test LibStdC++ std::variant data formatter works correctly.""" + self.build(dictionary={USE_LIBSTDCPP: "1"}) + + (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp", False) + ) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + self.assertEqual(3 + 4, 7) + + self.expect( + "frame variable v1", + substrs=["v1 = Active Type = int {", "Value = 12", "}"], + ) + + self.expect( + "frame variable v1_ref", + substrs=["v1_ref = Active Type = int : {", "Value = 12", "}"], + ) + + self.expect( + "frame variable v_v1", + substrs=[ + "v_v1 = Active Type = std::variant {", + "Value = Active Type = int {", + "Value = 12", + "}", + "}", + ], + ) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect( + "frame variable v1", + substrs=["v1 = Active Type = double {", "Value = 2", "}"], + ) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect( + "frame variable v2", + substrs=["v2 = Active Type = double {", "Value = 2", "}"], + ) + + self.expect( + "frame variable v3", + substrs=["v3 = Active Type = char {", "Value = 'A'", "}"], + ) + + self.expect("frame variable v_no_value", substrs=["v_no_value = No Value"]) + + self.expect( + "frame variable v_many_types_no_value", + substrs=["v_many_types_no_value = No Value"], + ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp new file mode 100644 index 00000000000000..545318f9358b67 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +struct S { + operator int() { throw 42; } +}; + +int main() { + bool has_variant = true; + + printf("%d\n", has_variant); // break here + + std::variant v1; + std::variant &v1_ref = v1; + std::variant v2; + std::variant v3; + std::variant> v_v1; + std::variant v_no_value; + // The next variant has many types, meaning the type index does not fit in + // a byte and must be `unsigned short` instead of `unsigned char` when + // using the unstable libc++ ABI. With stable libc++ ABI, the type index + // is always just `unsigned int`. + std::variant< + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, + int, int, int, int, int, int, int, int, int, int, int, int> + v_many_types_no_value; + + v1 = 12; // v contains int + v_v1 = v1; + int i = std::get(v1); + printf("%d\n", i); // break here + + v2 = 2.0; + double d = std::get(v2); + printf("%f\n", d); + + v3 = 'A'; + char c = std::get(v3); + printf("%d\n", c); + + // Checking v1 above and here to make sure we done maintain the incorrect + // state when we change its value. + v1 = 2.0; + d = std::get(v1); + printf("%f\n", d); // break here + + try { + v_no_value.emplace<0>(S()); + } catch (...) { + } + + printf("%zu\n", v_no_value.index()); + + try { + v_many_types_no_value.emplace<0>(S()); + } catch (...) { + } + + printf("%zu\n", v_many_types_no_value.index()); + + return 0; // break here +} From 53d20815eadd368ff1185af520bcb4aa75c9e96e Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Mon, 2 Oct 2023 14:18:14 -0700 Subject: [PATCH 2/4] Fix formatter --- lldb/examples/synthetic/gnu_libstdcpp.py | 7 +------ .../Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index 7462db74467468..29c926167fb440 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -898,12 +898,7 @@ def VariantSummaryProvider(valobj, dict): raw_obj = valobj.GetNonSyntheticValue() index_obj = raw_obj.GetChildMemberWithName("_M_index") data_obj = raw_obj.GetChildMemberWithName("_M_u") - if not ( - index_obj - and index_obj.IsValid() - and data_obj - and data_obj.IsValid() - ): + if not (index_obj and index_obj.IsValid() and data_obj and data_obj.IsValid()): return "" def get_variant_npos_value(index_byte_size): diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index d8a30729b6d02e..a285864ca2e122 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1105,10 +1105,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::variant<.+>$", eFormatterMatchRegex, - SyntheticChildrenSP(new ScriptedSyntheticChildren( - stl_synth_flags, - "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider"))); + "^std::variant<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider"))); stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(false); From eebb06c9b893798a1f2b22502bf32dad48b644eb Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Mon, 2 Oct 2023 14:31:36 -0700 Subject: [PATCH 3/4] Fix formatter again --- .../source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index a285864ca2e122..ad6d627938c052 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -336,10 +336,8 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { llvm::StringRef identifier; llvm::StringRef context; std::string path_str = path.str(); - bool success - = CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(), - context, - identifier); + bool success = CPlusPlusLanguage::ExtractContextAndIdentifier( + path_str.c_str(), context, identifier); if (!success) return m_full.GetStringRef().contains(path); From a5e34979045b5c612687f8b11367a863a12e5eef Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Mon, 2 Oct 2023 17:45:05 -0700 Subject: [PATCH 4/4] Remove unnecessary test code --- .../libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py index 88be87a5469e19..7a433fea5feca2 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py @@ -8,21 +8,18 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -USE_LIBSTDCPP = "USE_LIBSTDCPP" - class LibStdcxxVariantDataFormatterTestCase(TestBase): @add_test_categories(["libstdcxx"]) def test_with_run_command(self): """Test LibStdC++ std::variant data formatter works correctly.""" - self.build(dictionary={USE_LIBSTDCPP: "1"}) + self.build() (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "// break here", lldb.SBFileSpec("main.cpp", False) ) lldbutil.continue_to_breakpoint(self.process, bkpt) - self.assertEqual(3 + 4, 7) self.expect( "frame variable v1",