diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -3,9 +3,9 @@ CPlusPlusLanguage.cpp CPlusPlusNameParser.cpp CxxStringTypes.cpp + GenericBitset.cpp LibCxx.cpp LibCxxAtomic.cpp - LibCxxBitset.cpp LibCxxInitializerList.cpp LibCxxList.cpp LibCxxMap.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -895,6 +895,8 @@ SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( false); + SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; + stl_deref_flags.SetFrontEndWantsDereference(); cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( RegularExpression("^std::vector<.+>(( )?&)?$"), @@ -913,6 +915,10 @@ "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(true); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression("^std::bitset<.+>(( )?&)?$"), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); cpp_category_sp->GetRegexTypeSummariesContainer()->Add( RegularExpression("^std::vector<.+>(( )?&)?$"), TypeSummaryImplSP( @@ -959,6 +965,12 @@ "std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator, + "std::bitset synthetic child", ConstString("^std::bitset<.+>(( )?&)?$"), + stl_deref_flags, true); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, "libstdc++ std::unique_ptr summary provider", diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp rename from lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp rename to lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -1,4 +1,4 @@ -//===-- LibCxxBitset.cpp --------------------------------------------------===// +//===-- GenericBitset.cpp //-----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "LibCxx.h" +#include "LibStdcpp.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Target/Target.h" @@ -16,9 +17,15 @@ namespace { -class BitsetFrontEnd : public SyntheticChildrenFrontEnd { +/// This class can be used for handling bitsets from both libcxx and libstdcpp. +class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { public: - BitsetFrontEnd(ValueObject &valobj); + enum class StdLib { + LibCxx, + LibStdcpp, + }; + + GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); size_t GetIndexOfChildWithName(ConstString name) override { return formatters::ExtractIndexFromString(name.GetCString()); @@ -30,6 +37,8 @@ ValueObjectSP GetChildAtIndex(size_t idx) override; private: + ConstString GetDataContainerMemberName(); + // The lifetime of a ValueObject and all its derivative ValueObjects // (children, clones, etc.) is managed by a ClusterManager. These // objects are only destroyed when every shared pointer to any of them @@ -38,15 +47,16 @@ // Value objects created from raw data (i.e. in a different cluster) must // be referenced via shared pointer to keep them alive, however. std::vector m_elements; - ValueObject* m_first = nullptr; + ValueObject *m_first = nullptr; CompilerType m_bool_type; ByteOrder m_byte_order = eByteOrderInvalid; uint8_t m_byte_size = 0; + StdLib m_stdlib; }; } // namespace -BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj) - : SyntheticChildrenFrontEnd(valobj) { +GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib) + : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool); if (auto target_sp = m_backend.GetTargetSP()) { m_byte_order = target_sp->GetArchitecture().GetByteOrder(); @@ -55,7 +65,16 @@ } } -bool BitsetFrontEnd::Update() { +ConstString GenericBitsetFrontEnd::GetDataContainerMemberName() { + switch (m_stdlib) { + case StdLib::LibCxx: + return ConstString("__first_"); + case StdLib::LibStdcpp: + return ConstString("_M_w"); + } +} + +bool GenericBitsetFrontEnd::Update() { m_elements.clear(); m_first = nullptr; @@ -65,16 +84,17 @@ size_t capping_size = target_sp->GetMaximumNumberOfChildrenToDisplay(); size_t size = 0; + if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0)) size = arg->value.getLimitedValue(capping_size); m_elements.assign(size, ValueObjectSP()); - - m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true).get(); + m_first = m_backend.GetChildMemberWithName(GetDataContainerMemberName(), true) + .get(); return false; } -ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_t idx) { +ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(size_t idx) { if (idx >= m_elements.size() || !m_first) return ValueObjectSP(); @@ -112,9 +132,18 @@ return m_elements[idx]; } +SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibStdcpp); + return nullptr; +} + SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { if (valobj_sp) - return new BitsetFrontEnd(*valobj_sp); + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibCxx); return nullptr; } diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -41,6 +41,10 @@ LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd * +LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/Makefile rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/Makefile rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/Makefile --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/Makefile +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/Makefile @@ -1,4 +1,3 @@ CXX_SOURCES := main.cpp -USE_LIBCPP := 1 include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py @@ -0,0 +1,93 @@ +""" +Test lldb data formatter subsystem for bitset for libcxx and libstdcpp. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +USE_LIBSTDCPP = "USE_LIBSTDCPP" +USE_LIBCPP = "USE_LIBCPP" +VALUE = "VALUE" +REFERENCE = "REFERENCE" +POINTER = "POINTER" + +class GenericBitsetDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + primes = [1]*300 + primes[0] = primes[1] = 0 + for i in range(2, len(primes)): + for j in range(2*i, len(primes), i): + primes[j] = 0 + self.primes = primes + + def getBitsetVariant(self, size, variant): + if variant == VALUE: + return "std::bitset<" + str(size) + ">" + elif variant == REFERENCE: + return "std::bitset<" + str(size) + "> &" + elif variant == POINTER: + return "std::bitset<" + str(size) + "> *" + return "" + + def check(self, name, size, variant): + var = self.frame().FindVariable(name) + self.assertTrue(var.IsValid()) + self.assertEqual(var.GetNumChildren(), size) + children = [] + for i in range(size): + child = var.GetChildAtIndex(i) + children.append(ValueCheck(value=str(bool(child.GetValueAsUnsigned())).lower())) + self.assertEqual(child.GetValueAsUnsigned(), self.primes[i], + "variable: %s, index: %d"%(name, size)) + self.expect_var_path(name,type=self.getBitsetVariant(size,variant),children=children) + + def do_test_value(self, stdlib_type): + """Test that std::bitset is displayed correctly""" + self.build(dictionary={stdlib_type: "1"}) + + lldbutil.run_to_source_breakpoint(self, '// break here', + lldb.SBFileSpec("main.cpp", False)) + + self.check("empty", 0, VALUE) + self.check("small", 13, VALUE) + self.check("large", 70, VALUE) + + @add_test_categories(["libstdcxx"]) + def test_value_libstdcpp(self): + self.do_test_value(USE_LIBSTDCPP) + + @add_test_categories(["libc++"]) + def test_value_libcpp(self): + self.do_test_value(USE_LIBCPP) + + def do_test_ptr_and_ref(self, stdlib_type): + """Test that ref and ptr to std::bitset is displayed correctly""" + self.build(dictionary={stdlib_type: "1"}) + + (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, + 'Check ref and ptr', + lldb.SBFileSpec("main.cpp", False)) + + self.check("ref", 13, REFERENCE) + self.check("ptr", 13, POINTER) + + lldbutil.continue_to_breakpoint(process, bkpt) + + self.check("ref", 70, REFERENCE) + self.check("ptr", 70, POINTER) + + @add_test_categories(["libstdcxx"]) + def test_ptr_and_ref_libstdcpp(self): + self.do_test_ptr_and_ref(USE_LIBSTDCPP) + + @add_test_categories(["libc++"]) + def test_ptr_and_ref_libcpp(self): + self.do_test_ptr_and_ref(USE_LIBCPP) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/main.cpp rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/main.cpp rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/main.cpp --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/main.cpp @@ -1,27 +1,26 @@ #include #include -template -void fill(std::bitset &b) { +template void fill(std::bitset &b) { b.set(); b[0] = b[1] = false; for (std::size_t i = 2; i < N; ++i) { - for (std::size_t j = 2*i; j < N; j+=i) + for (std::size_t j = 2 * i; j < N; j += i) b[j] = false; } } -template +template void by_ref_and_ptr(std::bitset &ref, std::bitset *ptr) { - // Check ref and ptr - return; + // Check ref and ptr + return; } int main() { std::bitset<0> empty; std::bitset<13> small; fill(small); - std::bitset<200> large; + std::bitset<70> large; fill(large); by_ref_and_ptr(small, &small); // break here by_ref_and_ptr(large, &large); diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py deleted file mode 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Test lldb data formatter subsystem. -""" - - - -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class TestDataFormatterLibcxxBitset(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - def setUp(self): - TestBase.setUp(self) - - primes = [1]*300 - primes[0] = primes[1] = 0 - for i in range(2, len(primes)): - for j in range(2*i, len(primes), i): - primes[j] = 0 - self.primes = primes - - def check(self, name, size): - var = self.frame().FindVariable(name) - self.assertTrue(var.IsValid()) - self.assertEqual(var.GetNumChildren(), size) - for i in range(size): - child = var.GetChildAtIndex(i) - self.assertEqual(child.GetValueAsUnsigned(), self.primes[i], - "variable: %s, index: %d"%(name, size)) - - @add_test_categories(["libc++"]) - def test_value(self): - """Test that std::bitset is displayed correctly""" - self.build() - lldbutil.run_to_source_breakpoint(self, '// break here', - lldb.SBFileSpec("main.cpp", False)) - - self.check("empty", 0) - self.check("small", 13) - self.check("large", 200) - - @add_test_categories(["libc++"]) - def test_ptr_and_ref(self): - """Test that ref and ptr to std::bitset is displayed correctly""" - self.build() - (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, - 'Check ref and ptr', - lldb.SBFileSpec("main.cpp", False)) - - self.check("ref", 13) - self.check("ptr", 13) - - lldbutil.continue_to_breakpoint(process, bkpt) - - self.check("ref", 200) - self.check("ptr", 200)