Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile +++ lldb/trunk/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: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py +++ lldb/trunk/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: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp +++ lldb/trunk/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: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -428,6 +428,12 @@ 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, "libc++ std::list synthetic children", ConstString("^std::__(ndk)?1::list<.+>(( )?&)?$"), stl_synth_flags, true); @@ -503,6 +509,11 @@ 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", ConstString("^std::__(ndk)?1::list<.+>(( )?&)?$"), stl_summary_flags, true); AddCXXSummary(cpp_category_sp, Index: lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxx.h =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxx.h +++ lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -105,6 +105,10 @@ lldb::ValueObjectSP); SyntheticChildrenFrontEnd * +LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); Index: lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxxList.cpp =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ lldb/trunk/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -114,58 +114,91 @@ ListEntry m_entry; }; -} // end anonymous namespace - -namespace lldb_private { -namespace formatters { -class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { public: - LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + size_t GetIndexOfChildWithName(const ConstString &name) override { + return ExtractIndexFromString(name.GetCString()); + } + bool MightHaveChildren() override { return true; } + bool Update() override; - ~LibcxxStdListSyntheticFrontEnd() override = default; +protected: + AbstractListFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) {} - size_t CalculateNumChildren() override; + size_t m_count; + ValueObject *m_head; - lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + 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 + + size_t m_list_capping_size; + CompilerType m_element_type; + std::map m_iterators; + + bool HasLoop(size_t count); + ValueObjectSP GetItem(size_t idx); +}; +class ForwardListFrontEnd : public AbstractListFrontEnd { +public: + ForwardListFrontEnd(ValueObject &valobj); + + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; bool Update() override; +}; - bool MightHaveChildren() override; +class ListFrontEnd : public AbstractListFrontEnd { +public: + ListFrontEnd(lldb::ValueObjectSP valobj_sp); - size_t GetIndexOfChildWithName(const ConstString &name) override; + ~ListFrontEnd() override = default; -private: - bool HasLoop(size_t count); + size_t CalculateNumChildren() override; - size_t m_list_capping_size; - static const bool g_use_loop_detect = true; + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; - 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 Update() override; +private: 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; + m_list_capping_size = 0; + m_slow_runner.SetEntry(nullptr); + m_fast_runner.SetEntry(nullptr); + m_iterators.clear(); + + if (m_backend.GetTargetSP()) + m_list_capping_size = + m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + + 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); + + 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 +234,96 @@ return m_slow_runner == m_fast_runner; } -size_t lldb_private::formatters::LibcxxStdListSyntheticFrontEnd:: - CalculateNumChildren() { +ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { + size_t 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; + advance = 1; + } + } + ValueObjectSP value_sp = current.advance(advance); + m_iterators[idx] = current; + return value_sp; +} + +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; + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return nullptr; + + 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(); + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return false; + + 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; +} + +ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp), m_node_address(), m_tail(nullptr) { + if (valobj_sp) + Update(); +} + +size_t ListFrontEnd::CalculateNumChildren() { if (m_count != UINT32_MAX) return m_count; if (!m_head || !m_tail || m_node_address == 0) @@ -239,9 +360,7 @@ } } -lldb::ValueObjectSP -lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex( - size_t idx) { +lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(size_t idx) { static ConstString g_value("__value_"); static ConstString g_next("__next_"); @@ -254,23 +373,10 @@ if (HasLoop(idx + 1)) return lldb::ValueObjectSP(); - 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)); + ValueObjectSP current_sp = GetItem(idx); if (!current_sp) return lldb::ValueObjectSP(); - m_iterators[idx] = current; - current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child if (!current_sp) return lldb::ValueObjectSP(); @@ -303,23 +409,13 @@ m_element_type); } -bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() { - m_iterators.clear(); - m_head = m_tail = nullptr; +bool ListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + m_tail = nullptr; m_node_address = 0; - m_count = UINT32_MAX; - m_loop_detected = 0; - m_slow_runner.SetEntry(nullptr); - m_fast_runner.SetEntry(nullptr); Status err; ValueObjectSP backend_addr(m_backend.AddressOf(err)); - 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; if (err.Fail() || !backend_addr) return false; m_node_address = backend_addr->GetValueAsUnsigned(0); @@ -329,31 +425,18 @@ m_backend.GetChildMemberWithName(ConstString("__end_"), true)); if (!impl_sp) return false; - CompilerType list_type = m_backend.GetCompilerType(); - if (list_type.IsReferenceType()) - list_type = list_type.GetNonReferenceType(); - - if (list_type.GetNumTemplateArguments() == 0) - return false; - lldb::TemplateArgumentKind kind; - m_element_type = list_type.GetTemplateArgument(0, kind); m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get(); 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 ListFrontEnd(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; }