Skip to content

Commit 4058082

Browse files
authored
Add GDB pretty-printers for zend_string, zend_type (php#13463)
1 parent d24086b commit 4058082

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

.gdb.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
2+
"""GDB support for PHP types
3+
4+
Add this to your gdb by amending your ~/.gdbinit as follows:
5+
6+
source .gdb.py
7+
8+
Use
9+
(gdb) p /r any_variable
10+
to print |any_variable| without using any printers.
11+
12+
To interactively type Python for development of the printers, use
13+
(gdb) python foo = gdb.parse_and_eval('bar')
14+
to put the C value 'bar' in the current scope into a Python variable 'foo'.
15+
Then you can interact with that variable:
16+
(gdb) python print foo['impl_']
17+
"""
18+
19+
import gdb
20+
import re
21+
22+
pp_set = gdb.printing.RegexpCollectionPrettyPrinter("php")
23+
24+
class ZendStringPrettyPrinter(gdb.printing.PrettyPrinter):
25+
"Print a zend_string"
26+
27+
def __init__(self, val):
28+
self.val = val
29+
30+
def to_string(self):
31+
return self.format_string()
32+
33+
def children(self):
34+
for field in self.val.type.fields():
35+
yield (field.name, self.val[field.name])
36+
37+
def format_string(self):
38+
len = int(self.val['len'])
39+
truncated = False
40+
if len > 50:
41+
len = 50
42+
truncated = True
43+
44+
ptr_type = gdb.lookup_type('char').pointer()
45+
ary_type = gdb.lookup_type('char').array(len)
46+
str = self.val['val'].cast(ptr_type).dereference().cast(ary_type)
47+
str = str.format_string()
48+
if truncated:
49+
str += ' (%d bytes total)' % int(self.val['len'])
50+
51+
return str
52+
pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)
53+
54+
class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):
55+
"Print a zend_type"
56+
57+
def __init__(self, val):
58+
self.val = val
59+
self.load_bits()
60+
61+
def to_string(self):
62+
return self.format_type(self.val)
63+
64+
def children(self):
65+
for field in self.val.type.fields():
66+
yield (field.name, self.val[field.name])
67+
68+
def format_type(self, t):
69+
type_mask = int(t['type_mask'])
70+
type_mask_size = t['type_mask'].type.sizeof * 8
71+
separator = '|'
72+
parts = []
73+
meta = []
74+
for bit in range(0, type_mask_size):
75+
if type_mask & (1 << bit):
76+
type_name = self.bits.get(bit)
77+
match type_name:
78+
case None:
79+
parts.append('(1<<%d)' % bit)
80+
case 'list':
81+
list = t['ptr'].cast(gdb.lookup_type('zend_type_list').pointer())
82+
num_types = int(list['num_types'])
83+
types = list['types'].dereference().cast(gdb.lookup_type('zend_type').array(num_types))
84+
for i in range(0, num_types):
85+
str = self.format_type(types[i])
86+
if any((c in set('|&()')) for c in str):
87+
str = '(%s)' % str
88+
parts.append(str)
89+
case 'union' | 'arena':
90+
meta.append(type_name)
91+
case 'intersection':
92+
meta.append(type_name)
93+
separator = '&'
94+
case 'name':
95+
str = t['ptr'].cast(gdb.lookup_type('zend_string').pointer())
96+
parts.append(ZendStringPrettyPrinter(str).to_string())
97+
case _:
98+
parts.append(type_name)
99+
100+
str = separator.join(parts)
101+
102+
if len(meta) > 0:
103+
str = '[%s] %s' % (','.join(meta), str)
104+
105+
return str
106+
107+
def load_bits(self):
108+
(symbol,_) = gdb.lookup_symbol("zend_gc_refcount")
109+
if symbol == None:
110+
raise "Could not find zend_types.h: symbol zend_gc_refcount not found"
111+
filename = symbol.symtab.fullname()
112+
113+
bits = {}
114+
115+
with open(filename, 'r') as file:
116+
content = file.read()
117+
118+
pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)')
119+
matches = pattern.findall(content)
120+
for name, bit in matches:
121+
bits[int(bit)] = name.lower()
122+
123+
pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)')
124+
matches = pattern.findall(content)
125+
for name, bit in matches:
126+
if not int(bit) in bits:
127+
bits[int(bit)] = name.lower()
128+
129+
types = {}
130+
for bit in bits:
131+
types[bits[bit]] = bit
132+
133+
self.bits = bits
134+
self.types = types
135+
pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter)
136+
137+
gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)

0 commit comments

Comments
 (0)