summaryrefslogtreecommitdiff
path: root/tools/dev/gdb-py/svndbg/printers.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dev/gdb-py/svndbg/printers.py')
-rw-r--r--tools/dev/gdb-py/svndbg/printers.py417
1 files changed, 417 insertions, 0 deletions
diff --git a/tools/dev/gdb-py/svndbg/printers.py b/tools/dev/gdb-py/svndbg/printers.py
new file mode 100644
index 0000000..da041b4
--- /dev/null
+++ b/tools/dev/gdb-py/svndbg/printers.py
@@ -0,0 +1,417 @@
+#!/usr/bin/env python
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+import gdb
+import re
+
+import gdb.printing
+from gdb.printing import RegexpCollectionPrettyPrinter
+
+
+class TypedefRegexCollectionPrettyPrinter(RegexpCollectionPrettyPrinter):
+ """Class for implementing a collection of pretty-printers, matching the
+ type name to a regular expression.
+
+ A pretty-printer in this collection will be used if the type of the
+ value to be printed matches the printer's regular expression, or if
+ the value is a pointer to and/or typedef to a type name that matches
+ its regular expression. The variations are tried in this order:
+
+ 1. the type name as known to the debugger (could be a 'typedef');
+ 2. the type after stripping off any number of layers of 'typedef';
+ 3. if it is a pointer, the pointed-to type;
+ 4. if it is a pointer, the pointed-to type minus some 'typedef's.
+
+ In all cases, ignore 'const' and 'volatile' qualifiers. When
+ matching the pointed-to type, dereference the value or use 'None' if
+ the value was a null pointer.
+
+ This class 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):
+ """Find and return an instantiation of a printer for VAL.
+ """
+
+ def lookup_type(type, val):
+ """Return the first printer whose regular expression matches the
+ name (tag name for struct/union/enum types) of TYPE, ignoring
+ any 'const' or 'volatile' qualifiers.
+
+ VAL is a gdb.Value, or may be None to indicate a dereferenced
+ null pointer. TYPE is the associated gdb.Type.
+ """
+ if type.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION,
+ gdb.TYPE_CODE_ENUM]:
+ typename = type.tag
+ else:
+ typename = str(type.unqualified())
+ for printer in self.subprinters:
+ if printer.enabled and printer.compiled_re.search(typename):
+ return printer.gen_printer(val)
+
+ def lookup_type_or_alias(type, val):
+ """Return the first printer matching TYPE, or else if TYPE is a
+ typedef then the first printer matching the aliased type.
+
+ VAL is a gdb.Value, or may be None to indicate a dereferenced
+ null pointer. TYPE is the associated gdb.Type.
+ """
+ # First, look for a printer for the given (but unqualified) type.
+ printer = lookup_type(type, val)
+ if printer:
+ return printer
+
+ # If it's a typedef, look for a printer for the aliased type ...
+ while type.code == gdb.TYPE_CODE_TYPEDEF:
+ type = type.target()
+ printer = lookup_type(type, val)
+ if printer:
+ return printer
+
+ # First, look for a printer for the given (but unqualified) type, or
+ # its aliased type if it's a typedef.
+ printer = lookup_type_or_alias(val.type, val)
+ if printer:
+ return printer
+
+ # If it's a pointer, look for a printer for the pointed-to type.
+ if val.type.code == gdb.TYPE_CODE_PTR:
+ type = val.type.target()
+ printer = lookup_type_or_alias(
+ type, val and val.dereference() or None)
+ if printer:
+ return printer
+
+ # Cannot find a matching pretty printer in this collection.
+ 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 'map'.
+ 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):
+ if val:
+ self.hash_p = val.address
+ else:
+ self.hash_p = val
+
+ def to_string(self):
+ """Return a string to be displayed before children are displayed, or
+ return None if we don't want any such.
+ """
+ if not self.hash_p:
+ return 'NULL'
+ 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'
+
+def children_of_apr_array(array, value_type):
+ """Iterate over an 'apr_array_header_t' GDB value, in the way required for
+ a pretty-printer 'children' method when the display-hint is 'array'.
+ Cast the values to VALUE_TYPE.
+ """
+ nelts = int(array['nelts'])
+ elts = array['elts'].reinterpret_cast(value_type.pointer())
+ for i in range(nelts):
+ yield str(i), elts[i]
+
+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'
+
+########################################################################
+
+# Pretty-printing for Subversion libsvn_subr types.
+
+class SvnBooleanPrinter:
+ """for svn_boolean_t"""
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ if self.val is None:
+ return '(NULL)'
+ if self.val:
+ return 'TRUE'
+ else:
+ return 'FALSE'
+
+class SvnStringPrinter:
+ """for svn_string_t"""
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ if not self.val:
+ return 'NULL'
+
+ data = self.val['data']
+ len = int(self.val['len'])
+ return data.string(length=len)
+
+ def display_hint(self):
+ if self.val:
+ return 'string'
+
+class SvnMergeRangePrinter:
+ """for svn_merge_range_t"""
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ if not self.val:
+ return 'NULL'
+
+ r = self.val
+ start = int(r['start'])
+ end = int(r['end'])
+ if start >= 0 and start < end:
+ if start + 1 == end:
+ rs = str(end)
+ else:
+ rs = str(start + 1) + '-' + str(end)
+ elif end >= 0 and end < start:
+ if start == end + 1:
+ rs = '-' + str(start)
+ else:
+ rs = str(start) + '-' + str(end + 1)
+ else:
+ rs = '(INVALID: s=%d, e=%d)' % (start, end)
+ if not r['inheritable']:
+ rs += '*'
+ return rs
+
+ def display_hint(self):
+ if self.val:
+ return 'string'
+
+class SvnRangelistPrinter:
+ """for svn_rangelist_t"""
+ def __init__(self, val):
+ self.array = val
+ self.svn_merge_range_t = gdb.lookup_type('svn_merge_range_t')
+
+ def to_string(self):
+ if not self.array:
+ return 'NULL'
+
+ s = ''
+ for key, val in children_of_apr_array(self.array,
+ self.svn_merge_range_t.pointer()):
+ if s:
+ s += ','
+ s += SvnMergeRangePrinter(val).to_string()
+ return s
+
+ def display_hint(self):
+ if self.array:
+ return 'string'
+
+class SvnMergeinfoPrinter:
+ """for svn_mergeinfo_t"""
+ def __init__(self, val):
+ self.hash_p = val
+ self.svn_rangelist_t = gdb.lookup_type('svn_rangelist_t')
+
+ def to_string(self):
+ if self.hash_p == 0:
+ return 'NULL'
+
+ s = ''
+ for key, val in children_of_apr_hash(self.hash_p,
+ self.svn_rangelist_t.pointer()):
+ if s:
+ s += '; '
+ s += key + ':' + SvnRangelistPrinter(val).to_string()
+ return '{ ' + s + ' }'
+
+class SvnMergeinfoCatalogPrinter:
+ """for svn_mergeinfo_catalog_t"""
+ def __init__(self, val):
+ self.hash_p = val
+ self.svn_mergeinfo_t = gdb.lookup_type('svn_mergeinfo_t')
+
+ def to_string(self):
+ if self.hash_p == 0:
+ return 'NULL'
+
+ s = ''
+ for key, val in children_of_apr_hash(self.hash_p,
+ self.svn_mergeinfo_t):
+ if s:
+ s += ',\n '
+ s += "'" + key + "': " + SvnMergeinfoPrinter(val).to_string()
+ return '{ ' + s + ' }'
+
+########################################################################
+
+# Pretty-printing for Subversion libsvn_client types.
+
+class SvnPathrevPrinter:
+ """for svn_client__pathrev_t"""
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ if not self.val:
+ return 'NULL'
+
+ rev = int(self.val['rev'])
+ url = self.val['url'].string()
+ repos_root_url = self.val['repos_root_url'].string()
+ relpath = url[len(repos_root_url):]
+ return "%s@%d" % (relpath, rev)
+
+ def display_hint(self):
+ if self.val:
+ return 'string'
+
+
+########################################################################
+
+libapr_printer = None
+libsvn_printer = None
+
+def build_libsvn_printers():
+ """Construct the pretty-printer objects."""
+
+ global libapr_printer, libsvn_printer
+
+ libapr_printer = TypedefRegexCollectionPrettyPrinter("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)
+
+ libsvn_printer = TypedefRegexCollectionPrettyPrinter("libsvn")
+ libsvn_printer.add_printer('svn_boolean_t', r'^svn_boolean_t$',
+ SvnBooleanPrinter)
+ libsvn_printer.add_printer('svn_string_t', r'^svn_string_t$',
+ SvnStringPrinter)
+ libsvn_printer.add_printer('svn_client__pathrev_t', r'^svn_client__pathrev_t$',
+ SvnPathrevPrinter)
+ libsvn_printer.add_printer('svn_merge_range_t', r'^svn_merge_range_t$',
+ SvnMergeRangePrinter)
+ libsvn_printer.add_printer('svn_rangelist_t', r'^svn_rangelist_t$',
+ SvnRangelistPrinter)
+ libsvn_printer.add_printer('svn_mergeinfo_t', r'^svn_mergeinfo_t$',
+ SvnMergeinfoPrinter)
+ libsvn_printer.add_printer('svn_mergeinfo_catalog_t', r'^svn_mergeinfo_catalog_t$',
+ SvnMergeinfoCatalogPrinter)
+
+
+def register_libsvn_printers(obj):
+ """Register the pretty-printers for the object file OBJ."""
+
+ global libapr_printer, libsvn_printer
+
+ # Printers registered later take precedence.
+ gdb.printing.register_pretty_printer(obj, libapr_printer)
+ gdb.printing.register_pretty_printer(obj, 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()