diff mbox

FYI: update libstdc++ pretty-printers

Message ID m3lj271h09.fsf@fleche.redhat.com
State New
Headers show

Commit Message

Tom Tromey Jan. 26, 2011, 9:39 p.m. UTC
Barring objections, I will check this in during the next stage 1.

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

This patch updates the libstdc++ printers to work this way.  The change
is done in a backward-compatible way, so that they will continue to work
properly in older versions of gdb.

This patch also simplifies the printers somewhat, and probably speeds up
printer recognition as well, though I did not try to measure that.

Tom

2011-01-26  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.

Comments

Benjamin Kosnik Jan. 27, 2011, 5:32 p.m. UTC | #1
> Doug Evans recently added some features to gdb to make it possible for
> the user to individually enable or disable pretty-printers.  In order
> for this to work, the printers must conform to a certain extended
> protocol.

By this do you mean type-specific enable/disable? Ie, enable
std::string prettyprinter, and have std::list disabled? 

If so, way cool, can you give example syntax that does it?

> This patch updates the libstdc++ printers to work this way.  The
> change is done in a backward-compatible way, so that they will
> continue to work properly in older versions of gdb.
> 
> This patch also simplifies the printers somewhat, and probably speeds
> up printer recognition as well, though I did not try to measure that.

Nice job on the simplification. I find the new version easier to read,
FWIW. Registration seems much simpler.

-benjamin
Tom Tromey Jan. 27, 2011, 7:13 p.m. UTC | #2
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.

Benjamin> By this do you mean type-specific enable/disable? Ie, enable
Benjamin> std::string prettyprinter, and have std::list disabled? 

Yeah.

Benjamin> If so, way cool, can you give example syntax that does it?

Sure.  You need to know the name of the printers; but with the new code,
each printer is just named after the class it prints.  You can see all
of them with "info pretty-printers".

Then you can do something like:

(gdb) disable pretty-printer .* .*:std::list
1 printer disabled
55 of 56 printers enabled

The syntax is a little funny, since you supply regular expressions to
match against the objfile (executable or shared library) name, the
printer's "category" (I'm not sure if there is an official name for
this, but in this case it is "libstdc++-v6"), and the printer itself.

Tom
diff mbox

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,186 @@ 
     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):
+        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
+
+        if match.group(1) in self.lookup:
+            return self.lookup[match.group(1)].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 ()