diff options
Diffstat (limited to 'tools/dev/gdb-py/svndbg/printers.py')
-rw-r--r-- | tools/dev/gdb-py/svndbg/printers.py | 417 |
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() |