Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py @@ -0,0 +1,53 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestDataFormatterLibcxxForwardList(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.line = line_number('main.cpp', '// break here') + ns = 'ndk' if lldbplatformutil.target_is_android() else '' + self.namespace = 'std::__' + ns + '1' + + @add_test_categories(["libc++"]) + def test(self): + """Test that std::forward_list is displayed correctly""" + self.build() + lldbutil.run_to_source_breakpoint(self, '// break here', + lldb.SBFileSpec("main.cpp", False)) + + forward_list = self.namespace + '::forward_list' + self.expect("frame variable empty", + substrs=[forward_list, + 'size=0', + '{}']) + + self.expect("frame variable one_elt", + substrs=[forward_list, + 'size=1', + '{', + '[0] = 47', + '}']) + + self.expect("frame variable five_elts", + substrs=[forward_list, + 'size=5', + '{', + '[0] = 1', + '[1] = 22', + '[2] = 333', + '[3] = 4444', + '[4] = 55555', + '}']) Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp @@ -0,0 +1,7 @@ +#include + +int main() +{ + std::forward_list empty{}, one_elt{47}, five_elts{1,22,333,4444,55555}; + return 0; // break here +} Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -426,6 +426,11 @@ "libc++ std::vector synthetic children", ConstString("^std::__(ndk)?1::vector<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator, + "libc++ std::forward_list synthetic children", + ConstString("^std::__(ndk)?1::forward_list<.+>(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic( cpp_category_sp, lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, @@ -500,6 +505,11 @@ "libc++ std::vector summary provider", ConstString("^std::__(ndk)?1::vector<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + ConstString("^std::__(ndk)?1::forward_list<.+>(( )?&)?$"), + stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::list summary provider", Index: source/Plugins/Language/CPlusPlus/LibCxx.h =================================================================== --- source/Plugins/Language/CPlusPlus/LibCxx.h +++ source/Plugins/Language/CPlusPlus/LibCxx.h @@ -104,6 +104,10 @@ LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd * +LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); Index: source/Plugins/Language/CPlusPlus/LibCxxList.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -114,11 +114,45 @@ ListEntry m_entry; }; -} // end anonymous namespace +class AbstractListFrontEnd: public SyntheticChildrenFrontEnd { +public: + size_t GetIndexOfChildWithName(const ConstString &name) override { + return ExtractIndexFromString(name.GetCString()); + } + bool MightHaveChildren() override { return true; } + bool Update() override; + +protected: + AbstractListFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) {} + + size_t m_count; + ValueObject *m_head; + + static constexpr bool g_use_loop_detect = true; + size_t m_loop_detected; // The number of elements that have had loop detection + // run over them. + ListEntry m_slow_runner; // Used for loop detection + ListEntry m_fast_runner; // Used for loop detection + + bool HasLoop(size_t count); +}; -namespace lldb_private { -namespace formatters { -class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +class ForwardListFrontEnd: public AbstractListFrontEnd { +public: + ForwardListFrontEnd(ValueObject &valobj); + + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + +private: + size_t m_list_capping_size; + CompilerType m_element_type; + std::map m_iterators; +}; + +class LibcxxStdListSyntheticFrontEnd : public AbstractListFrontEnd { public: LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); @@ -130,42 +164,25 @@ bool Update() override; - bool MightHaveChildren() override; - - size_t GetIndexOfChildWithName(const ConstString &name) override; - private: - bool HasLoop(size_t count); - size_t m_list_capping_size; - static const bool g_use_loop_detect = true; - - size_t m_loop_detected; // The number of elements that have had loop detection - // run over them. - ListEntry m_slow_runner; // Used for loop detection - ListEntry m_fast_runner; // Used for loop detection lldb::addr_t m_node_address; - ValueObject *m_head; ValueObject *m_tail; CompilerType m_element_type; - size_t m_count; std::map m_iterators; }; -} // namespace formatters -} // namespace lldb_private - -lldb_private::formatters::LibcxxStdListSyntheticFrontEnd:: - LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) - : SyntheticChildrenFrontEnd(*valobj_sp), m_list_capping_size(0), - m_loop_detected(0), m_node_address(), m_head(nullptr), m_tail(nullptr), - m_element_type(), m_count(UINT32_MAX), m_iterators() { - if (valobj_sp) - Update(); + +} // end anonymous namespace + +bool AbstractListFrontEnd::Update() { + m_loop_detected = 0; + m_count = UINT32_MAX; + m_head = nullptr; + return false; } -bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop( - size_t count) { +bool AbstractListFrontEnd::HasLoop(size_t count) { if (!g_use_loop_detect) return false; // don't bother checking for a loop if we won't actually need to jump nodes @@ -201,8 +218,117 @@ return m_slow_runner == m_fast_runner; } -size_t lldb_private::formatters::LibcxxStdListSyntheticFrontEnd:: - CalculateNumChildren() { +ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj) + : AbstractListFrontEnd(valobj) { + Update(); +} + +size_t ForwardListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + ListEntry current(m_head); + m_count = 0; + while (current && m_count < m_list_capping_size) { + ++m_count; + current = current.next(); + } + return m_count; +} + +ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= CalculateNumChildren()) + return nullptr; + + if (!m_head) + return nullptr; + + if (HasLoop(idx + 1)) + return nullptr; + + size_t actual_advance = idx; + + ListIterator current(m_head); + if (idx > 0) { + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + current = cached_iterator->second; + actual_advance = 1; + } + } + + ValueObjectSP current_sp(current.advance(actual_advance)); + if (!current_sp) + return nullptr; + + m_iterators[idx] = current; + + current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child + if (!current_sp) + return nullptr; + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return nullptr; + + return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +bool ForwardListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + + m_list_capping_size = 0; + + if (m_backend.GetTargetSP()) + m_list_capping_size = + m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return false; + + /*m_node_address = backend_addr->GetValueAsUnsigned(0); + if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) + return false;*/ + + CompilerType list_type = m_backend.GetCompilerType(); + if (list_type.IsReferenceType()) + list_type = list_type.GetNonReferenceType(); + + if (list_type.GetNumTemplateArguments() == 0) + return false; + TemplateArgumentKind kind; + m_element_type = list_type.GetTemplateArgument(0, kind); + + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__before_begin_"), true)); + if (!impl_sp) + return false; + impl_sp = impl_sp->GetChildMemberWithName(ConstString("__first_"), true); + if (!impl_sp) + return false; + m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); + return false; +} + +LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp), m_list_capping_size(0), + m_node_address(), m_tail(nullptr), m_element_type(), m_iterators() { + if (valobj_sp) + Update(); +} + +size_t LibcxxStdListSyntheticFrontEnd::CalculateNumChildren() { if (m_count != UINT32_MAX) return m_count; if (!m_head || !m_tail || m_node_address == 0) @@ -240,8 +366,7 @@ } lldb::ValueObjectSP -lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex( - size_t idx) { +LibcxxStdListSyntheticFrontEnd::GetChildAtIndex(size_t idx) { static ConstString g_value("__value_"); static ConstString g_next("__next_"); @@ -303,7 +428,7 @@ m_element_type); } -bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() { +bool LibcxxStdListSyntheticFrontEnd::Update() { m_iterators.clear(); m_head = m_tail = nullptr; m_node_address = 0; @@ -342,18 +467,13 @@ return false; } -bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd:: - MightHaveChildren() { - return true; -} - -size_t lldb_private::formatters::LibcxxStdListSyntheticFrontEnd:: - GetIndexOfChildWithName(const ConstString &name) { - return ExtractIndexFromString(name.GetCString()); +SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxStdListSyntheticFrontEnd(valobj_sp) : nullptr); } SyntheticChildrenFrontEnd * -lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator( +formatters::LibcxxStdForwardListSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { - return (valobj_sp ? new LibcxxStdListSyntheticFrontEnd(valobj_sp) : nullptr); + return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr; }