You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/03/29 17:02:46 UTC

svn commit: r1306891 - /subversion/trunk/tools/dev/gdb-py/svndbg/printers.py

Author: julianfoad
Date: Thu Mar 29 15:02:45 2012
New Revision: 1306891

URL: http://svn.apache.org/viewvc?rev=1306891&view=rev
Log:
Expand the support for GDB to pretty-print Subversion data structures.

Building on Hyrum's work, this enables GDB to automatically print a
human-readable representation of a few more data structures.  The most
significant change here is the introduction of matching on the type name as
it appears at the point of use, such as 'svn_mergeinfo_catalog_t'.
Previously we could only match on the structure tag name after stripping
away typedefs, so a 'svn_mergeinfo_catalog_t' (defined as 'apr_hash_t *'
could only have been printed as an 'apr_hash_t'.  Now, however, we will
print just the keys of a plain apr_hash_t, but both the keys and the values
(which are nested hashes) of svn_mergeinfo_catalog_t.

* tools/dev/gdb-py/svndbg/printers.py
  (TypedefRegexCollectionPrettyPrinter, InferiorFunction, children_as_map):
    New classes and function for general pretty-printing.
  (children_of_apr_hash, AprHashPrinter, PtrAprHashPrinter, AprArrayPrinter,
   PtrAprArrayPrinter): New classes and functions for pretty-printing APR
    types.
  (SvnMergeinfoCatalogPrinter): New class for pretty-printing a Subversion
    type.
  (build_libsvn_printer, register_libsvn_printers): Initialize and register
    the new printers. Add some commentary.

Modified:
    subversion/trunk/tools/dev/gdb-py/svndbg/printers.py

Modified: subversion/trunk/tools/dev/gdb-py/svndbg/printers.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/dev/gdb-py/svndbg/printers.py?rev=1306891&r1=1306890&r2=1306891&view=diff
==============================================================================
--- subversion/trunk/tools/dev/gdb-py/svndbg/printers.py (original)
+++ subversion/trunk/tools/dev/gdb-py/svndbg/printers.py Thu Mar 29 15:02:45 2012
@@ -24,8 +24,168 @@ import gdb
 import re
 
 import gdb.printing
+from gdb.printing import RegexpCollectionPrettyPrinter
 
 
+class TypedefRegexCollectionPrettyPrinter(RegexpCollectionPrettyPrinter):
+    """Class for implementing a collection of regular-expression based
+       pretty-printers, matching on the type name at the point of use, such
+       as (but not necessarily) a 'typedef' name, ignoring 'const' or
+       'volatile' qualifiers.
+
+       This is modeled on RegexpCollectionPrettyPrinter, which (in GDB 7.3)
+       matches on the base type's tag name and can't match a pointer type or
+       any other type that doesn't have a tag name."""
+
+    def __init__(self, name):
+        super(TypedefRegexCollectionPrettyPrinter, self).__init__(name)
+
+    def __call__(self, val):
+        """Lookup the pretty-printer for the provided value."""
+
+        # Get the type name, without 'const' or 'volatile' qualifiers.
+        typename = str(val.type.unqualified())
+        if not typename:
+            return None
+
+        # Iterate over table of type regexps to find an enabled printer for
+        # that type.  Return an instantiation of the printer if found.
+        for printer in self.subprinters:
+            if printer.enabled and printer.compiled_re.search(typename):
+                return printer.gen_printer(val)
+
+        # Cannot find a pretty printer.  Return None.
+        return None
+
+class InferiorFunction:
+    """A class whose instances are callable functions on the inferior
+       process."""
+    def __init__(self, function_name):
+        self.function_name = function_name
+        self.func = None
+
+    def __call__(self, *args):
+        if not self.func:
+            self.func = gdb.parse_and_eval(self.function_name)
+        return self.func(*args)
+
+def children_as_map(children_iterator):
+    """Convert an iteration of (key, value) pairs into the form required for
+       a pretty-printer 'children' method when the display-hint is 'map'."""
+    for k, v in children_iterator:
+        yield 'key', k
+        yield 'val', v
+
+
+########################################################################
+
+# Pretty-printing for APR library types.
+
+# Some useful gdb.Type instances that can be initialized before any object
+# files are loaded.
+pvoidType = gdb.lookup_type('void').pointer()
+cstringType = gdb.lookup_type('char').pointer()
+
+# Some functions that resolve to calls into the inferior process.
+apr_hash_count = InferiorFunction('apr_hash_count')
+apr_hash_first = InferiorFunction('apr_hash_first')
+apr_hash_next = InferiorFunction('apr_hash_next')
+svn__apr_hash_index_key = InferiorFunction('svn__apr_hash_index_key')
+svn__apr_hash_index_val = InferiorFunction('svn__apr_hash_index_val')
+
+def children_of_apr_hash(hash_p, value_type=None):
+    """Iterate over an 'apr_hash_t *' GDB value, in the way required for a
+       pretty-printer 'children' method when the display-hint is 'array'.
+       Cast the value pointers to VALUE_TYPE, or return values as '...' if
+       VALUE_TYPE is None."""
+    hi = apr_hash_first(0, hash_p)
+    while (hi):
+        k = svn__apr_hash_index_key(hi).reinterpret_cast(cstringType)
+        if value_type:
+            val = svn__apr_hash_index_val(hi).reinterpret_cast(value_type)
+        else:
+            val = '...'
+        try:
+            key = k.string()
+        except:
+            key = '<unreadable>'
+        yield key, val
+        hi = apr_hash_next(hi)
+
+class AprHashPrinter:
+    """for 'apr_hash_t' of 'char *' keys and unknown values"""
+    def __init__(self, val):
+        self.hash_p = val.address
+
+    def to_string(self):
+        """Return a string to be displayed before children are displayed, or
+           return None if we don't want any such."""
+        return 'hash of ' + str(apr_hash_count(self.hash_p)) + ' items'
+
+    def children(self):
+        if not self.hash_p:
+            return []
+        return children_as_map(children_of_apr_hash(self.hash_p))
+
+    def display_hint(self):
+        return 'map'
+
+class PtrAprHashPrinter(AprHashPrinter):
+    """for pointer to 'apr_hash_t' of 'char *' keys and unknown values"""
+    def __init__(self, val):
+        self.hash_p = val
+
+    def to_string(self):
+        if not self.hash_p:
+            return 'NULL'
+        return AprHashPrinter.to_string(self)
+
+    def children(self):
+        if not self.hash_p:
+            return []
+        return AprHashPrinter.children(self)
+
+class AprArrayPrinter:
+    """for 'apr_array_header_t' of unknown elements"""
+    def __init__(self, val):
+        self.array = val
+
+    def to_string(self):
+        if not self.array:
+            return 'NULL'
+        nelts = self.array['nelts']
+        return 'array of ' + str(int(nelts)) + ' items'
+
+    def children(self):
+        # We can't display the children as we don't know their type.
+        return []
+
+    def display_hint(self):
+        return 'array'
+
+class PtrAprArrayPrinter(AprArrayPrinter):
+    """for pointer to 'apr_array_header_t' of unknown elements"""
+    def __init__(self, val):
+        if not val:
+            self.array = None
+        else:
+            self.array = val.dereference()
+
+    def to_string(self):
+        if not self.array:
+            return 'NULL'
+        return AprArrayPrinter.to_string(self)
+
+    def children(self):
+        if not self.array:
+            return []
+        return AprArrayPrinter.children(self)
+
+
+########################################################################
+
+# Pretty-printing for Subversion library types.
+
 class SvnStringPrinter:
     def __init__(self, val):
         self.val = val
@@ -42,22 +202,86 @@ class SvnStringPrinter:
     def display_hint(self):
         return 'string'
 
+class SvnMergeinfoCatalogPrinter:
+    """for svn_mergeinfo_catalog_t"""
+    def __init__(self, val):
+        self.hash_p = val
+
+    def to_string(self):
+        if self.hash_p == 0:
+            return 'NULL'
+        return 'mergeinfo catalog of ' + str(apr_hash_count(self.hash_p)) + ' items'
+
+    def children(self):
+        if self.hash_p == 0:
+            return None
+        mergeinfoType = gdb.lookup_type('svn_mergeinfo_t')
+        return children_as_map(children_of_apr_hash(self.hash_p, mergeinfoType))
+
+    def display_hint(self):
+        return 'map'
 
-def build_libsvn_printer():
-    global libsvn_printer
 
-    libsvn_printer = gdb.printing.RegexpCollectionPrettyPrinter("libsvn")
+########################################################################
+
+libapr_printer = None
+libapr_printer2 = None
+libsvn_printer = None
+libsvn_printer2 = None
 
-    libsvn_printer.add_printer('svn_string_t', '^svn_string_t$',
+def build_libsvn_printers():
+    """Construct the pretty-printer objects."""
+
+    global libapr_printer, libapr_printer2, libsvn_printer, libsvn_printer2
+
+    # These sub-printers match a struct's (or union)'s tag name,
+    # after stripping typedefs, references and const/volatile qualifiers.
+    libapr_printer = RegexpCollectionPrettyPrinter("libapr")
+    libapr_printer.add_printer('apr_hash_t', r'^apr_hash_t$',
+                               AprHashPrinter)
+    libapr_printer.add_printer('apr_array_header_t', r'^apr_array_header_t$',
+                               AprArrayPrinter)
+
+    # These sub-printers match a type name at the point of use,
+    # after stripping const/volatile qualifiers.
+    #
+    # TODO: The "apr_foo_t *" entries are in this collection merely because
+    #       the collection above can't match them, but ideally we'd fix that
+    #       matching and move these entries to there so that they get used
+    #       for any typedef that doesn't have its own specific pretty-printer
+    #       registered.
+    libapr_printer2 = TypedefRegexCollectionPrettyPrinter("libapr2")
+    libapr_printer2.add_printer('apr_hash_t *', r'^apr_hash_t \*$',
+                                PtrAprHashPrinter)
+    libapr_printer2.add_printer('apr_array_header_t *', r'^apr_array_header_t \*$',
+                                PtrAprArrayPrinter)
+
+    # These sub-printers match a struct's (or union)'s tag name,
+    # after stripping typedefs, references and const/volatile qualifiers.
+    libsvn_printer = RegexpCollectionPrettyPrinter("libsvn")
+    libsvn_printer.add_printer('svn_string_t', r'^svn_string_t$',
                                SvnStringPrinter)
 
+    # These sub-printers match a type name at the point of use,
+    # after stripping const/volatile qualifiers.
+    libsvn_printer2 = TypedefRegexCollectionPrettyPrinter("libsvn2")
+    libsvn_printer2.add_printer('svn_mergeinfo_catalog_t', r'^svn_mergeinfo_catalog_t$',
+                                SvnMergeinfoCatalogPrinter)
 
-libsvn_printer = None
 
 def register_libsvn_printers(obj):
-    global libsvn_printer
+    """Register the pretty-printers for the object file OBJ."""
+
+    global libapr_printer, libapr_printer2, libsvn_printer, libsvn_printer2
 
+    # Printers registered later take precedence.
+    gdb.printing.register_pretty_printer(obj, libapr_printer)
+    gdb.printing.register_pretty_printer(obj, libapr_printer2)
     gdb.printing.register_pretty_printer(obj, libsvn_printer)
+    gdb.printing.register_pretty_printer(obj, libsvn_printer2)
 
 
-build_libsvn_printer()
+# Construct the pretty-printer objects, once, at GDB start-up time when this
+# Python module is loaded.  (Registration happens later, once per object
+# file.)
+build_libsvn_printers()