diff --git a/.gdb.py b/.gdb.py index f4054519f5f27..4589d9f92a6ec 100644 --- a/.gdb.py +++ b/.gdb.py @@ -5,9 +5,15 @@ source .gdb.py -Use +If needed, pretty printers can be by-passed by using the /r flag: (gdb) p /r any_variable -to print |any_variable| without using any printers. + +Use |set print pretty| to enable multi-line printing and indentation: + (gdb) set print pretty on + +Use |set print max-depth| to control the maximum print depth for nested +structures: + (gdb) set print pretty on To interactively type Python for development of the printers, use (gdb) python foo = gdb.parse_and_eval('bar') @@ -32,7 +38,10 @@ def to_string(self): def children(self): for field in self.val.type.fields(): - yield (field.name, self.val[field.name]) + if field.name == 'val': + yield ('val', self.format_string()) + else: + yield (field.name, self.val[field.name]) def format_string(self): len = int(self.val['len']) @@ -49,6 +58,8 @@ def format_string(self): str += ' (%d bytes total)' % int(self.val['len']) return str + + pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter) class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter): @@ -56,7 +67,7 @@ class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter): def __init__(self, val): self.val = val - self.load_bits() + load_type_bits() def to_string(self): return self.format_type(self.val) @@ -73,7 +84,7 @@ def format_type(self, t): meta = [] for bit in range(0, type_mask_size): if type_mask & (1 << bit): - type_name = self.bits.get(bit) + type_name = type_bit_to_name.get(bit) match type_name: case None: parts.append('(1<<%d)' % bit) @@ -104,34 +115,225 @@ def format_type(self, t): return str - def load_bits(self): - (symbol,_) = gdb.lookup_symbol("zend_gc_refcount") - if symbol == None: - raise "Could not find zend_types.h: symbol zend_gc_refcount not found" - filename = symbol.symtab.fullname() - bits = {} +pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter) + +class ZendAstKindPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_ast_kind" + + def __init__(self, val): + self.val = val + + def to_string(self): + return self.val.cast(gdb.lookup_type('enum _zend_ast_kind')) + + +pp_set.add_printer('zend_ast_kind', '^zend_ast_kind$', ZendAstKindPrettyPrinter) + +class ZendAstPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_ast" + + def __init__(self, val): + self.val = val + + def to_string(self): + return '((%s*)0x%x)' % (str(self.cast().type), self.val.address) + + def children(self): + val = self.cast() + for field in val.type.fields(): + if field.name == 'child': + children = val[field.name] + num_children = self.num_children() + + ptr_type = gdb.lookup_type('zend_ast').pointer().pointer() + children = children.cast(ptr_type) + + for i in range(0, num_children): + c = children[i] + if int(c) != 0: + c = c.dereference() + yield ('child[%d]' % i, c) + elif field.name == 'name': + yield (field.name, ZendStringPrettyPrinter(val[field.name].dereference()).to_string()) + elif field.name == 'val': + yield (field.name, ZvalPrettyPrinter(val[field.name]).to_string()) + else: + yield (field.name, val[field.name]) + + def is_special(self): + special_shift = 6 # ZEND_AST_SPECIAL_SHIFT + kind = self.val['kind'] + return (kind >> special_shift) & 1 + + def is_decl(self): + return self.is_special() and int(self.val['kind']) >= enum_value('ZEND_AST_FUNC_DECL') + + def is_list(self): + list_shift = 7 # ZEND_AST_IS_LIST_SHIFT + kind = self.val['kind'] + return (kind >> list_shift) & 1 - with open(filename, 'r') as file: - content = file.read() + def cast(self): + kind = int(self.val['kind']) - pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)') - matches = pattern.findall(content) - for name, bit in matches: + if kind == enum_value('ZEND_AST_ZVAL') or kind == enum_value('ZEND_AST_CONSTANT'): + return self.val.cast(gdb.lookup_type('zend_ast_zval')) + if kind == enum_value('ZEND_AST_ZNODE'): + return self.val.cast(gdb.lookup_type('zend_ast_znode')) + if self.is_decl(): + return self.val.cast(gdb.lookup_type('zend_ast_decl')) + if self.is_list(): + return self.val.cast(gdb.lookup_type('zend_ast_list')) + + return self.val + + def num_children(self): + if self.is_decl(): + decl_type = gdb.lookup_type('zend_ast_decl') + child_type = decl_type['child'].type + return array_size(child_type) + if self.is_special(): + return 0 + elif self.is_list(): + return int(self.cast()['children']) + else: + num_children_shift = 8 # ZEND_AST_NUM_CHILDREN_SHIFT + kind = self.val['kind'] + return kind >> num_children_shift + + +pp_set.add_printer('zend_ast', '^_zend_ast$', ZendAstPrettyPrinter) + +class ZvalPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zval" + + def __init__(self, val): + self.val = val + load_type_bits() + + def to_string(self): + return self.value_to_string() + + def value_to_string(self): + t = int(self.val['u1']['v']['type']) + if t == type_name_to_bit['undef']: + return 'undef' + elif t == type_name_to_bit['null']: + return 'null' + elif t == type_name_to_bit['false']: + return 'false' + elif t == type_name_to_bit['true']: + return 'true' + elif t == type_name_to_bit['long']: + return str(self.val['value']['lval']) + elif t == type_name_to_bit['double']: + return str(self.val['value']['dval']) + elif t == type_name_to_bit['string']: + return ZendStringPrettyPrinter(self.val['value']['str'].dereference()).to_string() + elif t == type_name_to_bit['array']: + return 'array' + elif t == type_name_to_bit['object']: + return 'object(%s)' % ZendStringPrettyPrinter(self.val['value']['obj']['ce']['name'].dereference()).to_string() + elif t == type_name_to_bit['resource']: + return 'resource' + elif t == type_name_to_bit['reference']: + return 'reference' + elif t == type_name_to_bit['constant_ast']: + return 'constant_ast' + else: + return 'zval of type %d' % int(self.val['u1']['v']['type']) + + def children(self): + for field in self.val.type.fields(): + if field.name == 'value': + value = self.val['value'] + t = int(self.val['u1']['v']['type']) + if t == type_name_to_bit['undef']: + value = value['lval'] + elif t == type_name_to_bit['null']: + value = value['lval'] + elif t == type_name_to_bit['false']: + value = value['lval'] + elif t == type_name_to_bit['true']: + value = value['lval'] + elif t == type_name_to_bit['long']: + value = value['lval'] + elif t == type_name_to_bit['double']: + value = value['dval'] + elif t == type_name_to_bit['string']: + value = value['str'].dereference() + elif t == type_name_to_bit['array']: + value = value['ht'].dereference() + elif t == type_name_to_bit['object']: + value = value['obj'].dereference() + elif t == type_name_to_bit['resource']: + value = value['res'].dereference() + elif t == type_name_to_bit['reference']: + value = value['ref'].dereference() + elif t == type_name_to_bit['constant_ast']: + value = value['ast'].dereference() + else: + value = value['ptr'] + yield (field.name, value) + elif field.name == 'u2': + yield ('u2', self.val[field.name]['extra']) + else: + yield (field.name, self.val[field.name]) + + +pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter) + +type_bit_to_name = None +type_name_to_bit = None + +def load_type_bits(): + global type_bit_to_name + global type_name_to_bit + + if type_bit_to_name != None: + return + + (symbol,_) = gdb.lookup_symbol("zend_gc_refcount") + if symbol == None: + raise "Could not find zend_types.h: symbol zend_gc_refcount not found" + filename = symbol.symtab.fullname() + + bits = {} + + with open(filename, 'r') as file: + content = file.read() + + pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)') + matches = pattern.findall(content) + for name, bit in matches: + bits[int(bit)] = name.lower() + + pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)') + matches = pattern.findall(content) + for name, bit in matches: + if not int(bit) in bits: bits[int(bit)] = name.lower() - pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)') - matches = pattern.findall(content) - for name, bit in matches: - if not int(bit) in bits: - bits[int(bit)] = name.lower() + types = {} + for bit in bits: + types[bits[bit]] = bit - types = {} - for bit in bits: - types[bits[bit]] = bit + type_bit_to_name = bits + type_name_to_bit = types - self.bits = bits - self.types = types -pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter) +def lookup_symbol(name): + (symbol, _) = gdb.lookup_symbol(name) + if symbol == None: + raise Exception("Could not lookup symbol %s" % name) + return symbol + +def enum_value(name): + symbol = lookup_symbol(name) + return int(symbol.value()) + +def array_size(ary_type): + # array types have a single field whose type represents its range + return ary_type.fields()[0].type.range()[1]+1 gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)