#!/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 = '' 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()