[Lldb-commits] [lldb] e3dea5c - [formatters] Add a formatter for libstdc++ optional

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Mon Nov 22 15:36:59 PST 2021


Author: Walter Erquinigo
Date: 2021-11-22T15:36:46-08:00
New Revision: e3dea5cf0e326366ab95a49d167fde8b0816e292

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

LOG: [formatters] Add a formatter for libstdc++ optional

Besides adding the formatter and the summary, this makes the libcxx
tests also work for this case.

This is the polished version of https://reviews.llvm.org/D114266,
authored by Danil Stefaniuc.

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

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

Modified: 
    lldb/bindings/interface/SBValue.i
    lldb/examples/synthetic/gnu_libstdcpp.py
    lldb/include/lldb/API/SBValue.h
    lldb/source/API/SBValue.cpp
    lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp

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


################################################################################
diff  --git a/lldb/bindings/interface/SBValue.i b/lldb/bindings/interface/SBValue.i
index dd012e667a20c..bc66a4ae28f82 100644
--- a/lldb/bindings/interface/SBValue.i
+++ b/lldb/bindings/interface/SBValue.i
@@ -410,6 +410,9 @@ public:
     bool
     SetData (lldb::SBData &data, lldb::SBError& error);
 
+    lldb::SBValue
+    Clone(const char *new_name);
+
   lldb::addr_t
   GetLoadAddress();
 

diff  --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 9272b814f78d8..27fca0a5d3271 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -8,6 +8,34 @@
 # 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
 
+def StdOptionalSummaryProvider(valobj, dict):
+    has_value = valobj.GetNumChildren() > 0
+    # We add wrapping spaces for consistency with the libcxx formatter
+    return " Has Value=" + ("true" if has_value else "false") + " "
+
+
+class StdOptionalSynthProvider:
+    def __init__(self, valobj, dict):
+        self.valobj = valobj
+
+    def update(self):
+        try:
+            self.payload = self.valobj.GetChildMemberWithName('_M_payload')
+            self.value = self.payload.GetChildMemberWithName('_M_payload')
+            self.count = self.payload.GetChildMemberWithName('_M_engaged').GetValueAsUnsigned(0)
+        except:
+            self.count = 0
+        return False
+
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name):
+        return 0
+
+    def get_child_at_index(self, index):
+        return self.value.Clone('Value')
 
 """
  This formatter can be applied to all
@@ -26,15 +54,15 @@ def get_object_kind(self, valobj):
     def extract_type(self):
         type = self.valobj.GetType()
         # type of std::pair<key, value> is the first template
-        # argument type of the 4th template argument to std::map and 
-        # 3rd template argument for std::set. That's why 
+        # argument type of the 4th template argument to std::map and
+        # 3rd template argument for std::set. That's why
         # we need to know kind of the object
         template_arg_num = 4 if self.kind == "map" else 3
         allocator_type = type.GetTemplateArgumentType(template_arg_num)
         data_type = allocator_type.GetTemplateArgumentType(0)
         return data_type
 
-    def update(self): 
+    def update(self):
         # preemptively setting this to None - we might end up changing our mind
         # later
         self.count = None
@@ -64,12 +92,12 @@ def get_child_at_index(self, index):
             return None
         try:
             offset = index
-            current = self.next     
+            current = self.next
             while offset > 0:
                 current = current.GetChildMemberWithName('_M_nxt')
                 offset = offset - 1
             return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type)
-        
+
         except:
             logger >> "Cannot get child"
             return None
@@ -115,7 +143,7 @@ def is_valid(self, node):
         else:
             logger >> "synthetic value is not valid"
         return valid
-    
+
     def value(self, node):
         logger = lldb.formatters.Logger.Logger()
         value = node.GetValueAsUnsigned()
@@ -161,7 +189,7 @@ def num_children_impl(self):
             # After a std::list has been initialized, both next and prev will
             # be non-NULL
             next_val = self.next.GetValueAsUnsigned(0)
-            if next_val == 0: 
+            if next_val == 0:
                 return 0
             if self.has_loop():
                 return 0
@@ -172,14 +200,14 @@ def num_children_impl(self):
                 if next_val == self.node_address:
                     return 0
                 if next_val == prev_val:
-                    return 1   
+                    return 1
             size = 1
             current = self.next
             while current.GetChildMemberWithName(
                     '_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address():
                 size = size + 1
                 current = current.GetChildMemberWithName('_M_next')
-            return size 
+            return size
         except:
             logger >> "Error determining the size"
             return 0
@@ -190,7 +218,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)
@@ -247,7 +275,7 @@ def updateNodes(self):
 
     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
@@ -368,7 +396,7 @@ def update(self):
                     self.count = 0
             except:
                 pass
-            return False 
+            return False
 
     class StdVBoolImplementation(object):
 

diff  --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 69be02545b35d..a8578abec6b7e 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -246,6 +246,12 @@ class LLDB_API SBValue {
 
   bool SetData(lldb::SBData &data, lldb::SBError &error);
 
+  /// Creates a copy of the SBValue with a new name and setting the current
+  /// SBValue as its parent. It should be used when we want to change the
+  /// name of a SBValue without modifying the actual SBValue itself
+  /// (e.g. sythetic child provider).
+  lldb::SBValue Clone(const char *new_name);
+
   lldb::SBDeclaration GetDeclaration();
 
   /// Find out if a SBValue might have children.

diff  --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 9faee102c5e3d..e3325b8d36fa9 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1431,6 +1431,18 @@ bool SBValue::SetData(lldb::SBData &data, SBError &error) {
   return ret;
 }
 
+lldb::SBValue SBValue::Clone(const char *new_name) {
+  LLDB_RECORD_METHOD(lldb::SBValue, SBValue, Clone, (const char *), new_name);
+
+  ValueLocker locker;
+  lldb::ValueObjectSP value_sp(GetSP(locker));
+
+  if (value_sp)
+    return lldb::SBValue(value_sp->Clone(ConstString(new_name)));
+  else
+    return lldb::SBValue();
+}
+
 lldb::SBDeclaration SBValue::GetDeclaration() {
   LLDB_RECORD_METHOD_NO_ARGS(lldb::SBDeclaration, SBValue, GetDeclaration);
 
@@ -1656,6 +1668,7 @@ void RegisterMethods<SBValue>(Registry &R) {
   LLDB_REGISTER_METHOD(lldb::SBData, SBValue, GetData, ());
   LLDB_REGISTER_METHOD(bool, SBValue, SetData,
                        (lldb::SBData &, lldb::SBError &));
+  LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, Clone, (const char *));
   LLDB_REGISTER_METHOD(lldb::SBDeclaration, SBValue, GetDeclaration, ());
   LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBValue, Watch,
                        (bool, bool, bool, lldb::SBError &));

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 3182294abd448..0cb80e06df498 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -913,6 +913,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
+  cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+      RegularExpression("^std::optional<.+>(( )?&)?$"),
+      SyntheticChildrenSP(new ScriptedSyntheticChildren(
+          stl_synth_flags,
+          "lldb.formatters.cpp.gnu_libstdcpp.StdOptionalSynthProvider")));
   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
       RegularExpression("^std::multiset<.+> >(( )?&)?$"),
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
@@ -933,8 +938,14 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       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(
+      RegularExpression("^std::optional<.+>(( )?&)?$"),
+      TypeSummaryImplSP(new ScriptSummaryFormat(
+          stl_summary_flags,
+          "lldb.formatters.cpp.gnu_libstdcpp.StdOptionalSummaryProvider")));
   cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
       RegularExpression("^std::bitset<.+>(( )?&)?$"),
       TypeSummaryImplSP(

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
similarity index 84%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
index 23496eb206572..2ee2cf4d2c05c 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
@@ -1,6 +1,4 @@
 CXX_SOURCES := main.cpp
 
-USE_LIBCPP := 1
-
 CXXFLAGS_EXTRAS := -std=c++17 -fno-exceptions
 include Makefile.rules

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
similarity index 75%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
index 27c8d7f474ed1..557875132a656 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
@@ -1,29 +1,18 @@
-"""
-Test lldb data formatter subsystem.
-"""
-
-
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
+USE_LIBSTDCPP = "USE_LIBSTDCPP"
+USE_LIBCPP = "USE_LIBCPP"
 
-class LibcxxOptionalDataFormatterTestCase(TestBase):
+class GenericOptionalDataFormatterTestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    @add_test_categories(["libc++"])
-    ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
-    ## with -std=c++17.
-    @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '7.0'])
-    ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
-    @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
-
-    def test_with_run_command(self):
+    def do_test_with_run_command(self, stdlib_type):
         """Test that that file and class static variables display correctly."""
-        self.build()
+        self.build(dictionary={stdlib_type: "1"})
         self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
 
         bkpt = self.target().FindBreakpointByID(
@@ -45,7 +34,7 @@ def test_with_run_command(self):
         ## detected we have a sufficient libc++ version to support optional
         ## false means we do not and therefore should skip the test
         if output.find("(bool) has_optional = false") != -1 :
-           self.skipTest( "Optional not supported" ) 
+           self.skipTest( "Optional not supported" )
 
         lldbutil.continue_to_breakpoint(self.process(), bkpt)
 
@@ -71,3 +60,21 @@ def test_with_run_command(self):
                     substrs=['(optional_string) ostring =  Has Value=true  {',
                         'Value = "hello"',
                         '}'])
+
+    @add_test_categories(["libc++"])
+    ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
+    ## with -std=c++17.
+    @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '7.0'])
+    ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
+    @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
+    def test_with_run_command_libcpp(self):
+        self.do_test_with_run_command(USE_LIBCPP)
+
+    @add_test_categories(["libstdcxx"])
+    ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
+    ## with -std=c++17.
+    @skipIf(compiler="clang", compiler_version=['<', '7.0'])
+    ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
+    @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
+    def test_with_run_command_libstdcpp(self):
+        self.do_test_with_run_command(USE_LIBSTDCPP)

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp
new file mode 100644
index 0000000000000..8f30df4626636
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp
@@ -0,0 +1,41 @@
+#include <cstdio>
+#include <string>
+#include <vector>
+
+// If we have libc++ 4.0 or greater we should have <optional>
+// According to libc++ C++1z status page
+// https://libcxx.llvm.org/cxx1z_status.html
+#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 4000
+#include <optional>
+#define HAVE_OPTIONAL 1
+#else
+#define HAVE_OPTIONAL 0
+#endif
+
+int main() {
+  bool has_optional = HAVE_OPTIONAL;
+
+  printf("%d\n", has_optional); // break here
+
+#if HAVE_OPTIONAL == 1
+  using int_vect = std::vector<int>;
+  using optional_int = std::optional<int>;
+  using optional_int_vect = std::optional<int_vect>;
+  using optional_string = std::optional<std::string>;
+
+  optional_int number_not_engaged;
+  optional_int number_engaged = 42;
+
+  printf("%d\n", *number_engaged);
+
+  optional_int_vect numbers{{1, 2, 3, 4}};
+
+  printf("%d %d\n", numbers.value()[0], numbers.value()[1]);
+
+  optional_string ostring = "hello";
+
+  printf("%s\n", ostring->c_str());
+#endif
+
+  return 0; // break here
+}

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp
deleted file mode 100644
index 16bb98c61056a..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <cstdio>
-#include <string>
-#include <vector>
-
-// If we have libc++ 4.0 or greater we should have <optional>
-// According to libc++ C++1z status page https://libcxx.llvm.org/cxx1z_status.html
-#if _LIBCPP_VERSION >= 4000
-#include <optional>
-#define HAVE_OPTIONAL 1
-#else
-#define HAVE_OPTIONAL 0
-#endif
-
-
-int main()
-{
-    bool has_optional = HAVE_OPTIONAL ;
-
-    printf( "%d\n", has_optional ) ; // break here
-
-#if HAVE_OPTIONAL == 1
-    using int_vect = std::vector<int> ;
-    using optional_int = std::optional<int> ;
-    using optional_int_vect = std::optional<int_vect> ;
-    using optional_string = std::optional<std::string> ;
-
-    optional_int number_not_engaged ;
-    optional_int number_engaged = 42 ;
-
-    printf( "%d\n", *number_engaged) ;
-
-    optional_int_vect numbers{{1,2,3,4}} ;
-
-    printf( "%d %d\n", numbers.value()[0], numbers.value()[1] ) ;
-
-    optional_string ostring = "hello" ;
-
-    printf( "%s\n", ostring->c_str() ) ;
-#endif
-
-    return 0; // break here
-}


        


More information about the lldb-commits mailing list