Skip to content

Commit ab589e4

Browse files
authored
Add GDB pretty-printers for zend_ast, zval (#13520)
1 parent 4058082 commit ab589e4

File tree

1 file changed

+229
-27
lines changed

1 file changed

+229
-27
lines changed

.gdb.py

Lines changed: 229 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
66
source .gdb.py
77
8-
Use
8+
If needed, pretty printers can be by-passed by using the /r flag:
99
(gdb) p /r any_variable
10-
to print |any_variable| without using any printers.
10+
11+
Use |set print pretty| to enable multi-line printing and indentation:
12+
(gdb) set print pretty on
13+
14+
Use |set print max-depth| to control the maximum print depth for nested
15+
structures:
16+
(gdb) set print pretty on
1117
1218
To interactively type Python for development of the printers, use
1319
(gdb) python foo = gdb.parse_and_eval('bar')
@@ -32,7 +38,10 @@ def to_string(self):
3238

3339
def children(self):
3440
for field in self.val.type.fields():
35-
yield (field.name, self.val[field.name])
41+
if field.name == 'val':
42+
yield ('val', self.format_string())
43+
else:
44+
yield (field.name, self.val[field.name])
3645

3746
def format_string(self):
3847
len = int(self.val['len'])
@@ -49,14 +58,16 @@ def format_string(self):
4958
str += ' (%d bytes total)' % int(self.val['len'])
5059

5160
return str
61+
62+
5263
pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)
5364

5465
class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):
5566
"Print a zend_type"
5667

5768
def __init__(self, val):
5869
self.val = val
59-
self.load_bits()
70+
load_type_bits()
6071

6172
def to_string(self):
6273
return self.format_type(self.val)
@@ -73,7 +84,7 @@ def format_type(self, t):
7384
meta = []
7485
for bit in range(0, type_mask_size):
7586
if type_mask & (1 << bit):
76-
type_name = self.bits.get(bit)
87+
type_name = type_bit_to_name.get(bit)
7788
match type_name:
7889
case None:
7990
parts.append('(1<<%d)' % bit)
@@ -104,34 +115,225 @@ def format_type(self, t):
104115

105116
return str
106117

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()
112118

113-
bits = {}
119+
pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter)
120+
121+
class ZendAstKindPrettyPrinter(gdb.printing.PrettyPrinter):
122+
"Print a zend_ast_kind"
123+
124+
def __init__(self, val):
125+
self.val = val
126+
127+
def to_string(self):
128+
return self.val.cast(gdb.lookup_type('enum _zend_ast_kind'))
129+
130+
131+
pp_set.add_printer('zend_ast_kind', '^zend_ast_kind$', ZendAstKindPrettyPrinter)
132+
133+
class ZendAstPrettyPrinter(gdb.printing.PrettyPrinter):
134+
"Print a zend_ast"
135+
136+
def __init__(self, val):
137+
self.val = val
138+
139+
def to_string(self):
140+
return '((%s*)0x%x)' % (str(self.cast().type), self.val.address)
141+
142+
def children(self):
143+
val = self.cast()
144+
for field in val.type.fields():
145+
if field.name == 'child':
146+
children = val[field.name]
147+
num_children = self.num_children()
148+
149+
ptr_type = gdb.lookup_type('zend_ast').pointer().pointer()
150+
children = children.cast(ptr_type)
151+
152+
for i in range(0, num_children):
153+
c = children[i]
154+
if int(c) != 0:
155+
c = c.dereference()
156+
yield ('child[%d]' % i, c)
157+
elif field.name == 'name':
158+
yield (field.name, ZendStringPrettyPrinter(val[field.name].dereference()).to_string())
159+
elif field.name == 'val':
160+
yield (field.name, ZvalPrettyPrinter(val[field.name]).to_string())
161+
else:
162+
yield (field.name, val[field.name])
163+
164+
def is_special(self):
165+
special_shift = 6 # ZEND_AST_SPECIAL_SHIFT
166+
kind = self.val['kind']
167+
return (kind >> special_shift) & 1
168+
169+
def is_decl(self):
170+
return self.is_special() and int(self.val['kind']) >= enum_value('ZEND_AST_FUNC_DECL')
171+
172+
def is_list(self):
173+
list_shift = 7 # ZEND_AST_IS_LIST_SHIFT
174+
kind = self.val['kind']
175+
return (kind >> list_shift) & 1
114176

115-
with open(filename, 'r') as file:
116-
content = file.read()
177+
def cast(self):
178+
kind = int(self.val['kind'])
117179

118-
pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)')
119-
matches = pattern.findall(content)
120-
for name, bit in matches:
180+
if kind == enum_value('ZEND_AST_ZVAL') or kind == enum_value('ZEND_AST_CONSTANT'):
181+
return self.val.cast(gdb.lookup_type('zend_ast_zval'))
182+
if kind == enum_value('ZEND_AST_ZNODE'):
183+
return self.val.cast(gdb.lookup_type('zend_ast_znode'))
184+
if self.is_decl():
185+
return self.val.cast(gdb.lookup_type('zend_ast_decl'))
186+
if self.is_list():
187+
return self.val.cast(gdb.lookup_type('zend_ast_list'))
188+
189+
return self.val
190+
191+
def num_children(self):
192+
if self.is_decl():
193+
decl_type = gdb.lookup_type('zend_ast_decl')
194+
child_type = decl_type['child'].type
195+
return array_size(child_type)
196+
if self.is_special():
197+
return 0
198+
elif self.is_list():
199+
return int(self.cast()['children'])
200+
else:
201+
num_children_shift = 8 # ZEND_AST_NUM_CHILDREN_SHIFT
202+
kind = self.val['kind']
203+
return kind >> num_children_shift
204+
205+
206+
pp_set.add_printer('zend_ast', '^_zend_ast$', ZendAstPrettyPrinter)
207+
208+
class ZvalPrettyPrinter(gdb.printing.PrettyPrinter):
209+
"Print a zval"
210+
211+
def __init__(self, val):
212+
self.val = val
213+
load_type_bits()
214+
215+
def to_string(self):
216+
return self.value_to_string()
217+
218+
def value_to_string(self):
219+
t = int(self.val['u1']['v']['type'])
220+
if t == type_name_to_bit['undef']:
221+
return 'undef'
222+
elif t == type_name_to_bit['null']:
223+
return 'null'
224+
elif t == type_name_to_bit['false']:
225+
return 'false'
226+
elif t == type_name_to_bit['true']:
227+
return 'true'
228+
elif t == type_name_to_bit['long']:
229+
return str(self.val['value']['lval'])
230+
elif t == type_name_to_bit['double']:
231+
return str(self.val['value']['dval'])
232+
elif t == type_name_to_bit['string']:
233+
return ZendStringPrettyPrinter(self.val['value']['str'].dereference()).to_string()
234+
elif t == type_name_to_bit['array']:
235+
return 'array'
236+
elif t == type_name_to_bit['object']:
237+
return 'object(%s)' % ZendStringPrettyPrinter(self.val['value']['obj']['ce']['name'].dereference()).to_string()
238+
elif t == type_name_to_bit['resource']:
239+
return 'resource'
240+
elif t == type_name_to_bit['reference']:
241+
return 'reference'
242+
elif t == type_name_to_bit['constant_ast']:
243+
return 'constant_ast'
244+
else:
245+
return 'zval of type %d' % int(self.val['u1']['v']['type'])
246+
247+
def children(self):
248+
for field in self.val.type.fields():
249+
if field.name == 'value':
250+
value = self.val['value']
251+
t = int(self.val['u1']['v']['type'])
252+
if t == type_name_to_bit['undef']:
253+
value = value['lval']
254+
elif t == type_name_to_bit['null']:
255+
value = value['lval']
256+
elif t == type_name_to_bit['false']:
257+
value = value['lval']
258+
elif t == type_name_to_bit['true']:
259+
value = value['lval']
260+
elif t == type_name_to_bit['long']:
261+
value = value['lval']
262+
elif t == type_name_to_bit['double']:
263+
value = value['dval']
264+
elif t == type_name_to_bit['string']:
265+
value = value['str'].dereference()
266+
elif t == type_name_to_bit['array']:
267+
value = value['ht'].dereference()
268+
elif t == type_name_to_bit['object']:
269+
value = value['obj'].dereference()
270+
elif t == type_name_to_bit['resource']:
271+
value = value['res'].dereference()
272+
elif t == type_name_to_bit['reference']:
273+
value = value['ref'].dereference()
274+
elif t == type_name_to_bit['constant_ast']:
275+
value = value['ast'].dereference()
276+
else:
277+
value = value['ptr']
278+
yield (field.name, value)
279+
elif field.name == 'u2':
280+
yield ('u2', self.val[field.name]['extra'])
281+
else:
282+
yield (field.name, self.val[field.name])
283+
284+
285+
pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter)
286+
287+
type_bit_to_name = None
288+
type_name_to_bit = None
289+
290+
def load_type_bits():
291+
global type_bit_to_name
292+
global type_name_to_bit
293+
294+
if type_bit_to_name != None:
295+
return
296+
297+
(symbol,_) = gdb.lookup_symbol("zend_gc_refcount")
298+
if symbol == None:
299+
raise "Could not find zend_types.h: symbol zend_gc_refcount not found"
300+
filename = symbol.symtab.fullname()
301+
302+
bits = {}
303+
304+
with open(filename, 'r') as file:
305+
content = file.read()
306+
307+
pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)')
308+
matches = pattern.findall(content)
309+
for name, bit in matches:
310+
bits[int(bit)] = name.lower()
311+
312+
pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)')
313+
matches = pattern.findall(content)
314+
for name, bit in matches:
315+
if not int(bit) in bits:
121316
bits[int(bit)] = name.lower()
122317

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()
318+
types = {}
319+
for bit in bits:
320+
types[bits[bit]] = bit
128321

129-
types = {}
130-
for bit in bits:
131-
types[bits[bit]] = bit
322+
type_bit_to_name = bits
323+
type_name_to_bit = types
132324

133-
self.bits = bits
134-
self.types = types
135-
pp_set.add_printer('zend_type', '^zend_type$', ZendTypePrettyPrinter)
325+
def lookup_symbol(name):
326+
(symbol, _) = gdb.lookup_symbol(name)
327+
if symbol == None:
328+
raise Exception("Could not lookup symbol %s" % name)
329+
return symbol
330+
331+
def enum_value(name):
332+
symbol = lookup_symbol(name)
333+
return int(symbol.value())
334+
335+
def array_size(ary_type):
336+
# array types have a single field whose type represents its range
337+
return ary_type.fields()[0].type.range()[1]+1
136338

137339
gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)

0 commit comments

Comments
 (0)