Patchwork FYI: update libstdc++ pretty-printers

login
register
mail settings
Submitter Tom Tromey
Date Feb. 1, 2011, 2:27 p.m.
Message ID <m3mxmfq17x.fsf@fleche.redhat.com>
Download mbox | patch
Permalink /patch/81317/
State New
Headers show

Comments

Tom Tromey - Feb. 1, 2011, 2:27 p.m.
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

Tom> Doug Evans recently added some features to gdb to make it possible for
Tom> the user to individually enable or disable pretty-printers.  In order
Tom> for this to work, the printers must conform to a certain extended
Tom> protocol.

I happened to notice that I forgot to actually check the 'enabled'
flag -- so disabling did not actually work with the previous patch.

Here's an updated patch.

Tom

2011-02-01  Tom Tromey  <tromey@redhat.com>

	* python/libstdcxx/v6/printers.py (_use_gdb_pp): New global.
	Try to import `gdb.printing' module.
	(UniquePointerPrinter.__init__): Add 'typename' argument.
	(StdSlistPrinter.__init__): Likewise.
	(StdSlistIteratorPrinter.__init__): Likewise.
	(StdVectorIteratorPrinter.__init__): Likewise.
	(StdRbtreeIteratorPrinter.__init__): Likewise.
	(StdDebugIteratorPrinter.__init__): Likewise.
	(StdDequeIteratorPrinter.__init__): Likewise.
	(StdStringPrinter.__init__): Likewise.
	(RxPrinter, Printer): New class.
	(libstdcxx_printer): New global.
	(register_libstdcxx_printers): Rewrite.
	(build_libstdcxx_dictionary): Rewrite.
	(pretty_printers_dict): Remove.
Tom Tromey - March 14, 2011, 8:28 p.m.
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

Tom> Doug Evans recently added some features to gdb to make it possible for
Tom> the user to individually enable or disable pretty-printers.  In order
Tom> for this to work, the printers must conform to a certain extended
Tom> protocol.

Tom> I happened to notice that I forgot to actually check the 'enabled'
Tom> flag -- so disabling did not actually work with the previous patch.

Tom> Here's an updated patch.

Now that GCC is in Stage 1, I am checking this in.

Tom

Patch

Index: python/libstdcxx/v6/printers.py
===================================================================
--- python/libstdcxx/v6/printers.py	(revision 168933)
+++ python/libstdcxx/v6/printers.py	(working copy)
@@ -1,6 +1,6 @@ 
 # Pretty-printers for libstc++.
 
-# Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+# Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -19,6 +19,13 @@ 
 import itertools
 import re
 
+# Try to use the new-style pretty-printing if available.
+_use_gdb_pp = True
+try:
+    import gdb.printing
+except ImportError:
+    _use_gdb_pp = False
+
 class StdPointerPrinter:
     "Print a smart pointer of some kind"
 
@@ -36,7 +43,7 @@ 
 class UniquePointerPrinter:
     "Print a unique_ptr"
 
-    def __init__ (self, val):
+    def __init__ (self, typename, val):
         self.val = val
 
     def to_string (self):
@@ -125,7 +132,7 @@ 
             self.count = self.count + 1
             return ('[%d]' % count, elt['_M_data'])
 
-    def __init__(self, val):
+    def __init__(self, typename, val):
         self.val = val
 
     def children(self):
@@ -141,7 +148,7 @@ 
 class StdSlistIteratorPrinter:
     "Print __gnu_cxx::slist::iterator"
 
-    def __init__(self, val):
+    def __init__(self, typename, val):
         self.val = val
 
     def to_string(self):
@@ -228,7 +235,7 @@ 
 class StdVectorIteratorPrinter:
     "Print std::vector::iterator"
 
-    def __init__(self, val):
+    def __init__(self, typename, val):
         self.val = val
 
     def to_string(self):
@@ -351,7 +358,7 @@ 
 class StdRbtreeIteratorPrinter:
     "Print std::map::iterator"
 
-    def __init__ (self, val):
+    def __init__ (self, typename, val):
         self.val = val
 
     def to_string (self):
@@ -363,7 +370,7 @@ 
 class StdDebugIteratorPrinter:
     "Print a debug enabled version of an iterator"
 
-    def __init__ (self, val):
+    def __init__ (self, typename, val):
         self.val = val
 
     # Just strip away the encapsulating __gnu_debug::_Safe_iterator
@@ -557,7 +564,7 @@ 
 class StdDequeIteratorPrinter:
     "Print std::deque::iterator"
 
-    def __init__(self, val):
+    def __init__(self, typename, val):
         self.val = val
 
     def to_string(self):
@@ -566,7 +573,7 @@ 
 class StdStringPrinter:
     "Print a std::basic_string of some kind"
 
-    def __init__(self, val):
+    def __init__(self, typename, val):
         self.val = val
 
     def to_string(self):
@@ -678,125 +685,189 @@ 
     def display_hint (self):
         return 'map'
 
-def register_libstdcxx_printers (obj):
-    "Register libstdc++ pretty-printers with objfile Obj."
+# A "regular expression" printer which conforms to the
+# "SubPrettyPrinter" protocol from gdb.printing.
+class RxPrinter(object):
+    def __init__(self, name, function):
+        super(RxPrinter, self).__init__()
+        self.name = name
+        self.function = function
+        self.enabled = True
 
-    if obj == None:
-        obj = gdb
+    def invoke(self, value):
+        if not self.enabled:
+            return None
+        return self.function(self.name, value)
 
-    obj.pretty_printers.append (lookup_function)
+# A pretty-printer that conforms to the "PrettyPrinter" protocol from
+# gdb.printing.  It can also be used directly as an old-style printer.
+class Printer(object):
+    def __init__(self, name):
+        super(Printer, self).__init__()
+        self.name = name
+        self.subprinters = []
+        self.lookup = {}
+        self.enabled = True
+        self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)<.*>$')
 
-def lookup_function (val):
-    "Look-up and return a pretty-printer that can print val."
+    def add(self, name, function):
+        # A small sanity check.
+        # FIXME
+        if not self.compiled_rx.match(name + '<>'):
+            raise ValueError, 'libstdc++ programming error: "%s" does not match' % name
+        printer = RxPrinter(name, function)
+        self.subprinters.append(printer)
+        self.lookup[name] = printer
 
-    # Get the type.
-    type = val.type
+    @staticmethod
+    def get_basic_type(type):
+        # If it points to a reference, get the reference.
+        if type.code == gdb.TYPE_CODE_REF:
+            type = type.target ()
 
-    # If it points to a reference, get the reference.
-    if type.code == gdb.TYPE_CODE_REF:
-        type = type.target ()
+        # Get the unqualified type, stripped of typedefs.
+        type = type.unqualified ().strip_typedefs ()
 
-    # Get the unqualified type, stripped of typedefs.
-    type = type.unqualified ().strip_typedefs ()
+        return type.tag
 
-    # Get the type name.    
-    typename = type.tag
-    if typename == None:
+    def __call__(self, val):
+        typename = self.get_basic_type(val.type)
+        if not typename:
+            return None
+
+        # All the types we match are template types, so we can use a
+        # dictionary.
+        match = self.compiled_rx.match(typename)
+        if not match:
+            return None
+
+        basename = match.group(1)
+        if basename in self.lookup:
+            return self.lookup[basename].invoke(val)
+
+        # Cannot find a pretty printer.  Return None.
         return None
 
-    # Iterate over local dictionary of types to determine
-    # if a printer is registered for that type.  Return an
-    # instantiation of the printer if found.
-    for function in pretty_printers_dict:
-        if function.search (typename):
-            return pretty_printers_dict[function] (val)
-        
-    # Cannot find a pretty printer.  Return None.
-    return None
+libstdcxx_printer = None
 
+def register_libstdcxx_printers (obj):
+    "Register libstdc++ pretty-printers with objfile Obj."
+
+    global _use_gdb_pp
+    global libstdcxx_printer
+
+    if _use_gdb_pp:
+        gdb.printing.register_pretty_printer(obj, libstdcxx_printer)
+    else:
+        if obj is None:
+            obj = gdb
+        obj.pretty_printers.append(libstdcxx_printer)
+
 def build_libstdcxx_dictionary ():
+    global libstdcxx_printer
+
+    libstdcxx_printer = Printer("libstdc++-v6")
+
     # libstdc++ objects requiring pretty-printing.
     # In order from:
     # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html
-    pretty_printers_dict[re.compile('^std::basic_string<.*>$')] = lambda val: StdStringPrinter(val)
-    pretty_printers_dict[re.compile('^std::bitset<.*>$')] = lambda val: StdBitsetPrinter("std::bitset", val)
-    pretty_printers_dict[re.compile('^std::deque<.*>$')] = lambda val: StdDequePrinter("std::deque", val)
-    pretty_printers_dict[re.compile('^std::list<.*>$')] = lambda val: StdListPrinter("std::list", val)
-    pretty_printers_dict[re.compile('^std::map<.*>$')] = lambda val: StdMapPrinter("std::map", val)
-    pretty_printers_dict[re.compile('^std::multimap<.*>$')] = lambda val: StdMapPrinter("std::multimap", val)
-    pretty_printers_dict[re.compile('^std::multiset<.*>$')] = lambda val: StdSetPrinter("std::multiset", val)
-    pretty_printers_dict[re.compile('^std::priority_queue<.*>$')] = lambda val: StdStackOrQueuePrinter("std::priority_queue", val)
-    pretty_printers_dict[re.compile('^std::queue<.*>$')] = lambda val: StdStackOrQueuePrinter("std::queue", val)
-    pretty_printers_dict[re.compile('^std::tuple<.*>$')] = lambda val: StdTuplePrinter("std::tuple", val)
-    pretty_printers_dict[re.compile('^std::set<.*>$')] = lambda val: StdSetPrinter("std::set", val)
-    pretty_printers_dict[re.compile('^std::stack<.*>$')] = lambda val: StdStackOrQueuePrinter("std::stack", val)
-    pretty_printers_dict[re.compile('^std::unique_ptr<.*>$')] = UniquePointerPrinter
-    pretty_printers_dict[re.compile('^std::vector<.*>$')] = lambda val: StdVectorPrinter("std::vector", val)
+    libstdcxx_printer.add('std::basic_string', StdStringPrinter)
+    libstdcxx_printer.add('std::bitset', StdBitsetPrinter)
+    libstdcxx_printer.add('std::deque', StdDequePrinter)
+    libstdcxx_printer.add('std::list', StdListPrinter)
+    libstdcxx_printer.add('std::map', StdMapPrinter)
+    libstdcxx_printer.add('std::multimap', StdMapPrinter)
+    libstdcxx_printer.add('std::multiset', StdSetPrinter)
+    libstdcxx_printer.add('std::priority_queue', StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::queue', StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::tuple', StdTuplePrinter)
+    libstdcxx_printer.add('std::set', StdSetPrinter)
+    libstdcxx_printer.add('std::stack', StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::unique_ptr', UniquePointerPrinter)
+    libstdcxx_printer.add('std::vector', StdVectorPrinter)
     # vector<bool>
 
     # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG.
-    pretty_printers_dict[re.compile('^std::__debug::bitset<.*>$')] = lambda val: StdBitsetPrinter("std::__debug::bitset", val)
-    pretty_printers_dict[re.compile('^std::__debug::deque<.*>$')] = lambda val: StdDequePrinter("std::__debug::deque", val)
-    pretty_printers_dict[re.compile('^std::__debug::list<.*>$')] = lambda val: StdListPrinter("std::__debug::list", val)
-    pretty_printers_dict[re.compile('^std::__debug::map<.*>$')] = lambda val: StdMapPrinter("std::__debug::map", val)
-    pretty_printers_dict[re.compile('^std::__debug::multimap<.*>$')] = lambda val: StdMapPrinter("std::__debug::multimap", val)
-    pretty_printers_dict[re.compile('^std::__debug::multiset<.*>$')] = lambda val: StdSetPrinter("std::__debug::multiset", val)
-    pretty_printers_dict[re.compile('^std::__debug::priority_queue<.*>$')] = lambda val: StdStackOrQueuePrinter("std::__debug::priority_queue", val)
-    pretty_printers_dict[re.compile('^std::__debug::queue<.*>$')] = lambda val: StdStackOrQueuePrinter("std::__debug::queue", val)
-    pretty_printers_dict[re.compile('^std::__debug::set<.*>$')] = lambda val: StdSetPrinter("std::__debug::set", val)
-    pretty_printers_dict[re.compile('^std::__debug::stack<.*>$')] = lambda val: StdStackOrQueuePrinter("std::__debug::stack", val)
-    pretty_printers_dict[re.compile('^std::__debug::unique_ptr<.*>$')] = UniquePointerPrinter
-    pretty_printers_dict[re.compile('^std::__debug::vector<.*>$')] = lambda val: StdVectorPrinter("std::__debug::vector", val)
+    libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter)
+    libstdcxx_printer.add('std::__debug::deque', StdDequePrinter)
+    libstdcxx_printer.add('std::__debug::list', StdListPrinter)
+    libstdcxx_printer.add('std::__debug::map', StdMapPrinter)
+    libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter)
+    libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter)
+    libstdcxx_printer.add('std::__debug::priority_queue',
+                          StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::__debug::queue', StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::__debug::set', StdSetPrinter)
+    libstdcxx_printer.add('std::__debug::stack', StdStackOrQueuePrinter)
+    libstdcxx_printer.add('std::__debug::unique_ptr', UniquePointerPrinter)
+    libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter)
 
     # These are the TR1 and C++0x printers.
     # For array - the default GDB pretty-printer seems reasonable.
-    pretty_printers_dict[re.compile('^std::shared_ptr<.*>$')] = lambda val: StdPointerPrinter ('std::shared_ptr', val)
-    pretty_printers_dict[re.compile('^std::weak_ptr<.*>$')] = lambda val: StdPointerPrinter ('std::weak_ptr', val)
-    pretty_printers_dict[re.compile('^std::unordered_map<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::unordered_map', val)
-    pretty_printers_dict[re.compile('^std::unordered_set<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::unordered_set', val)
-    pretty_printers_dict[re.compile('^std::unordered_multimap<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::unordered_multimap', val)
-    pretty_printers_dict[re.compile('^std::unordered_multiset<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::unordered_multiset', val)
+    libstdcxx_printer.add('std::shared_ptr', StdPointerPrinter)
+    libstdcxx_printer.add('std::weak_ptr', StdPointerPrinter)
+    libstdcxx_printer.add('std::unordered_map', Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::unordered_set', Tr1UnorderedSetPrinter)
+    libstdcxx_printer.add('std::unordered_multimap', Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::unordered_multiset', Tr1UnorderedSetPrinter)
 
-    pretty_printers_dict[re.compile('^std::tr1::shared_ptr<.*>$')] = lambda val: StdPointerPrinter ('std::tr1::shared_ptr', val)
-    pretty_printers_dict[re.compile('^std::tr1::weak_ptr<.*>$')] = lambda val: StdPointerPrinter ('std::tr1::weak_ptr', val)
-    pretty_printers_dict[re.compile('^std::tr1::unordered_map<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::tr1::unordered_map', val)
-    pretty_printers_dict[re.compile('^std::tr1::unordered_set<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::tr1::unordered_set', val)
-    pretty_printers_dict[re.compile('^std::tr1::unordered_multimap<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::tr1::unordered_multimap', val)
-    pretty_printers_dict[re.compile('^std::tr1::unordered_multiset<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::tr1::unordered_multiset', val)
+    libstdcxx_printer.add('std::tr1::shared_ptr', StdPointerPrinter)
+    libstdcxx_printer.add('std::tr1::weak_ptr', StdPointerPrinter)
+    libstdcxx_printer.add('std::tr1::unordered_map', Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::tr1::unordered_set', Tr1UnorderedSetPrinter)
+    libstdcxx_printer.add('std::tr1::unordered_multimap',
+                          Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::tr1::unordered_multiset',
+                          Tr1UnorderedSetPrinter)
 
     # These are the C++0x printer registrations for -D_GLIBCXX_DEBUG cases.
     # The tr1 namespace printers do not seem to have any debug
     # equivalents, so do no register them.
-    pretty_printers_dict[re.compile('^std::__debug::unordered_map<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::__debug::unordered_map', val)
-    pretty_printers_dict[re.compile('^std::__debug::unordered_set<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::__debug::unordered_set', val)
-    pretty_printers_dict[re.compile('^std::__debug::unordered_multimap<.*>$')] = lambda val: Tr1UnorderedMapPrinter ('std::__debug::unordered_multimap',  val)
-    pretty_printers_dict[re.compile('^std::__debug::unordered_multiset<.*>$')] = lambda val: Tr1UnorderedSetPrinter ('std::__debug:unordered_multiset', val)
+    libstdcxx_printer.add('std::__debug::unordered_map',
+                          Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::__debug::unordered_set',
+                          Tr1UnorderedSetPrinter)
+    libstdcxx_printer.add('std::__debug::unordered_multimap',
+                          Tr1UnorderedMapPrinter)
+    libstdcxx_printer.add('std::__debug::unordered_multiset',
+                          Tr1UnorderedSetPrinter)
 
 
     # Extensions.
-    pretty_printers_dict[re.compile('^__gnu_cxx::slist<.*>$')] = StdSlistPrinter
+    libstdcxx_printer.add('__gnu_cxx::slist', StdSlistPrinter)
 
     if True:
         # These shouldn't be necessary, if GDB "print *i" worked.
         # But it often doesn't, so here they are.
-        pretty_printers_dict[re.compile('^std::_List_iterator<.*>$')] = lambda val: StdListIteratorPrinter("std::_List_iterator",val)
-        pretty_printers_dict[re.compile('^std::_List_const_iterator<.*>$')] = lambda val: StdListIteratorPrinter("std::_List_const_iterator",val)
-        pretty_printers_dict[re.compile('^std::_Rb_tree_iterator<.*>$')] = lambda val: StdRbtreeIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^std::_Rb_tree_const_iterator<.*>$')] = lambda val: StdRbtreeIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^std::_Deque_iterator<.*>$')] = lambda val: StdDequeIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^std::_Deque_const_iterator<.*>$')] = lambda val: StdDequeIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^__gnu_cxx::__normal_iterator<.*>$')] = lambda val: StdVectorIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^__gnu_cxx::_Slist_iterator<.*>$')] = lambda val: StdSlistIteratorPrinter(val)
+        libstdcxx_printer.add('std::_List_iterator', StdListIteratorPrinter)
+        libstdcxx_printer.add('std::_List_const_iterator',
+                              StdListIteratorPrinter)
+        libstdcxx_printer.add('std::_Rb_tree_iterator',
+                              StdRbtreeIteratorPrinter)
+        libstdcxx_printer.add('std::_Rb_tree_const_iterator',
+                              StdRbtreeIteratorPrinter)
+        libstdcxx_printer.add('std::_Deque_iterator', StdDequeIteratorPrinter)
+        libstdcxx_printer.add('std::_Deque_const_iterator',
+                              StdDequeIteratorPrinter)
+        libstdcxx_printer.add('__gnu_cxx::__normal_iterator',
+                              StdVectorIteratorPrinter)
+        libstdcxx_printer.add('__gnu_cxx::_Slist_iterator',
+                              StdSlistIteratorPrinter)
 
-        # Debug (compiled with -D_GLIBCXX_DEBUG) printer registrations.
-        # The Rb_tree debug iterator when unwrapped from the encapsulating __gnu_debug::_Safe_iterator
-        # does not have the __norm namespace. Just use the existing printer registration for that.
-        pretty_printers_dict[re.compile('^__gnu_debug::_Safe_iterator<.*>$')] = lambda val: StdDebugIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^std::__norm::_List_iterator<.*>$')] = lambda val: StdListIteratorPrinter ("std::__norm::_List_iterator",val)
-        pretty_printers_dict[re.compile('^std::__norm::_List_const_iterator<.*>$')] = lambda val: StdListIteratorPrinter ("std::__norm::_List_const_iterator",val)
-        pretty_printers_dict[re.compile('^std::__norm::_Deque_const_iterator<.*>$')] = lambda val: StdDequeIteratorPrinter(val)
-        pretty_printers_dict[re.compile('^std::__norm::_Deque_iterator<.*>$')] = lambda val: StdDequeIteratorPrinter(val)
+        # Debug (compiled with -D_GLIBCXX_DEBUG) printer
+        # registrations.  The Rb_tree debug iterator when unwrapped
+        # from the encapsulating __gnu_debug::_Safe_iterator does not
+        # have the __norm namespace. Just use the existing printer
+        # registration for that.
+        libstdcxx_printer.add('__gnu_debug::_Safe_iterator',
+                              StdDebugIteratorPrinter)
+        libstdcxx_printer.add('std::__norm::_List_iterator',
+                              StdListIteratorPrinter)
+        libstdcxx_printer.add('std::__norm::_List_const_iterator',
+                              StdListIteratorPrinter)
+        libstdcxx_printer.add('std::__norm::_Deque_const_iterator',
+                              StdDequeIteratorPrinter)
+        libstdcxx_printer.add('std::__norm::_Deque_iterator',
+                              StdDequeIteratorPrinter)
 
-pretty_printers_dict = {}
-
 build_libstdcxx_dictionary ()