diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -2,15 +2,15 @@ import re import lldb.formatters.Logger +FORWARD_LIST = "FORWARD_LIST" +LIST = "LIST" + # C++ STL formatters for LLDB # These formatters are based upon the version of the GNU libstdc++ # as it ships with Mac OS X 10.6.8 thru 10.8.0 # You are encouraged to look at the STL implementation for your platform # before relying on these formatters to do the right thing for your setup - - -class StdListSynthProvider: - +class AbstractListSynthProvider: def __init__(self, valobj, dict): logger = lldb.formatters.Logger.Logger() self.valobj = valobj @@ -18,29 +18,27 @@ logger >> "Providing synthetic children for a list named " + \ str(valobj.GetName()) - def next_node(self, node): + def next_node_abstract(self, node): logger = lldb.formatters.Logger.Logger() return node.GetChildMemberWithName('_M_next') - def is_valid(self, node): + def is_valid_abstract(self, node, break_condition): logger = lldb.formatters.Logger.Logger() - valid = self.value(self.next_node(node)) != self.node_address + valid = self.value_abstract(self.next_node_abstract(node)) != break_condition if valid: logger >> "%s is valid" % str(self.valobj.GetName()) else: logger >> "synthetic value is not valid" return valid - - def value(self, node): + + def value_abstract(self, node): logger = lldb.formatters.Logger.Logger() value = node.GetValueAsUnsigned() logger >> "synthetic value for {}: {}".format( str(self.valobj.GetName()), value) return value - # Floyd's cycle-finding algorithm - # try to detect if this list has a loop - def has_loop(self): + def has_loop_abstract(self, is_valid_break_condition): global _list_uses_loop_detector logger = lldb.formatters.Logger.Logger() if not _list_uses_loop_detector: @@ -49,17 +47,17 @@ slow = self.next fast1 = self.next fast2 = self.next - while self.is_valid(slow): - slow_value = self.value(slow) - fast1 = self.next_node(fast2) - fast2 = self.next_node(fast1) - if self.value(fast1) == slow_value or self.value( + while self.is_valid_abstract(slow, is_valid_break_condition): + slow_value = self.value_abstract(slow) + fast1 = self.next_node_abstract(fast2) + fast2 = self.next_node_abstract(fast1) + if self.value_abstract(fast1) == slow_value or self.value_abstract( fast2) == slow_value: return True - slow = self.next_node(slow) + slow = self.next_node_abstract(slow) return False - def num_children(self): + def num_children_abstract(self, incoming_size, size_correction, is_valid_break_condition, type): logger = lldb.formatters.Logger.Logger() if self.count is None: # libstdc++ 6.0.21 added dedicated count field. @@ -67,42 +65,46 @@ if count_child and count_child.IsValid(): self.count = count_child.GetValueAsUnsigned(0) if self.count is None: - self.count = self.num_children_impl() - return self.count + self.count = self.num_children_impl_abstract(incoming_size,size_correction, is_valid_break_condition, type) + return self.count - def num_children_impl(self): + def num_children_impl_abstract(self, incoming_size, size_correction, is_valid_break_condition, type): logger = lldb.formatters.Logger.Logger() try: - next_val = self.next.GetValueAsUnsigned(0) - prev_val = self.prev.GetValueAsUnsigned(0) # After a std::list has been initialized, both next and prev will # be non-NULL - if next_val == 0 or prev_val == 0: - return 0 - if next_val == self.node_address: + next_val = self.next.GetValueAsUnsigned(0) + if next_val == 0: return 0 - if next_val == prev_val: - return 1 - if self.has_loop(): + if self.has_loop_abstract(is_valid_break_condition): return 0 - size = 2 + if type == LIST: + prev_val = self.prev.GetValueAsUnsigned(0) + if prev_val == 0: + return 0 + if next_val == self.node_address: + return 0 + if next_val == prev_val: + return 1 + size = incoming_size current = self.next while current.GetChildMemberWithName( - '_M_next').GetValueAsUnsigned(0) != self.node_address: + '_M_next').GetValueAsUnsigned(0) != is_valid_break_condition: size = size + 1 current = current.GetChildMemberWithName('_M_next') - return (size - 1) + return (size - size_correction) except: - return 0 + logger >> "Error determining the size" + return 0 - def get_child_index(self, name): + def get_child_index_abstract(self, name): logger = lldb.formatters.Logger.Logger() try: return int(name.lstrip('[').rstrip(']')) except: return -1 - - def get_child_at_index(self, index): + + def get_child_at_index_abstract(self, index, multiplier): logger = lldb.formatters.Logger.Logger() logger >> "Fetching child " + str(index) if index < 0: @@ -117,12 +119,12 @@ offset = offset - 1 return current.CreateChildAtOffset( '[' + str(index) + ']', - 2 * current.GetType().GetByteSize(), + multiplier * current.GetType().GetByteSize(), self.data_type) except: return None - def extract_type(self): + def extract_type_abstract(self): logger = lldb.formatters.Logger.Logger() list_type = self.valobj.GetType().GetUnqualifiedType() if list_type.IsReferenceType(): @@ -133,27 +135,65 @@ data_type = None return data_type - def update(self): + def update_abstract(self): logger = lldb.formatters.Logger.Logger() # preemptively setting this to None - we might end up changing our mind # later self.count = None try: - impl = self.valobj.GetChildMemberWithName('_M_impl') - self.node = impl.GetChildMemberWithName('_M_node') - self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) - self.next = self.node.GetChildMemberWithName('_M_next') - self.prev = self.node.GetChildMemberWithName('_M_prev') - self.data_type = self.extract_type() + self.impl = self.valobj.GetChildMemberWithName('_M_impl') + self.data_type = self.extract_type_abstract() self.data_size = self.data_type.GetByteSize() except: pass return False - def has_children(self): + def has_children_abstract(self): return True +class StdForwardListSynthProvider(AbstractListSynthProvider): + + def __init__(self, valobj, dict): + super().__init__(valobj, dict) + + def num_children(self): + return super().num_children_abstract(incoming_size=1, size_correction=0, is_valid_break_condition=0, type=FORWARD_LIST) + + + def get_child_at_index(self, index): + return super().get_child_at_index_abstract(index=index,multiplier=1) + + def extract_type(self): + return super().extract_type_abstract() + + def update(self): + super().update_abstract() + self.node = self.impl.GetChildMemberWithName('_M_head') + self.next = self.node.GetChildMemberWithName('_M_next') + return False + + +class StdListSynthProvider(AbstractListSynthProvider): + + def __init__(self, valobj, dict): + super().__init__(valobj, dict) + + def num_children(self): + return super().num_children_abstract(incoming_size=2, size_correction=1, is_valid_break_condition=self.node_address, type=LIST) + + def get_child_at_index(self, index): + return super().get_child_at_index_abstract(index=index,multiplier=2) + + def update(self): + self.update_abstract() + self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) + self.node = self.impl.GetChildMemberWithName('_M_node') + self.prev = self.node.GetChildMemberWithName('_M_prev') + self.next = self.node.GetChildMemberWithName('_M_next') + return False + + class StdVectorSynthProvider: class StdVectorImplementation(object): 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 @@ -923,6 +923,11 @@ SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression("^std::(__cxx11::)?forward_list<.+>(( )?&)?$"), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(false); cpp_category_sp->GetRegexTypeSummariesContainer()->Add( @@ -953,6 +958,10 @@ RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"), TypeSummaryImplSP( new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression("^std::(__cxx11::)?forward_list<.+>(( )?&)?$"), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); AddCXXSynthetic( cpp_category_sp, diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp + +USE_LIBSTDCPP := 1 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterStdForwardList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterStdForwardList.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterStdForwardList.py @@ -0,0 +1,51 @@ +""" +Test lldb data formatter subsystem. +""" + + + +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') + self.namespace = 'std' + + @add_test_categories(["libstdcxx"]) + 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', + '}']) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/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 +}