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 @@ -9,12 +9,13 @@ # before relying on these formatters to do the right thing for your setup -class StdListSynthProvider: - - def __init__(self, valobj, dict): +class AbstractListSynthProvider: + def __init__(self, valobj, dict, has_prev, node_value_pointer_offset): logger = lldb.formatters.Logger.Logger() self.valobj = valobj self.count = None + self.has_prev = has_prev + self.node_value_pointer_offset = node_value_pointer_offset logger >> "Providing synthetic children for a list named " + \ str(valobj.GetName()) @@ -24,13 +25,13 @@ def is_valid(self, node): logger = lldb.formatters.Logger.Logger() - valid = self.value(self.next_node(node)) != self.node_address + valid = self.value(self.next_node(node)) != self.get_end_of_list_address() if valid: logger >> "%s is valid" % str(self.valobj.GetName()) else: logger >> "synthetic value is not valid" return valid - + def value(self, node): logger = lldb.formatters.Logger.Logger() value = node.GetValueAsUnsigned() @@ -68,32 +69,38 @@ self.count = count_child.GetValueAsUnsigned(0) if self.count is None: self.count = self.num_children_impl() - return self.count + return self.count def num_children_impl(self): 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(): return 0 - size = 2 + if self.has_prev: + 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 = self.node_value_pointer_offset current = self.next while current.GetChildMemberWithName( - '_M_next').GetValueAsUnsigned(0) != self.node_address: + '_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address(): size = size + 1 current = current.GetChildMemberWithName('_M_next') - return (size - 1) + if self.has_prev: + return (size-1) + return size except: - return 0 + logger >> "Error determining the size" + return 0 def get_child_index(self, name): logger = lldb.formatters.Logger.Logger() @@ -101,7 +108,7 @@ return int(name.lstrip('[').rstrip(']')) except: return -1 - + def get_child_at_index(self, index): logger = lldb.formatters.Logger.Logger() logger >> "Fetching child " + str(index) @@ -117,7 +124,7 @@ offset = offset - 1 return current.CreateChildAtOffset( '[' + str(index) + ']', - 2 * current.GetType().GetByteSize(), + self.node_value_pointer_offset * current.GetType().GetByteSize(), self.data_type) except: return None @@ -139,11 +146,7 @@ # 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.impl = self.valobj.GetChildMemberWithName('_M_impl') self.data_type = self.extract_type() self.data_size = self.data_type.GetByteSize() except: @@ -152,6 +155,70 @@ def has_children(self): return True + + # This address is used to identify if a node traversal has reached its end + # and is mandatory to be overriden in each AbstractListSynthProvider child class + def get_end_of_list_address(self): + raise NotImplementedError + + +class StdForwardListSynthProvider(AbstractListSynthProvider): + + def __init__(self, valobj, dict): + has_prev = False + # Lists have 1 or more pointers followed by the value. + # We should specify the number of the pointers for the correct + # size determination + node_value_pointer_offset = 1 + super().__init__(valobj, dict, has_prev, node_value_pointer_offset) + + def num_children(self): + return super().num_children() + + def get_child_at_index(self, index): + return super().get_child_at_index(index) + + def extract_type(self): + return super().extract_type() + + def update(self): + super().update() + self.node = self.impl.GetChildMemberWithName('_M_head') + self.next = self.node.GetChildMemberWithName('_M_next') + return False + + # This address is used to identify if a node traversal has reached its end + def get_end_of_list_address(self): + return 0 + + +class StdListSynthProvider(AbstractListSynthProvider): + + def __init__(self, valobj, dict): + has_prev = True + # Lists have 1 or more pointers followed by the value. + # We should specify the number of the pointers for the correct + # size determination + node_value_pointer_offset = 2 + super().__init__(valobj, dict, has_prev, node_value_pointer_offset) + + def num_children(self): + return super().num_children() + + def get_child_at_index(self, index): + return super().get_child_at_index(index) + + def update(self): + super().update() + 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 + + # This address is used to identify if a node traversal has reached its end + def get_end_of_list_address(self): + return self.node_address class StdVectorSynthProvider: 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 +}