tools/dev/gdb-py/svndbg/printers.py (239 lines of code) (raw):
#!/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')
apr_hash_this_key = InferiorFunction('apr_hash_this_key')
apr_hash_this_val = InferiorFunction('apr_hash_this_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 = apr_hash_this_key(hi).reinterpret_cast(cstringType)
if value_type:
val = apr_hash_this_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()