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 @@ -6,6 +6,7 @@ LibCxx.cpp LibCxxAtomic.cpp LibCxxBitset.cpp + LibStdcppBitset.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 @@ -311,7 +311,7 @@ public: void reset() { Alloc.Reset(); } - template T *makeNode(Args &&... args) { + template T *makeNode(Args &&...args) { return new (Alloc.Allocate(sizeof(T), alignof(T))) T(std::forward(args)...); } @@ -332,13 +332,12 @@ public: ManglingSubstitutor() : Base(nullptr, nullptr) {} - template - ConstString substitute(llvm::StringRef Mangled, Ts &&... Vals) { + template + ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) { this->getDerived().reset(Mangled, std::forward(Vals)...); return substituteImpl(Mangled); } - protected: void reset(llvm::StringRef Mangled) { Base::reset(Mangled.begin(), Mangled.end()); @@ -392,7 +391,6 @@ llvm::StringRef(Written, std::distance(Written, currentParserPos())); Written = currentParserPos(); } - }; /// Given a mangled function `Mangled`, replace all the primitive function type @@ -566,26 +564,23 @@ AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF16, "std::u16string summary provider", - ConstString( - "^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), + ConstString("^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$"), stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF32, "std::u32string summary provider", - ConstString( - "^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), + ConstString("^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$"), stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, - lldb_private::formatters::LibcxxWStringSummaryProvider, - "std::wstring summary provider", - ConstString("^std::__[[:alnum:]]+::wstring$"), - stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + ConstString("^std::__[[:alnum:]]+::wstring$"), stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider", @@ -782,11 +777,11 @@ "libc++ std::tuple summary provider", ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"), stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::LibCxxAtomicSummaryProvider, - "libc++ std::atomic summary provider", - ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_summary_flags, - true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibCxxAtomicSummaryProvider, + "libc++ std::atomic summary provider", + ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), + stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxOptionalSummaryProvider, "libc++ std::optional summary provider", @@ -873,7 +868,8 @@ "std::allocator >"), cxx11_string_summary_sp); cpp_category_sp->GetTypeSummariesContainer()->Add( - ConstString("std::__cxx11::basic_string, " + ConstString("std::__cxx11::basic_string, " "std::allocator >"), cxx11_string_summary_sp); @@ -969,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_synth_flags, true); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, "libstdc++ std::unique_ptr summary provider", @@ -1177,9 +1179,8 @@ llvm::call_once(g_initialize, []() -> void { g_formatters.push_back([](lldb_private::ValueObject &valobj, - lldb::DynamicValueType, - FormatManager & - fmt_mgr) -> SyntheticChildren::SharedPointer { + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { static CXXSyntheticChildren::SharedPointer formatter_sp( new CXXSyntheticChildren( SyntheticChildren::Flags() @@ -1196,9 +1197,8 @@ return nullptr; }); g_formatters.push_back([](lldb_private::ValueObject &valobj, - lldb::DynamicValueType, - FormatManager & - fmt_mgr) -> SyntheticChildren::SharedPointer { + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { static CXXSyntheticChildren::SharedPointer formatter_sp( new CXXSyntheticChildren( SyntheticChildren::Flags() @@ -1213,7 +1213,6 @@ } return nullptr; }); - }); return g_formatters; 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,11 @@ LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppBitset.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppBitset.cpp @@ -0,0 +1,121 @@ +//===-- LibCStdcppBitset.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 "LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class BitsetcppFrontEnd : public SyntheticChildrenFrontEnd { +public: + BitsetcppFrontEnd(ValueObject &valobj); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + bool Update() override; + size_t CalculateNumChildren() override { return m_elements.size(); } + ValueObjectSP GetChildAtIndex(size_t idx) override; + +private: + // 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 + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + // 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; + CompilerType m_bool_type; + ByteOrder m_byte_order = eByteOrderInvalid; + uint8_t m_byte_size = 0; +}; +} // namespace + +BitsetcppFrontEnd::BitsetcppFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) { + m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool); + if (auto target_sp = m_backend.GetTargetSP()) { + m_byte_order = target_sp->GetArchitecture().GetByteOrder(); + m_byte_size = target_sp->GetArchitecture().GetAddressByteSize(); + Update(); + } +} + +bool BitsetcppFrontEnd::Update() { + m_elements.clear(); + m_first = nullptr; + + TargetSP target_sp = m_backend.GetTargetSP(); + if (!target_sp) + return false; + 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("_M_w"), true).get(); + return false; +} + +ValueObjectSP BitsetcppFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_elements.size() || !m_first) + return ValueObjectSP(); + + if (m_elements[idx]) + return m_elements[idx]; + + ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false); + CompilerType type; + ValueObjectSP chunk; + // For small bitsets __first_ is not an array, but a plain size_t. + if (m_first->GetCompilerType().IsArrayType(&type)) { + llvm::Optional bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + chunk = m_first->GetChildAtIndex(idx / *bit_size, true); + } else { + type = m_first->GetCompilerType(); + chunk = m_first->GetSP(); + } + if (!type || !chunk) + return {}; + + llvm::Optional bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + size_t chunk_idx = idx % *bit_size; + uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx)); + DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size); + + m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), + data, ctx, m_bool_type); + + + return m_elements[idx]; +} + +SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new BitsetcppFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/Makefile @@ -0,0 +1,5 @@ +CXX_SOURCES := main.cpp + +USE_LIBSTDCPP := 1 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/TestDataFormatterStdBitset.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/TestDataFormatterStdBitset.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/TestDataFormatterStdBitset.py @@ -0,0 +1,232 @@ +""" +Test lldb data formatter subsystem. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class StdBitsetDataFormatterTestCase(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(["libstdcxx"]) + 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", 70) + + @add_test_categories(["libstdcxx"]) + 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) + 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']) \ No newline at end of file diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/bitset/main.cpp @@ -0,0 +1,29 @@ +#include +#include + +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) + b[j] = false; + } +} + +template +void by_ref_and_ptr(std::bitset &ref, std::bitset *ptr) { + // Check ref and ptr + return; +} + +int main() { + std::bitset<0> empty; + std::bitset<13> small; + fill(small); + std::bitset<70> large; + fill(large); + by_ref_and_ptr(small, &small); // break here + by_ref_and_ptr(large, &large); + return 0; +}