Skip to content

Commit 9903a88

Browse files
committed
Add GDB pretty-printers for zend_string, zend_type
1 parent 5b501f2 commit 9903a88

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

.gdb.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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+
if field.name == 'val':
36+
yield ('val', self.format_string())
37+
else:
38+
yield (field.name, self.val[field.name])
39+
40+
def format_string(self):
41+
len = int(self.val['len'])
42+
truncated = False
43+
if len > 50:
44+
len = 50
45+
truncated = True
46+
47+
ptr_type = gdb.lookup_type('char').pointer()
48+
ary_type = gdb.lookup_type('char').array(len)
49+
str = self.val['val'].cast(ptr_type).dereference().cast(ary_type)
50+
str = str.format_string()
51+
if truncated:
52+
str += ' (%d bytes total)' % int(self.val['len'])
53+
54+
return str
55+
pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)
56+
57+
class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):
58+
"Print a zend_type"
59+
60+
def __init__(self, val):
61+
self.val = val
62+
self.load_bits()
63+
64+
def to_string(self):
65+
return self.format_type(self.val)
66+
67+
def children(self):
68+
for field in self.val.type.fields():
69+
yield (field.name, self.val[field.name])
70+
71+
def format_type(self, t):
72+
type_mask = int(t['type_mask'])
73+
type_mask_size = t['type_mask'].type.sizeof * 8
74+
separator = '|'
75+
parts = []
76+
meta = []
77+
for bit in range(0, type_mask_size):
78+
if type_mask & (1 << bit):
79+
type_name = self.bits.get(bit)
80+
match type_name:
81+
case None:
82+
parts.append('(1<<%d)' % bit)
83+
case 'list':
84+
list = t['ptr'].cast(gdb.lookup_type('zend_type_list').pointer())
85+
num_types = int(list['num_types'])
86+
types = list['types'].dereference().cast(gdb.lookup_type('zend_type').array(num_types))
87+
for i in range(0, num_types):
88+
str = self.format_type(types[i])
89+
if any((c in set('|&()')) for c in str):
90+
str = '(%s)' % str
91+
parts.append(str)
92+
case 'union' | 'arena':
93+
meta.append(type_name)
94+
case 'intersection':
95+
meta.append(type_name)
96+
separator = '&'
97+
case 'name':
98+
str = t['ptr'].cast(gdb.lookup_type('zend_string').pointer())
99+
parts.append(ZendStringPrettyPrinter(str).to_string())
100+
case _:
101+
parts.append(type_name)
102+
103+
str = separator.join(parts)
104+
105+
if len(meta) > 0:
106+
str = '[%s] %s' % (','.join(meta), str)
107+
108+
return str
109+
110+
def load_bits(self):
111+
(symbol,_) = gdb.lookup_symbol("zend_gc_refcount")
112+
if symbol == None:
113+
raise "Could not find zend_types.h: symbol zend_gc_refcount not found"
114+
filename = symbol.symtab.fullname()
115+
116+
bits = {}
117+
118+
with open(filename, 'r') as file:
119+
content = file.read()
120+
121+
pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)')
122+
matches = pattern.findall(content)
123+
for name, bit in matches:
124+
bits[int(bit)] = name.lower()
125+
126+
pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)')
127+
matches = pattern.findall(content)
128+
for name, bit in matches:
129+
if not int(bit) in bits:
130+
bits[int(bit)] = name.lower()
131+
132+
types = {}
133+
for bit in bits:
134+
types[bits[bit]] = bit
135+
136+
self.bits = bits
137+
self.types = types
138+
pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter)
139+
140+
gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)

0 commit comments

Comments
 (0)