Skip to content

Commit 32bd6ac

Browse files
GDB: format output for class entries and class constants
For class entries, for null pointers replace `0x0` with `NULL` for clarity. For string pointers that are set, show the string contents. For class constants, the `ce` (class entry pointer) field is shown with the name of the class, the `doc_comment` field is shown with the string contents if possible, and `NULL` if not, and the `attributes` field is shown as `NULL` instead of `0x0` if not set.
1 parent 1b9568d commit 32bd6ac

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

main/debug_gdb_scripts.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,12 @@ asm(
737737
".ascii \"\\n\"\n"
738738
".ascii \"pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)\\n\"\n"
739739
".ascii \"\\n\"\n"
740+
".ascii \"def zendStringPointerPrinter(ptr):\\n\"\n"
741+
".ascii \" \\\"Given a pointer to a zend_string, show the contents (or NULL)\\\"\\n\"\n"
742+
".ascii \" if int(ptr) == 0:\\n\"\n"
743+
".ascii \" return 'NULL'\\n\"\n"
744+
".ascii \" return ZendStringPrettyPrinter(ptr.dereference()).to_string()\\n\"\n"
745+
".ascii \"\\n\"\n"
740746
".ascii \"class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
741747
".ascii \" \\\"Print a zend_type\\\"\\n\"\n"
742748
".ascii \"\\n\"\n"
@@ -959,6 +965,87 @@ asm(
959965
".ascii \"\\n\"\n"
960966
".ascii \"pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter)\\n\"\n"
961967
".ascii \"\\n\"\n"
968+
".ascii \"def ptrOrNull(ptr):\\n\"\n"
969+
".ascii \" \\\"For NULL pointers, show NULL instead of 0x0\\\"\\n\"\n"
970+
".ascii \" if int(ptr) == 0:\\n\"\n"
971+
".ascii \" return 'NULL'\\n\"\n"
972+
".ascii \" return ptr\\n\"\n"
973+
".ascii \"\\n\"\n"
974+
".ascii \"class ZendClassEntryPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
975+
".ascii \" \\\"Print a zend_class_entry\\\"\\n\"\n"
976+
".ascii \"\\n\"\n"
977+
".ascii \" # Fields that are pointers and should show NULL instead of 0x0, string\\n\"\n"
978+
".ascii \" # fields not checked here since zendStringPointerPrinter() handles them\\n\"\n"
979+
".ascii \" POINTER_FIELDS = [\\n\"\n"
980+
".ascii \" # Class data\\n\"\n"
981+
".ascii \" 'default_properties_table',\\n\"\n"
982+
".ascii \" 'default_static_members_table',\\n\"\n"
983+
".ascii \" 'static_members_table__ptr',\\n\"\n"
984+
".ascii \" 'mutable_data__ptr',\\n\"\n"
985+
".ascii \" 'properties_info_table',\\n\"\n"
986+
".ascii \" # Magic methods\\n\"\n"
987+
".ascii \" 'constructor', 'destructor', 'clone', '__get', '__set', '__unset',\\n\"\n"
988+
".ascii \" '__isset', '__call', '__callstatic', '__tostring', '__debugInfo',\\n\"\n"
989+
".ascii \" '__serialize', '__unserialize',\\n\"\n"
990+
".ascii \" # Iterators/arrays implementation\\n\"\n"
991+
".ascii \" 'iterator_funcs_ptr', 'arrayaccess_funcs_ptr',\\n\"\n"
992+
".ascii \" # handlers\\n\"\n"
993+
".ascii \" 'get_iterator', 'get_static_method',\\n\"\n"
994+
".ascii \" # serialization\\n\"\n"
995+
".ascii \" 'serialize', 'unserialize',\\n\"\n"
996+
".ascii \" # traits\\n\"\n"
997+
".ascii \" 'trait_names', 'trait_aliases', 'trait_precedences',\\n\"\n"
998+
".ascii \" # other\\n\"\n"
999+
".ascii \" 'attributes',\\n\"\n"
1000+
".ascii \" 'backed_enum_table',\\n\"\n"
1001+
".ascii \" ]\\n\"\n"
1002+
".ascii \" # String pointers, show the string contents if possible\\n\"\n"
1003+
".ascii \" STRING_FIELDS = [ 'name', 'doc_comment' ]\\n\"\n"
1004+
".ascii \"\\n\"\n"
1005+
".ascii \" def __init__(self, val):\\n\"\n"
1006+
".ascii \" self.val = val\\n\"\n"
1007+
".ascii \"\\n\"\n"
1008+
".ascii \" def to_string(self):\\n\"\n"
1009+
".ascii \" return zendStringPointerPrinter(self.val['name'])\\n\"\n"
1010+
".ascii \"\\n\"\n"
1011+
".ascii \" def children(self):\\n\"\n"
1012+
".ascii \" for field in self.val.type.fields():\\n\"\n"
1013+
".ascii \" if field.name is not None:\\n\"\n"
1014+
".ascii \" if field.name in self.STRING_FIELDS:\\n\"\n"
1015+
".ascii \" yield (field.name, zendStringPointerPrinter(self.val[field.name]))\\n\"\n"
1016+
".ascii \" elif field.name in self.POINTER_FIELDS:\\n\"\n"
1017+
".ascii \" yield (field.name, ptrOrNull(self.val[field.name]))\\n\"\n"
1018+
".ascii \" else:\\n\"\n"
1019+
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
1020+
".ascii \" else:\\n\"\n"
1021+
".ascii \" # Don't break on the union fields. Unfortunately, pretty\\n\"\n"
1022+
".ascii \" # printers done in python cannot match the default formatting of\\n\"\n"
1023+
".ascii \" # C anonymous fields, which omit the name entirely, see\\n\"\n"
1024+
".ascii \" # binutils-gdb/gdb/cp-valprint.c#248 (as of commit\\n\"\n"
1025+
".ascii \" # b6532accdd8e24329cc69bb58bc2883796008776)\\n\"\n"
1026+
".ascii \" yield ('<anonymous>', self.val[field])\\n\"\n"
1027+
".ascii \"\\n\"\n"
1028+
".ascii \"pp_set.add_printer('zend_class_entry', '^_zend_class_entry$', ZendClassEntryPrettyPrinter)\\n\"\n"
1029+
".ascii \"\\n\"\n"
1030+
".ascii \"class ZendClassConstantPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n"
1031+
".ascii \" \\\"Print a zend_class_constant\\\"\\n\"\n"
1032+
".ascii \"\\n\"\n"
1033+
".ascii \" def __init__(self, val):\\n\"\n"
1034+
".ascii \" self.val = val\\n\"\n"
1035+
".ascii \"\\n\"\n"
1036+
".ascii \" def children(self):\\n\"\n"
1037+
".ascii \" for field in self.val.type.fields():\\n\"\n"
1038+
".ascii \" if field.name == 'doc_comment':\\n\"\n"
1039+
".ascii \" yield ('doc_comment', zendStringPointerPrinter(self.val['doc_comment']))\\n\"\n"
1040+
".ascii \" elif field.name == 'ce':\\n\"\n"
1041+
".ascii \" yield ('ce', zendStringPointerPrinter(self.val['ce']['name']))\\n\"\n"
1042+
".ascii \" elif field.name == 'attributes':\\n\"\n"
1043+
".ascii \" yield ('attributes', ptrOrNull(self.val['attributes']))\\n\"\n"
1044+
".ascii \" else:\\n\"\n"
1045+
".ascii \" yield (field.name, self.val[field.name])\\n\"\n"
1046+
".ascii \"\\n\"\n"
1047+
".ascii \"pp_set.add_printer('zend_class_constant', '^_zend_class_constant$', ZendClassConstantPrettyPrinter)\\n\"\n"
1048+
".ascii \"\\n\"\n"
9621049
".ascii \"type_bit_to_name = None\\n\"\n"
9631050
".ascii \"type_name_to_bit = None\\n\"\n"
9641051
".ascii \"\\n\"\n"

scripts/gdb/php_gdb.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ def format_string(self):
6767

6868
pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)
6969

70+
def zendStringPointerPrinter(ptr):
71+
"Given a pointer to a zend_string, show the contents (or NULL)"
72+
if int(ptr) == 0:
73+
return 'NULL'
74+
return ZendStringPrettyPrinter(ptr.dereference()).to_string()
75+
7076
class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):
7177
"Print a zend_type"
7278

@@ -289,6 +295,87 @@ def children(self):
289295

290296
pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter)
291297

298+
def ptrOrNull(ptr):
299+
"For NULL pointers, show NULL instead of 0x0"
300+
if int(ptr) == 0:
301+
return 'NULL'
302+
return ptr
303+
304+
class ZendClassEntryPrettyPrinter(gdb.printing.PrettyPrinter):
305+
"Print a zend_class_entry"
306+
307+
# Fields that are pointers and should show NULL instead of 0x0, string
308+
# fields not checked here since zendStringPointerPrinter() handles them
309+
POINTER_FIELDS = [
310+
# Class data
311+
'default_properties_table',
312+
'default_static_members_table',
313+
'static_members_table__ptr',
314+
'mutable_data__ptr',
315+
'properties_info_table',
316+
# Magic methods
317+
'constructor', 'destructor', 'clone', '__get', '__set', '__unset',
318+
'__isset', '__call', '__callstatic', '__tostring', '__debugInfo',
319+
'__serialize', '__unserialize',
320+
# Iterators/arrays implementation
321+
'iterator_funcs_ptr', 'arrayaccess_funcs_ptr',
322+
# handlers
323+
'get_iterator', 'get_static_method',
324+
# serialization
325+
'serialize', 'unserialize',
326+
# traits
327+
'trait_names', 'trait_aliases', 'trait_precedences',
328+
# other
329+
'attributes',
330+
'backed_enum_table',
331+
]
332+
# String pointers, show the string contents if possible
333+
STRING_FIELDS = [ 'name', 'doc_comment' ]
334+
335+
def __init__(self, val):
336+
self.val = val
337+
338+
def to_string(self):
339+
return zendStringPointerPrinter(self.val['name'])
340+
341+
def children(self):
342+
for field in self.val.type.fields():
343+
if field.name is not None:
344+
if field.name in self.STRING_FIELDS:
345+
yield (field.name, zendStringPointerPrinter(self.val[field.name]))
346+
elif field.name in self.POINTER_FIELDS:
347+
yield (field.name, ptrOrNull(self.val[field.name]))
348+
else:
349+
yield (field.name, self.val[field.name])
350+
else:
351+
# Don't break on the union fields. Unfortunately, pretty
352+
# printers done in python cannot match the default formatting of
353+
# C anonymous fields, which omit the name entirely, see
354+
# binutils-gdb/gdb/cp-valprint.c#248 (as of commit
355+
# b6532accdd8e24329cc69bb58bc2883796008776)
356+
yield ('<anonymous>', self.val[field])
357+
358+
pp_set.add_printer('zend_class_entry', '^_zend_class_entry$', ZendClassEntryPrettyPrinter)
359+
360+
class ZendClassConstantPrettyPrinter(gdb.printing.PrettyPrinter):
361+
"Print a zend_class_constant"
362+
363+
def __init__(self, val):
364+
self.val = val
365+
366+
def children(self):
367+
for field in self.val.type.fields():
368+
if field.name == 'doc_comment':
369+
yield ('doc_comment', zendStringPointerPrinter(self.val['doc_comment']))
370+
elif field.name == 'ce':
371+
yield ('ce', zendStringPointerPrinter(self.val['ce']['name']))
372+
elif field.name == 'attributes':
373+
yield ('attributes', ptrOrNull(self.val['attributes']))
374+
else:
375+
yield (field.name, self.val[field.name])
376+
377+
pp_set.add_printer('zend_class_constant', '^_zend_class_constant$', ZendClassConstantPrettyPrinter)
378+
292379
type_bit_to_name = None
293380
type_name_to_bit = None
294381

0 commit comments

Comments
 (0)