[Lldb-commits] [lldb] 577c1ee - [formatters] Add a libstdcpp formatter for forward_list and refactor list formatter

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 9 21:33:17 PST 2021


Author: Danil Stefaniuc
Date: 2021-11-09T21:33:08-08:00
New Revision: 577c1eecf8c4b078eecb57e1c5b8d86adfc3c08a

URL: https://github.com/llvm/llvm-project/commit/577c1eecf8c4b078eecb57e1c5b8d86adfc3c08a
DIFF: https://github.com/llvm/llvm-project/commit/577c1eecf8c4b078eecb57e1c5b8d86adfc3c08a.diff

LOG: [formatters] Add a libstdcpp formatter for forward_list and refactor list formatter

This diff adds a data formatter for libstdcpp's forward_list. Besides, it refactors the existing code by extracting the common functionality between libstdcpp forward_list and list formatters into the AbstractListSynthProvider class.

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D113362

Added: 
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp

Modified: 
    lldb/examples/synthetic/gnu_libstdcpp.py
    lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp

Removed: 
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp


################################################################################
diff  --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 30149d17b68af..a5b24273489f2 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -9,12 +9,17 @@
 # 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):
+        '''
+        :param valobj: The value object of the list
+        :param dict: A dict with metadata provided by LLDB
+        :param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one
+        '''
         logger = lldb.formatters.Logger.Logger()
         self.valobj = valobj
         self.count = None
+        self.has_prev = has_prev
         logger >> "Providing synthetic children for a list named " + \
             str(valobj.GetName())
 
@@ -24,13 +29,13 @@ def next_node(self, node):
 
     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()
@@ -73,26 +78,30 @@ def num_children(self):
     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 = 1
             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)
+            return size 
         except:
+            logger >> "Error determining the size"
             return 0
 
     def get_child_index(self, name):
@@ -101,7 +110,7 @@ def get_child_index(self, name):
             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)
@@ -115,9 +124,11 @@ def get_child_at_index(self, index):
             while offset > 0:
                 current = current.GetChildMemberWithName('_M_next')
                 offset = offset - 1
+            # C++ lists store the data of a node after its pointers. In the case of a forward list, there's just one pointer (next), and
+            # in the case of a double-linked list, there's an additional pointer (prev).
             return current.CreateChildAtOffset(
                 '[' + str(index) + ']',
-                2 * current.GetType().GetByteSize(),
+               (2 if self.has_prev else 1) * current.GetType().GetByteSize(),
                 self.data_type)
         except:
             return None
@@ -139,19 +150,60 @@ def update(self):
         # 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()
+            self.updateNodes()
         except:
             pass
         return False
 
+    '''
+    Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev)
+    and is mandatory to be overriden in each AbstractListSynthProvider subclass
+    '''
+    def updateNodes(self):
+        raise NotImplementedError
+
     def has_children(self):
         return True
+        
+    '''
+     Method is used to identify if a node traversal has reached its end
+     and is mandatory to be overriden in each AbstractListSynthProvider subclass
+    '''
+    def get_end_of_list_address(self):
+        raise NotImplementedError
+
+
+class StdForwardListSynthProvider(AbstractListSynthProvider):
+
+    def __init__(self, valobj, dict):
+        has_prev = False
+        super().__init__(valobj, dict, has_prev)
+
+    def updateNodes(self):
+        self.node = self.impl.GetChildMemberWithName('_M_head')
+        self.next = self.node.GetChildMemberWithName('_M_next')
+
+    def get_end_of_list_address(self):
+        return 0
+
+
+class StdListSynthProvider(AbstractListSynthProvider):
+
+    def __init__(self, valobj, dict):
+        has_prev = True
+        super().__init__(valobj, dict, has_prev)
+
+    def updateNodes(self):
+        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')
+
+    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
index e4d9813dcd5cf..83e8e52b86f26 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -923,6 +923,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       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 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       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/libcxx/forward_list/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile
similarity index 75%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile
index 680e1abfbef58..99998b20bcb05 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/Makefile
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/Makefile
@@ -1,4 +1,3 @@
 CXX_SOURCES := main.cpp
 
-USE_LIBCPP := 1
 include Makefile.rules

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
similarity index 79%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
index efbc4c538798e..d54aedd4960b9 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/TestDataFormatterLibcxxForwardList.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
@@ -9,8 +9,10 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
+USE_LIBSTDCPP = "USE_LIBSTDCPP"
+USE_LIBCPP = "USE_LIBCPP"
 
-class TestDataFormatterLibcxxForwardList(TestBase):
+class TestDataFormatterGenericForwardList(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
@@ -19,10 +21,10 @@ def setUp(self):
         self.line = line_number('main.cpp', '// break here')
         self.namespace = 'std'
 
-    @add_test_categories(["libc++"])
-    def test(self):
+    
+    def do_test(self, stdlib_type):
         """Test that std::forward_list is displayed correctly"""
-        self.build()
+        self.build(dictionary={stdlib_type: "1"})
         lldbutil.run_to_source_breakpoint(self, '// break here',
                 lldb.SBFileSpec("main.cpp", False))
 
@@ -49,3 +51,12 @@ def test(self):
                              '[3] = 4444',
                              '[4] = 55555',
                              '}'])
+
+    @add_test_categories(["libstdcxx"])
+    def test_libstdcpp(self):
+        self.do_test(USE_LIBSTDCPP)
+
+    @add_test_categories(["libc++"])
+    def test_libcpp(self):
+         self.do_test(USE_LIBCPP)
+    

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
index 0000000000000..bbefa765d5fe2
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp
@@ -0,0 +1,7 @@
+#include <forward_list>
+
+int main() {
+  std::forward_list<int> empty{}, one_elt{47},
+      five_elts{1, 22, 333, 4444, 55555};
+  return 0; // break here
+}

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp
deleted file mode 100644
index 73abda6e82e56..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/forward_list/main.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <forward_list>
-
-int main()
-{
-  std::forward_list<int> empty{}, one_elt{47}, five_elts{1,22,333,4444,55555};
-  return 0; // break here
-}


        


More information about the lldb-commits mailing list