GDB script improvements (#17227)

* Support IS_INDIRECT, IS_PTR, IS_ALIAS_PTR when printing zvals
* Pretty print the ce_flags field when pretty printing a zend_class_entry
* Pretty print the const flags when pretty printing a zend_class_constant
* Added a minimal zend_property_info pretty printer to pretty print the flags field
* Added minimal zend_function, zend_op_array, and zend_internal_function pretty printers: The string value is the function/method name, function type (user/internal), and location (when possible). The fn_flags field is pretty printed
* Added a minimal zend_op pretty printer to pretty print the opcode
* Added a zend_refcounted_h pretty printer to pretty print the type_info
* Added minimal pretty printers for zend_object and zend_array
* Print the type and address of pretty-printed structs
* Added gdb commands: print_fn_flags, print_ce_flags, print_prop_flags, print_const_flags, print_ref_type_info, print_opcode, dump_op_array
* Fields of type zend_string, zend_class_entry, zend_array are printed by default in all our pretty printers, using short representation
* Increased the maximum length of printed strings to 200 chars before truncation (was 50)
This commit is contained in:
Arnaud Le Blanc 2025-01-30 12:22:07 +01:00 committed by GitHub
parent fa7c67d622
commit 9df1c930bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1462 additions and 242 deletions

View file

@ -144,7 +144,7 @@ asm(
".ascii \"\\n\"\n"
".ascii \" printf \\\") \\\"\\n\"\n"
".ascii \" else\\n\"\n"
".ascii \" printf \\\"??? \\\"\\n\"\n"
".ascii \" printf \\\"\?\?\? \\\"\\n\"\n"
".ascii \" end\\n\"\n"
".ascii \" if $func != 0\\n\"\n"
".ascii \" if $func->type == 2\\n\"\n"
@ -688,7 +688,7 @@ asm(
".ascii \"\\n\"\n"
".ascii \"Use |set print max-depth| to control the maximum print depth for nested\\n\"\n"
".ascii \"structures:\\n\"\n"
".ascii \" (gdb) set print pretty on\\n\"\n"
".ascii \" (gdb) set print max-depth 5\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"To interactively type Python for development of the printers, use\\n\"\n"
".ascii \" (gdb) python foo = gdb.parse_and_eval('bar')\\n\"\n"
@ -699,6 +699,8 @@ asm(
".ascii \"\\n\"\n"
".ascii \"import gdb\\n\"\n"
".ascii \"import re\\n\"\n"
".ascii \"import traceback\\n\"\n"
".ascii \"import os\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set = gdb.printing.RegexpCollectionPrettyPrinter(\\\"php\\\")\\n\"\n"
".ascii \"\\n\"\n"
@ -709,53 +711,65 @@ asm(
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return self.format_string()\\n\"\n"
".ascii \" return '((zend_string*) 0x%x) %s' % (self.val.address, format_zstr(self.val))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'val':\\n\"\n"
".ascii \" yield ('val', self.format_string())\\n\"\n"
".ascii \" yield ('val', format_zstr(self.val))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def format_string(self):\\n\"\n"
".ascii \" len = int(self.val['len'])\\n\"\n"
".ascii \" truncated = False\\n\"\n"
".ascii \" if len > 50:\\n\"\n"
".ascii \" len = 50\\n\"\n"
".ascii \" truncated = True\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" ptr_type = gdb.lookup_type('char').pointer()\\n\"\n"
".ascii \" ary_type = gdb.lookup_type('char').array(len)\\n\"\n"
".ascii \" str = self.val['val'].cast(ptr_type).dereference().cast(ary_type)\\n\"\n"
".ascii \" str = str.format_string()\\n\"\n"
".ascii \" if truncated:\\n\"\n"
".ascii \" str += ' (%d bytes total)' % int(self.val['len'])\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return str\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def zendStringPointerPrinter(ptr):\\n\"\n"
".ascii \" \\\"Given a pointer to a zend_string, show the contents (if non-NULL)\\\"\\n\"\n"
".ascii \" if int(ptr) == 0:\\n\"\n"
".ascii \" return '0x0'\\n\"\n"
".ascii \" return ZendStringPrettyPrinter(ptr.dereference()).to_string()\\n\"\n"
".ascii \"class ZendObjectPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_object\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return 'object(%s)' % format_zstr(self.val['ce']['name'])\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_object', '^_zend_object$', ZendObjectPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendArrayPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_array\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return 'array(%d)' % self.val['nNumOfElements']\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name is None:\\n\"\n"
".ascii \" name = '<anonymous>'\\n\"\n"
".ascii \" val = self.val[field]\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" name = field.name\\n\"\n"
".ascii \" val = self.val[field.name]\\n\"\n"
".ascii \" yield (name, format_nested(val))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_array', '^_zend_array$', ZendArrayPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_type\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" load_type_bits()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return self.format_type(self.val)\\n\"\n"
".ascii \" return '((zend_type*) 0x%x) %s' % (self.val.address, self.format_type(self.val))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def format_type(self, t):\\n\"\n"
".ascii \" type_mask = int(t['type_mask'])\\n\"\n"
@ -765,7 +779,7 @@ asm(
".ascii \" meta = []\\n\"\n"
".ascii \" for bit in range(0, type_mask_size):\\n\"\n"
".ascii \" if type_mask & (1 << bit):\\n\"\n"
".ascii \" type_name = type_bit_to_name.get(bit)\\n\"\n"
".ascii \" type_name = ZendTypeBits.zendTypeName(bit)\\n\"\n"
".ascii \" match type_name:\\n\"\n"
".ascii \" case None:\\n\"\n"
".ascii \" parts.append('(1<<%d)' % bit)\\n\"\n"
@ -785,7 +799,7 @@ asm(
".ascii \" separator = '&'\\n\"\n"
".ascii \" case 'name':\\n\"\n"
".ascii \" str = t['ptr'].cast(gdb.lookup_type('zend_string').pointer())\\n\"\n"
".ascii \" parts.append(ZendStringPrettyPrinter(str).to_string())\\n\"\n"
".ascii \" parts.append(format_zstr(str))\\n\"\n"
".ascii \" case _:\\n\"\n"
".ascii \" parts.append(type_name)\\n\"\n"
".ascii \"\\n\"\n"
@ -836,11 +850,11 @@ asm(
".ascii \" c = c.dereference()\\n\"\n"
".ascii \" yield ('child[%d]' % i, c)\\n\"\n"
".ascii \" elif field.name == 'name':\\n\"\n"
".ascii \" yield (field.name, ZendStringPrettyPrinter(val[field.name].dereference()).to_string())\\n\"\n"
".ascii \" yield (field.name, format_zstr(val[field.name]))\\n\"\n"
".ascii \" elif field.name == 'val':\\n\"\n"
".ascii \" yield (field.name, ZvalPrettyPrinter(val[field.name]).to_string())\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, val[field.name])\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def is_special(self):\\n\"\n"
".ascii \" special_shift = 6 # ZEND_AST_SPECIAL_SHIFT\\n\"\n"
@ -893,37 +907,46 @@ asm(
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \" load_type_bits()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return self.value_to_string()\\n\"\n"
".ascii \" return '((zval*) 0x%x) %s' % (self.val.address, self.value_to_string())\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def value_to_string(self):\\n\"\n"
".ascii \" t = int(self.val['u1']['v']['type'])\\n\"\n"
".ascii \" if t == type_name_to_bit['undef']:\\n\"\n"
".ascii \" if t == ZendTypeBits.bit('undef'):\\n\"\n"
".ascii \" return 'undef'\\n\"\n"
".ascii \" elif t == type_name_to_bit['null']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('null'):\\n\"\n"
".ascii \" return 'null'\\n\"\n"
".ascii \" elif t == type_name_to_bit['false']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('false'):\\n\"\n"
".ascii \" return 'false'\\n\"\n"
".ascii \" elif t == type_name_to_bit['true']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('true'):\\n\"\n"
".ascii \" return 'true'\\n\"\n"
".ascii \" elif t == type_name_to_bit['long']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('long'):\\n\"\n"
".ascii \" return str(self.val['value']['lval'])\\n\"\n"
".ascii \" elif t == type_name_to_bit['double']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('double'):\\n\"\n"
".ascii \" return str(self.val['value']['dval'])\\n\"\n"
".ascii \" elif t == type_name_to_bit['string']:\\n\"\n"
".ascii \" return ZendStringPrettyPrinter(self.val['value']['str'].dereference()).to_string()\\n\"\n"
".ascii \" elif t == type_name_to_bit['array']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('string'):\\n\"\n"
".ascii \" return format_zstr(self.val['value']['str'])\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('array'):\\n\"\n"
".ascii \" return 'array'\\n\"\n"
".ascii \" elif t == type_name_to_bit['object']:\\n\"\n"
".ascii \" return 'object(%s)' % ZendStringPrettyPrinter(self.val['value']['obj']['ce']['name'].dereference()).to_string()\\n\"\n"
".ascii \" elif t == type_name_to_bit['resource']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('object'):\\n\"\n"
".ascii \" return 'object(%s)' % format_zstr(self.val['value']['obj']['ce']['name'])\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('resource'):\\n\"\n"
".ascii \" return 'resource'\\n\"\n"
".ascii \" elif t == type_name_to_bit['reference']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('reference'):\\n\"\n"
".ascii \" return 'reference'\\n\"\n"
".ascii \" elif t == type_name_to_bit['constant_ast']:\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('constant_ast'):\\n\"\n"
".ascii \" return 'constant_ast'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('indirect'):\\n\"\n"
".ascii \" value = self.val['value']['zv']\\n\"\n"
".ascii \" valuestr = ZvalPrettyPrinter(value).to_string()\\n\"\n"
".ascii \" return 'indirect: ((zval*) 0x%x) %s' % (int(value), valuestr)\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('ptr'):\\n\"\n"
".ascii \" value = int(self.val['value']['ptr'])\\n\"\n"
".ascii \" return 'ptr: ((void*) 0x%x)' % (value)\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('alias_ptr'):\\n\"\n"
".ascii \" value = int(self.val['value']['ptr'])\\n\"\n"
".ascii \" return 'alias_ptr: ((void*) 0x%x)' % (value)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" return 'zval of type %d' % int(self.val['u1']['v']['type'])\\n\"\n"
".ascii \"\\n\"\n"
@ -931,38 +954,42 @@ asm(
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'value':\\n\"\n"
".ascii \" value = self.val['value']\\n\"\n"
".ascii \" sub_field = 'ptr'\\n\"\n"
".ascii \" t = int(self.val['u1']['v']['type'])\\n\"\n"
".ascii \" if t == type_name_to_bit['undef']:\\n\"\n"
".ascii \" value = value['lval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['null']:\\n\"\n"
".ascii \" value = value['lval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['false']:\\n\"\n"
".ascii \" value = value['lval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['true']:\\n\"\n"
".ascii \" value = value['lval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['long']:\\n\"\n"
".ascii \" value = value['lval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['double']:\\n\"\n"
".ascii \" value = value['dval']\\n\"\n"
".ascii \" elif t == type_name_to_bit['string']:\\n\"\n"
".ascii \" value = value['str'].dereference()\\n\"\n"
".ascii \" elif t == type_name_to_bit['array']:\\n\"\n"
".ascii \" value = value['ht'].dereference()\\n\"\n"
".ascii \" elif t == type_name_to_bit['object']:\\n\"\n"
".ascii \" value = value['obj'].dereference()\\n\"\n"
".ascii \" elif t == type_name_to_bit['resource']:\\n\"\n"
".ascii \" value = value['res'].dereference()\\n\"\n"
".ascii \" elif t == type_name_to_bit['reference']:\\n\"\n"
".ascii \" value = value['ref'].dereference()\\n\"\n"
".ascii \" elif t == type_name_to_bit['constant_ast']:\\n\"\n"
".ascii \" value = value['ast'].dereference()\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" value = value['ptr']\\n\"\n"
".ascii \" yield (field.name, value)\\n\"\n"
".ascii \" if t == ZendTypeBits.bit('undef'):\\n\"\n"
".ascii \" sub_field = 'lval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('null'):\\n\"\n"
".ascii \" sub_field = 'lval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('false'):\\n\"\n"
".ascii \" sub_field = 'lval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('true'):\\n\"\n"
".ascii \" sub_field = 'lval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('long'):\\n\"\n"
".ascii \" sub_field = 'lval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('double'):\\n\"\n"
".ascii \" sub_field = 'dval'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('string'):\\n\"\n"
".ascii \" sub_field = 'str'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('array'):\\n\"\n"
".ascii \" sub_field = 'arr'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('object'):\\n\"\n"
".ascii \" sub_field = 'obj'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('resource'):\\n\"\n"
".ascii \" sub_field = 'res'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('reference'):\\n\"\n"
".ascii \" sub_field = 'ref'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('constant_ast'):\\n\"\n"
".ascii \" sub_field = 'ast'\\n\"\n"
".ascii \" elif t == ZendTypeBits.bit('indirect'):\\n\"\n"
".ascii \" sub_field = 'zv'\\n\"\n"
".ascii \" value = value[sub_field]\\n\"\n"
".ascii \" if sub_field != 'ptr' and value.type.code == gdb.TYPE_CODE_PTR:\\n\"\n"
".ascii \" value = value.dereference()\\n\"\n"
".ascii \" yield ('%s.%s' % (field.name, sub_field), value)\\n\"\n"
".ascii \" elif field.name == 'u2':\\n\"\n"
".ascii \" yield ('u2', self.val[field.name]['extra'])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter)\\n\"\n"
@ -970,22 +997,20 @@ asm(
".ascii \"class ZendClassEntryPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_class_entry\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # String pointers, show the string contents if possible\\n\"\n"
".ascii \" STRING_FIELDS = [ 'name', 'doc_comment' ]\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" return zendStringPointerPrinter(self.val['name'])\\n\"\n"
".ascii \" return '((zend_class_entry*) 0x%x) %s' % (self.val.address, format_zstr(self.val['name']))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name is not None:\\n\"\n"
".ascii \" if field.name in self.STRING_FIELDS:\\n\"\n"
".ascii \" yield (field.name, zendStringPointerPrinter(self.val[field.name]))\\n\"\n"
".ascii \" if field.name == 'ce_flags':\\n\"\n"
".ascii \" flags = self.val[field.name]\\n\"\n"
".ascii \" yield (field.name, '%d (%s)' % (flags, ZendAccFlags.format_ce_flags(flags)))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" # Don't break on the union fields. Unfortunately, pretty\\n\"\n"
".ascii \" # printers done in python cannot match the default formatting of\\n\"\n"
@ -1004,52 +1029,592 @@ asm(
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'doc_comment':\\n\"\n"
".ascii \" yield ('doc_comment', zendStringPointerPrinter(self.val['doc_comment']))\\n\"\n"
".ascii \" elif field.name == 'ce':\\n\"\n"
".ascii \" yield ('ce', zendStringPointerPrinter(self.val['ce']['name']))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" if field.name == 'value':\\n\"\n"
".ascii \" flags = self.val[field.name]['u2']['constant_flags']\\n\"\n"
".ascii \" yield ('value.u2.constant_flags', '%d (%s)' % (flags, ZendAccFlags.format_const_flags(flags)))\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_class_constant', '^_zend_class_constant$', ZendClassConstantPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"type_bit_to_name = None\\n\"\n"
".ascii \"type_name_to_bit = None\\n\"\n"
".ascii \"class ZendPropertyInfoPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_property_info\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def load_type_bits():\\n\"\n"
".ascii \" global type_bit_to_name\\n\"\n"
".ascii \" global type_name_to_bit\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if type_bit_to_name != None:\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'flags':\\n\"\n"
".ascii \" flags = self.val[field.name]\\n\"\n"
".ascii \" yield ('flags', '%d (%s)' % (flags, ZendAccFlags.format_prop_flags(flags)))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_property_info', '^_zend_property_info$', ZendPropertyInfoPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendFunctionPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_function\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" match int(self.val['type']):\\n\"\n"
".ascii \" case ZendFnTypes.ZEND_INTERNAL_FUNCTION:\\n\"\n"
".ascii \" typestr = 'internal'\\n\"\n"
".ascii \" case ZendFnTypes.ZEND_USER_FUNCTION:\\n\"\n"
".ascii \" typestr = 'user'\\n\"\n"
".ascii \" case ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n"
".ascii \" typestr = 'eval'\\n\"\n"
".ascii \" case _:\\n\"\n"
".ascii \" typestr = '\?\?\?'\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if self.val['common']['function_name']:\\n\"\n"
".ascii \" namestr = format_zstr(self.val['common']['function_name'])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" namestr = '{main}'\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if self.val['common']['scope']:\\n\"\n"
".ascii \" str = '%s method %s::%s' % (typestr, format_zstr(self.val['common']['scope']['name']), namestr)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" str = '%s function %s' % (typestr, namestr)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n"
".ascii \" str = '%s %s:%d' % (str, format_zstr(self.val['op_array']['filename']), int(self.val['op_array']['line_start']))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return '((zend_function*) 0x%x) %s' % (self.val.address, str)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'common' or field.name == 'type' or field.name == 'quick_arg_flags':\\n\"\n"
".ascii \" # Redundant with op_array / internal_function\\n\"\n"
".ascii \" continue\\n\"\n"
".ascii \" elif field.name == 'op_array':\\n\"\n"
".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" elif field.name == 'internal_function':\\n\"\n"
".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_INTERNAL_FUNCTION:\\n\"\n"
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_function', '^_zend_function$', ZendFunctionPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendOpArrayPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_op_array\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" if self.val['function_name']:\\n\"\n"
".ascii \" namestr = format_zstr(self.val['function_name'])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" namestr = '{main}'\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if self.val['scope']:\\n\"\n"
".ascii \" str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" str = 'function %s' % (namestr)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" str = '%s %s:%d' % (str, format_zstr(self.val['filename']), int(self.val['line_start']))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return '((zend_op_array*) 0x%x) %s' % (self.val.address, str)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'fn_flags':\\n\"\n"
".ascii \" value = self.val[field.name]\\n\"\n"
".ascii \" yield (field.name, '%d (%s)' % (value, ZendAccFlags.format_fn_flags(value)))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_op_array', '^_zend_op_array$', ZendOpArrayPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendOpPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_op\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'opcode':\\n\"\n"
".ascii \" opcode = int(self.val[field.name])\\n\"\n"
".ascii \" yield (field.name, '%d (%s)' % (opcode, ZendOpcodes.name(opcode)))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_op', '^_zend_op$', ZendOpPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendInternalFunctionPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_internal_function\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def to_string(self):\\n\"\n"
".ascii \" if self.val['function_name']:\\n\"\n"
".ascii \" namestr = format_zstr(self.val['function_name'])\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" namestr = '\?\?\?'\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if self.val['scope']:\\n\"\n"
".ascii \" str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" str = 'function %s' % (namestr)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return '((zend_internal_function*) 0x%x) %s' % (self.val.address, str)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'fn_flags':\\n\"\n"
".ascii \" yield ('fn_flags', ('%d (%s)' % (self.val[field.name], ZendAccFlags.format_fn_flags(self.val[field.name]))))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_internal_function', '^_zend_internal_function$', ZendInternalFunctionPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendRefcountedHPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
".ascii \" \\\"Print a zend_refcounted_h\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__(self, val):\\n\"\n"
".ascii \" self.val = val\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def children(self):\\n\"\n"
".ascii \" for field in self.val.type.fields():\\n\"\n"
".ascii \" if field.name == 'u':\\n\"\n"
".ascii \" val = self.val[field.name]\\n\"\n"
".ascii \" if val == None:\\n\"\n"
".ascii \" val = self.val\\n\"\n"
".ascii \" for subfield in val.type.fields():\\n\"\n"
".ascii \" if subfield.name == 'type_info':\\n\"\n"
".ascii \" flags = int(val[subfield.name])\\n\"\n"
".ascii \" yield (('%s.%s' % (field.name, subfield.name)), '%d (%s)' % (flags, ZendRefTypeInfo.format(flags)))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (('%s.%s' % (field.name, subfield.name)), format_nested(val[subfield.name]))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" yield (('%s' % field.name), format_nested(self.val[field.name]))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"pp_set.add_printer('zend_refcounted_h', '^_zend_refcounted_h$', ZendRefcountedHPrettyPrinter)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class PrintAccFlagsCommand(gdb.Command):\\n\"\n"
".ascii \" \\\"Pretty print ACC flags\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__ (self, type):\\n\"\n"
".ascii \" self.type = type\\n\"\n"
".ascii \" name = 'print_%s_flags' % type\\n\"\n"
".ascii \" super(PrintAccFlagsCommand, self).__init__(name, gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def invoke (self, arg, from_tty):\\n\"\n"
".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n"
".ascii \" print(ZendAccFlags.format_flags(arg, self.type))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"PrintAccFlagsCommand('fn')\\n\"\n"
".ascii \"PrintAccFlagsCommand('ce')\\n\"\n"
".ascii \"PrintAccFlagsCommand('prop')\\n\"\n"
".ascii \"PrintAccFlagsCommand('const')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class PrintOpcodeCommand(gdb.Command):\\n\"\n"
".ascii \" \\\"Pretty print opcode\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__ (self):\\n\"\n"
".ascii \" super(PrintOpcodeCommand, self).__init__(\\\"print_opcode\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def invoke (self, arg, from_tty):\\n\"\n"
".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n"
".ascii \" print(ZendOpcodes.name(arg))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"PrintOpcodeCommand()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class PrintRefTypeInfoCommand(gdb.Command):\\n\"\n"
".ascii \" \\\"Pretty print zend_refcounted.gc.u.type_info\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__ (self):\\n\"\n"
".ascii \" super(PrintRefTypeInfoCommand, self).__init__(\\\"print_ref_type_info\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def invoke (self, arg, from_tty):\\n\"\n"
".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n"
".ascii \" print(ZendRefTypeInfo.format(arg))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"PrintRefTypeInfoCommand()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class DumpOpArrayCommand(gdb.Command):\\n\"\n"
".ascii \" \\\"Dump an op_array\\\"\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def __init__ (self):\\n\"\n"
".ascii \" super(DumpOpArrayCommand, self).__init__(\\\"dump_op_array\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def invoke (self, arg, from_tty):\\n\"\n"
".ascii \" op_array = gdb.parse_and_eval(arg)\\n\"\n"
".ascii \" if op_array.type.code != gdb.TYPE_CODE_PTR:\\n\"\n"
".ascii \" print(\\\"Must pass a zend_op_array* (got a %s)\\\" % op_array.type)\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \" if str(gdb.types.get_basic_type(op_array.type.target())) != 'struct _zend_op_array':\\n\"\n"
".ascii \" print(\\\"Must pass a zend_op_array* (got a %s)\\\" % op_array.type)\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \" if int(op_array) == 0:\\n\"\n"
".ascii \" print(\\\"NULL\\\")\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \" gdb.execute(\\\"call zend_dump_op_array((zend_op_array*)0x%x, 0, 0, 0)\\\" % (int(op_array)))\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" (symbol,_) = gdb.lookup_symbol(\\\"zend_gc_refcount\\\")\\n\"\n"
".ascii \"DumpOpArrayCommand()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendTypeBits:\\n\"\n"
".ascii \" _bits = None\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def zendTypeName(self, bit):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" for name in self._bits:\\n\"\n"
".ascii \" if bit == self._bits[name]:\\n\"\n"
".ascii \" return name\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def zvalTypeName(self, bit):\\n\"\n"
".ascii \" # Same as zendTypeName, but return the last matching one\\n\"\n"
".ascii \" # e.g. 13 is IS_PTR, not IS_ITERABLE\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" ret = None\\n\"\n"
".ascii \" for name in self._bits:\\n\"\n"
".ascii \" if bit == self._bits[name]:\\n\"\n"
".ascii \" ret = name\\n\"\n"
".ascii \" return ret\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def bit(self, name):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" return self._bits.get(name)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def _load(self):\\n\"\n"
".ascii \" if self._bits != None:\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" dirname = detect_source_dir()\\n\"\n"
".ascii \" filename = os.path.join(dirname, 'zend_types.h')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" bits = {}\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" with open(filename, 'r') as file:\\n\"\n"
".ascii \" content = file.read()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define _ZEND_TYPE_([^\\\\s]+)_BIT\\\\s+\\\\(1u << (\\\\d+)\\\\)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit in matches:\\n\"\n"
".ascii \" bits[name.lower()] = int(bit)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define IS_([^\\\\s]+)\\\\s+(\\\\d+)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit in matches:\\n\"\n"
".ascii \" bits[name.lower()] = int(bit)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" self._bits = bits\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendFnTypes:\\n\"\n"
".ascii \" ZEND_INTERNAL_FUNCTION = 1\\n\"\n"
".ascii \" ZEND_USER_FUNCTION = 2\\n\"\n"
".ascii \" ZEND_EVAL_CODE = 4\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendAccFlag:\\n\"\n"
".ascii \" ce = False\\n\"\n"
".ascii \" fn = False\\n\"\n"
".ascii \" prop = False\\n\"\n"
".ascii \" const = False\\n\"\n"
".ascii \" bit = 0\\n\"\n"
".ascii \" def __init__(self, ce, fn, prop, const, bit):\\n\"\n"
".ascii \" self.ce = ce\\n\"\n"
".ascii \" self.fn = fn\\n\"\n"
".ascii \" self.prop = prop\\n\"\n"
".ascii \" self.const = const\\n\"\n"
".ascii \" self.bit = bit\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" def applies_to(self, type):\\n\"\n"
".ascii \" return getattr(self, type)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendAccFlags:\\n\"\n"
".ascii \" _flags = None\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def fn_flag_name(self, bit):\\n\"\n"
".ascii \" self.flag_name(bit, 'fn')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def ce_flag_name(self, bit):\\n\"\n"
".ascii \" self.flag_name(bit, 'ce')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def prop_flag_name(self, bit):\\n\"\n"
".ascii \" self.flag_name(bit, 'prop')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def const_flag_name(self, bit):\\n\"\n"
".ascii \" self.flag_name(bit, 'const')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def flag_name(self, bit, type):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" for name in self._flags:\\n\"\n"
".ascii \" flag = self._flags[name]\\n\"\n"
".ascii \" if flag.applies_to(type) and bit == flag.bit:\\n\"\n"
".ascii \" return name\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def flag_bit(self, name):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" return self._flags[name]\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format_flags(self, flags, type):\\n\"\n"
".ascii \" flags = int(flags)\\n\"\n"
".ascii \" names = []\\n\"\n"
".ascii \" for i in range(0, 31):\\n\"\n"
".ascii \" if (flags & (1 << i)) != 0:\\n\"\n"
".ascii \" name = self.flag_name(i, type)\\n\"\n"
".ascii \" if name == None:\\n\"\n"
".ascii \" names.append('(1 << %d)' % (i))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" names.append(name)\\n\"\n"
".ascii \" return ' | '.join(names)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format_fn_flags(self, flags):\\n\"\n"
".ascii \" return self.format_flags(flags, 'fn')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format_ce_flags(self, flags):\\n\"\n"
".ascii \" return self.format_flags(flags, 'ce')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format_prop_flags(self, flags):\\n\"\n"
".ascii \" return self.format_flags(flags, 'prop')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format_const_flags(self, flags):\\n\"\n"
".ascii \" return self.format_flags(flags, 'const')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def _load(self):\\n\"\n"
".ascii \" if self._flags != None:\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" dirname = detect_source_dir()\\n\"\n"
".ascii \" filename = os.path.join(dirname, 'zend_compile.h')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" flags = {}\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" with open(filename, 'r') as file:\\n\"\n"
".ascii \" content = file.read()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define (ZEND_ACC_[^\\\\s]+)\\\\s+\\\\(1U\?\\\\s+<<\\\\s+(\\\\d+)\\\\)\\\\s+/\\\\*\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\*/')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit, cls, func, prop, const in matches:\\n\"\n"
".ascii \" flags[name] = ZendAccFlag(cls == 'X', func == 'X', prop == 'X', const == 'X', int(bit))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" self._flags = flags\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendOpcodes:\\n\"\n"
".ascii \" _opcodes = None\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def name(self, number):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" number = int(number)\\n\"\n"
".ascii \" for name in self._opcodes:\\n\"\n"
".ascii \" if number == self._opcodes[name]:\\n\"\n"
".ascii \" return name\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def number(self, name):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" return self._opcodes[name]\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def _load(self):\\n\"\n"
".ascii \" if self._opcodes != None:\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" dirname = detect_source_dir()\\n\"\n"
".ascii \" filename = os.path.join(dirname, 'zend_vm_opcodes.h')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" opcodes = {}\\n\"\n"
".ascii \" found_nop = False\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" with open(filename, 'r') as file:\\n\"\n"
".ascii \" content = file.read()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define (ZEND_[^\\\\s]+)\\\\s+([0-9]+)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, number in matches:\\n\"\n"
".ascii \" if not found_nop:\\n\"\n"
".ascii \" if name == 'ZEND_NOP':\\n\"\n"
".ascii \" found_nop = True\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" continue\\n\"\n"
".ascii \" if name == 'ZEND_VM_LAST_OPCODE':\\n\"\n"
".ascii \" break\\n\"\n"
".ascii \" opcodes[name] = int(number)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" self._opcodes = opcodes\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"class ZendRefTypeInfo:\\n\"\n"
".ascii \" _bits = None\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def flag_name(self, bit):\\n\"\n"
".ascii \" return self._flag_name_in(bit, self._bits)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def _flag_name_in(self, bit, bits):\\n\"\n"
".ascii \" for name in bits:\\n\"\n"
".ascii \" if bit == bits[name]:\\n\"\n"
".ascii \" return name\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def bit(self, name):\\n\"\n"
".ascii \" return self._bits.get(name)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def format(self, flags):\\n\"\n"
".ascii \" self._load()\\n\"\n"
".ascii \" names = []\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" type = flags & self._type_mask\\n\"\n"
".ascii \" type_name = ZendTypeBits.zvalTypeName(type)\\n\"\n"
".ascii \" if type_name is not None:\\n\"\n"
".ascii \" names.append('IS_%s' % type_name.upper())\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" bits = self._bits\\n\"\n"
".ascii \" type_bits = None\\n\"\n"
".ascii \" match type_name:\\n\"\n"
".ascii \" case 'string':\\n\"\n"
".ascii \" type_bits = self._str_bits\\n\"\n"
".ascii \" case 'array':\\n\"\n"
".ascii \" type_bits = self._array_bits\\n\"\n"
".ascii \" case 'object':\\n\"\n"
".ascii \" type_bits = self._obj_bits\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" type_flags = flags & self._flags_mask\\n\"\n"
".ascii \" for i in range(0, 31):\\n\"\n"
".ascii \" if (1<<i) > type_flags:\\n\"\n"
".ascii \" break\\n\"\n"
".ascii \" if (type_flags & (1<<i)) != 0:\\n\"\n"
".ascii \" name = self.flag_name(i)\\n\"\n"
".ascii \" if type_bits is not None:\\n\"\n"
".ascii \" name2 = self._flag_name_in(i, type_bits)\\n\"\n"
".ascii \" if name2 is not None:\\n\"\n"
".ascii \" if name is not None:\\n\"\n"
".ascii \" names.append('%s(%s)' % (name2, name))\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" names.append(name2)\\n\"\n"
".ascii \" continue\\n\"\n"
".ascii \" if name is not None:\\n\"\n"
".ascii \" names.append(name)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" names.append('(1 << %d)' % (i))\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if (flags & (1<<self.bit('GC_NOT_COLLECTABLE'))) == 0:\\n\"\n"
".ascii \" gc_color = (flags >> self._info_shift) & self._gc_color\\n\"\n"
".ascii \" match gc_color:\\n\"\n"
".ascii \" case self._gc_black:\\n\"\n"
".ascii \" names.append('GC_BLACK')\\n\"\n"
".ascii \" case self._gc_white:\\n\"\n"
".ascii \" names.append('GC_WHITE')\\n\"\n"
".ascii \" case self._gc_grey:\\n\"\n"
".ascii \" names.append('GC_GREY')\\n\"\n"
".ascii \" case self._gc_purple:\\n\"\n"
".ascii \" names.append('GC_PURPLE')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" gc_address = (flags >> self._info_shift) & self._gc_address\\n\"\n"
".ascii \" if gc_address != 0:\\n\"\n"
".ascii \" names.append('GC_ADDRESS(%d)' % gc_address)\\n\"\n"
".ascii \" else:\\n\"\n"
".ascii \" info = flags & self._info_mask\\n\"\n"
".ascii \" if info != 0:\\n\"\n"
".ascii \" names.append('GC_INFO(%d)' % info)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return ' | '.join(names)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" @classmethod\\n\"\n"
".ascii \" def _load(self):\\n\"\n"
".ascii \" if self._bits != None:\\n\"\n"
".ascii \" return\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" dirname = detect_source_dir()\\n\"\n"
".ascii \" filename = os.path.join(dirname, 'zend_types.h')\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" bits = {}\\n\"\n"
".ascii \" str_bits = {}\\n\"\n"
".ascii \" array_bits = {}\\n\"\n"
".ascii \" obj_bits = {}\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" with open(filename, 'r') as file:\\n\"\n"
".ascii \" content = file.read()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # GC_NOT_COLLECTABLE (1<<4)\\n\"\n"
".ascii \" pattern = re.compile(r'#define (GC_[^\\\\s]+)\\\\s+\\\\(\\\\s*1\\\\s*<<\\\\s*([0-9]+)\\\\s*\\\\)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit in matches:\\n\"\n"
".ascii \" bits[name] = int(bit)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # GC_TYPE_MASK 0x0000000f\\n\"\n"
".ascii \" # GC_INFO_SHIFT 10\\n\"\n"
".ascii \" pattern = re.compile(r'#define (GC_[^\\\\s]+)\\\\s+((0x)\?[0-9a-f]+)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit, _ in matches:\\n\"\n"
".ascii \" match name:\\n\"\n"
".ascii \" case 'GC_TYPE_MASK':\\n\"\n"
".ascii \" self._type_mask = int(bit, 0)\\n\"\n"
".ascii \" case 'GC_FLAGS_MASK':\\n\"\n"
".ascii \" self._flags_mask = int(bit, 0)\\n\"\n"
".ascii \" case 'GC_INFO_MASK':\\n\"\n"
".ascii \" self._info_mask = int(bit, 0)\\n\"\n"
".ascii \" case 'GC_INFO_SHIFT':\\n\"\n"
".ascii \" self._info_shift = int(bit, 0)\\n\"\n"
".ascii \" case 'GC_FLAGS_SHIFT':\\n\"\n"
".ascii \" self._flags_shift = int(bit, 0)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # IS_STR_INTERNED GC_IMMUTABLE\\n\"\n"
".ascii \" # IS_STR_PERMANENT (1<<8)\\n\"\n"
".ascii \" pattern = re.compile(r'#define (IS_(STR|ARRAY|OBJ)_[^\\\\s]+)\\\\s+(\\\\(\\\\s*1\\\\s*<<\\\\s*([0-9]+)\\\\s*\\\\)|GC_[a-zA-Z_]+)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, type, val, bit in matches:\\n\"\n"
".ascii \" if bit == '':\\n\"\n"
".ascii \" bit = bits.get(val)\\n\"\n"
".ascii \" if bit == None:\\n\"\n"
".ascii \" continue\\n\"\n"
".ascii \" match type:\\n\"\n"
".ascii \" case 'STR':\\n\"\n"
".ascii \" target = str_bits\\n\"\n"
".ascii \" case 'ARRAY':\\n\"\n"
".ascii \" target = array_bits\\n\"\n"
".ascii \" case 'OBJ':\\n\"\n"
".ascii \" target = obj_bits\\n\"\n"
".ascii \" target[name] = int(bit)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # Hard coded because these are not exposed in header files\\n\"\n"
".ascii \" self._gc_address = 0x0fffff\\n\"\n"
".ascii \" self._gc_color = 0x300000\\n\"\n"
".ascii \" self._gc_black = 0x000000\\n\"\n"
".ascii \" self._gc_white = 0x100000\\n\"\n"
".ascii \" self._gc_grey = 0x200000\\n\"\n"
".ascii \" self._gc_purple = 0x300000\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" self._bits = bits\\n\"\n"
".ascii \" self._str_bits = str_bits\\n\"\n"
".ascii \" self._array_bits = array_bits\\n\"\n"
".ascii \" self._obj_bits = obj_bits\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def detect_source_dir():\\n\"\n"
".ascii \" (symbol,_) = gdb.lookup_symbol(\\\"zend_visibility_to_set_visibility\\\")\\n\"\n"
".ascii \" if symbol == None:\\n\"\n"
".ascii \" raise Exception(\\\"Could not find zend_types.h: symbol zend_gc_refcount not found\\\")\\n\"\n"
".ascii \" raise Exception(\\\"Could not find zend_compile.h: symbol zend_visibility_to_set_visibility not found\\\")\\n\"\n"
".ascii \" filename = symbol.symtab.fullname()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" bits = {}\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" with open(filename, 'r') as file:\\n\"\n"
".ascii \" content = file.read()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define _ZEND_TYPE_([^\\\\s]+)_BIT\\\\s+\\\\(1u << (\\\\d+)\\\\)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit in matches:\\n\"\n"
".ascii \" bits[int(bit)] = name.lower()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" pattern = re.compile(r'#define IS_([^\\\\s]+)\\\\s+(\\\\d+)')\\n\"\n"
".ascii \" matches = pattern.findall(content)\\n\"\n"
".ascii \" for name, bit in matches:\\n\"\n"
".ascii \" if not int(bit) in bits:\\n\"\n"
".ascii \" bits[int(bit)] = name.lower()\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" types = {}\\n\"\n"
".ascii \" for bit in bits:\\n\"\n"
".ascii \" types[bits[bit]] = bit\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" type_bit_to_name = bits\\n\"\n"
".ascii \" type_name_to_bit = types\\n\"\n"
".ascii \" dirname = os.path.dirname(filename)\\n\"\n"
".ascii \" return dirname\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def lookup_symbol(name):\\n\"\n"
".ascii \" (symbol, _) = gdb.lookup_symbol(name)\\n\"\n"
@ -1065,6 +1630,51 @@ asm(
".ascii \" # array types have a single field whose type represents its range\\n\"\n"
".ascii \" return ary_type.fields()[0].type.range()[1]+1\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def format_zstr(zstr):\\n\"\n"
".ascii \" len = int(zstr['len'])\\n\"\n"
".ascii \" truncated = False\\n\"\n"
".ascii \" if len > 200:\\n\"\n"
".ascii \" len = 200\\n\"\n"
".ascii \" truncated = True\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" ptr_type = gdb.lookup_type('char').pointer()\\n\"\n"
".ascii \" ary_type = gdb.lookup_type('char').array(len)\\n\"\n"
".ascii \" str = zstr['val'].cast(ptr_type).dereference().cast(ary_type)\\n\"\n"
".ascii \" str = str.format_string()\\n\"\n"
".ascii \" if truncated:\\n\"\n"
".ascii \" str += ' (%d bytes total)' % int(zstr['len'])\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return str\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"def format_nested(value):\\n\"\n"
".ascii \" orig_value = value\\n\"\n"
".ascii \" type = value.type\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" # Null pointers\\n\"\n"
".ascii \" if type.code == gdb.TYPE_CODE_PTR and int(value) == 0:\\n\"\n"
".ascii \" return orig_value\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" addr = orig_value.address\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" while type.code == gdb.TYPE_CODE_PTR:\\n\"\n"
".ascii \" addr = int(value)\\n\"\n"
".ascii \" type = type.target()\\n\"\n"
".ascii \" try:\\n\"\n"
".ascii \" value = value.dereference()\\n\"\n"
".ascii \" except:\\n\"\n"
".ascii \" pass\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" type = gdb.types.get_basic_type(type)\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" if type.tag and re.match(r'^_zend_string$', type.tag):\\n\"\n"
".ascii \" return format_zstr(value)\\n\"\n"
".ascii \" elif type.tag and re.match(r'^_zend_class_entry$', type.tag):\\n\"\n"
".ascii \" return '((zend_class_entry*)0x%x) %s' % (addr, format_zstr(value['name']))\\n\"\n"
".ascii \" elif type.tag and re.match(r'^_zend_array$', type.tag):\\n\"\n"
".ascii \" return '((zend_array*)0x%x) array(%d)' % (addr, value['nNumOfElements'])\\n\"\n"
".ascii \"\\n\"\n"
".ascii \" return orig_value\\n\"\n"
".ascii \"\\n\"\n"
".ascii \"gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)\\n\"\n"
".ascii \"\\n\"\n"
".byte 0\n"