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 @@ -959,6 +959,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_synth_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,30 @@ 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()); + // We dereference the value to later get the correct size from the bitset + // template argument. + ValueObjectSP dereferenced_type = m_backend.GetSP(); + if (m_backend.GetCompilerType().IsPointerOrReferenceType()) { + Status err; + dereferenced_type = m_backend.Dereference(err); + if (err.Fail() || !dereferenced_type) + dereferenced_type.reset(); + } - m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true).get(); + if (dereferenced_type) { + if (auto arg = + dereferenced_type->GetCompilerType().GetIntegralTemplateArgument(0)) + size = arg->value.getLimitedValue(capping_size); + } + + m_elements.assign(size, ValueObjectSP()); + 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 +145,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,250 @@ +""" +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" + +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 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)) + + 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) + self.check("small", 13) + self.check("large", 70) + + @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) + self.check("ptr", 13) + self.expect("p ref", + substrs=['[0] = false', + '[1] = false', + '[2] = true', + '[3] = true', + '[4] = false', + '[5] = true', + '[6] = false', + '[7] = true', + '[8] = false', + '[9] = false', + '[10] = false', + '[11] = true', + '[12] = false']) + + self.expect("p *ptr", + substrs=['[0] = false', + '[1] = false', + '[2] = true', + '[3] = true', + '[4] = false', + '[5] = true', + '[6] = false', + '[7] = true', + '[8] = false', + '[9] = false', + '[10] = false', + '[11] = true', + '[12] = false']) + + + + lldbutil.continue_to_breakpoint(process, bkpt) + + self.expect("p ref", + substrs=['[0] = false', + '[1] = false', + '[2] = true', + '[3] = true', + '[4] = false', + '[5] = true', + '[6] = false', + '[7] = true', + '[8] = false', + '[9] = false', + '[10] = false', + '[11] = true', + '[12] = false', + '[13] = true', + '[14] = false', + '[15] = false', + '[16] = false', + '[17] = true', + '[18] = false', + '[19] = true', + '[20] = false', + '[21] = false', + '[22] = false', + '[23] = true', + '[24] = false', + '[25] = false', + '[26] = false', + '[27] = false', + '[28] = false', + '[29] = true', + '[30] = false', + '[31] = true', + '[32] = false', + '[33] = false', + '[34] = false', + '[35] = false', + '[36] = false', + '[37] = true', + '[38] = false', + '[39] = false', + '[40] = false', + '[41] = true', + '[42] = false', + '[43] = true', + '[44] = false', + '[45] = false', + '[46] = false', + '[47] = true', + '[48] = false', + '[49] = false', + '[50] = false', + '[51] = false', + '[52] = false', + '[53] = true', + '[54] = false', + '[55] = false', + '[56] = false', + '[57] = false', + '[58] = false', + '[59] = true', + '[60] = false', + '[61] = true', + '[62] = false', + '[63] = false', + '[64] = false', + '[65] = false', + '[66] = false', + '[67] = true', + '[68] = false', + '[69] = false']) + + self.expect("p *ptr", + substrs=['[0] = false', + '[1] = false', + '[2] = true', + '[3] = true', + '[4] = false', + '[5] = true', + '[6] = false', + '[7] = true', + '[8] = false', + '[9] = false', + '[10] = false', + '[11] = true', + '[12] = false', + '[13] = true', + '[14] = false', + '[15] = false', + '[16] = false', + '[17] = true', + '[18] = false', + '[19] = true', + '[20] = false', + '[21] = false', + '[22] = false', + '[23] = true', + '[24] = false', + '[25] = false', + '[26] = false', + '[27] = false', + '[28] = false', + '[29] = true', + '[30] = false', + '[31] = true', + '[32] = false', + '[33] = false', + '[34] = false', + '[35] = false', + '[36] = false', + '[37] = true', + '[38] = false', + '[39] = false', + '[40] = false', + '[41] = true', + '[42] = false', + '[43] = true', + '[44] = false', + '[45] = false', + '[46] = false', + '[47] = true', + '[48] = false', + '[49] = false', + '[50] = false', + '[51] = false', + '[52] = false', + '[53] = true', + '[54] = false', + '[55] = false', + '[56] = false', + '[57] = false', + '[58] = false', + '[59] = true', + '[60] = false', + '[61] = true', + '[62] = false', + '[63] = false', + '[64] = false', + '[65] = false', + '[66] = false', + '[67] = true', + '[68] = false', + '[69] = false']) + + @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)