Skip to content

Commit 4ff7553

Browse files
committed
gdb: restructure pretty printers into different files
1 parent 0bd84e0 commit 4ff7553

5 files changed

+370
-391
lines changed

tools/stage1_gdb_pretty_printers.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# pretty printing for stage1.
2+
# put "source /path/to/stage1_gdb_pretty_printers.py" in ~/.gdbinit to load it automatically.
3+
import gdb.printing
4+
5+
class ZigListPrinter:
6+
def __init__(self, val):
7+
self.val = val
8+
9+
def to_string(self):
10+
return '%s of length %d, capacity %d' % (self.val.type.name, int(self.val['length']), int(self.val['capacity']))
11+
12+
def children(self):
13+
def it(ziglist):
14+
for i in range(int(ziglist.val['length'])):
15+
item = ziglist.val['items'] + i
16+
yield ('[%d]' % i, item.dereference())
17+
return it(self)
18+
19+
def display_hint(self):
20+
return 'array'
21+
22+
# handle both Buf and ZigList<char> because Buf* doesn't work otherwise (gdb bug?)
23+
class BufPrinter:
24+
def __init__(self, val):
25+
self.val = val['list'] if val.type.name == 'Buf' else val
26+
27+
def to_string(self):
28+
return self.val['items'].string(length=int(self.val['length']))
29+
30+
def display_hint(self):
31+
return 'string'
32+
33+
pp = gdb.printing.RegexpCollectionPrettyPrinter('Zig stage1 compiler')
34+
pp.add_printer('Buf', '^Buf$', BufPrinter)
35+
pp.add_printer('ZigList<char>', '^ZigList<char>$', BufPrinter)
36+
pp.add_printer('ZigList', '^ZigList<.*>$', ZigListPrinter)
37+
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)

tools/stage2_gdb_pretty_printers.py

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# pretty printing for stage 2.
2+
# put "source /path/to/stage2_gdb_pretty_printers.py" in ~/.gdbinit to load it automatically.
3+
import re
4+
import gdb.printing
5+
6+
class TypePrinter:
7+
no_payload_count = 4096
8+
9+
# Keep in sync with src/type.zig
10+
# Types which have no payload do not need to be entered here.
11+
payload_type_names = {
12+
'array_u8': 'type.Len',
13+
'array_u8_sentinel_0': 'Len',
14+
15+
'single_const_pointer': 'ElemType',
16+
'single_mut_pointer': 'ElemType',
17+
'many_const_pointer': 'ElemType',
18+
'many_mut_pointer': 'ElemType',
19+
'c_const_pointer': 'ElemType',
20+
'c_mut_pointer': 'ElemType',
21+
'const_slice': 'ElemType',
22+
'mut_slice': 'ElemType',
23+
'optional': 'ElemType',
24+
'optional_single_mut_pointer': 'ElemType',
25+
'optional_single_const_pointer': 'ElemType',
26+
'anyframe_T': 'ElemType',
27+
28+
'int_signed': 'Bits',
29+
'int_unsigned': 'Bits',
30+
31+
'error_set': 'ErrorSet',
32+
'error_set_inferred': 'ErrorSetInferred',
33+
'error_set_merged': 'ErrorSetMerged',
34+
35+
'array': 'Array',
36+
'vector': 'Array',
37+
38+
'array_sentinel': 'ArraySentinel',
39+
'pointer': 'Pointer',
40+
'function': 'Function',
41+
'error_union': 'ErrorUnion',
42+
'error_set_single': 'Name',
43+
'opaque': 'Opaque',
44+
'struct': 'Struct',
45+
'union': 'Union',
46+
'union_tagged': 'Union',
47+
'enum_full, .enum_nonexhaustive': 'EnumFull',
48+
'enum_simple': 'EnumSimple',
49+
'enum_numbered': 'EnumNumbered',
50+
'empty_struct': 'ContainerScope',
51+
'tuple': 'Tuple',
52+
'anon_struct': 'AnonStruct',
53+
}
54+
55+
def __init__(self, val):
56+
self.val = val
57+
58+
def tag(self):
59+
tag_if_small_enough = self.val['tag_if_small_enough']
60+
tag_type = tag_if_small_enough.type
61+
62+
if tag_if_small_enough < TypePrinter.no_payload_count:
63+
return tag_if_small_enough
64+
else:
65+
return self.val['ptr_otherwise'].dereference()['tag']
66+
67+
def payload_type(self):
68+
tag = self.tag()
69+
if tag is None:
70+
return None
71+
72+
type_name = TypePrinter.payload_type_names.get(str(tag))
73+
if type_name is None:
74+
return None
75+
return gdb.lookup_type('struct type.%s' % type_name)
76+
77+
def to_string(self):
78+
tag = self.tag()
79+
if tag is None:
80+
return '(invalid type)'
81+
if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
82+
return '.%s' % str(tag)
83+
return None
84+
85+
def children(self):
86+
if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
87+
return
88+
89+
yield ('tag', '.%s' % str(self.tag()))
90+
91+
payload_type = self.payload_type()
92+
if payload_type is not None:
93+
yield ('payload', self.val['ptr_otherwise'].cast(payload_type.pointer()).dereference()['data'])
94+
95+
class ValuePrinter:
96+
no_payload_count = 4096
97+
98+
# Keep in sync with src/value.zig
99+
# Values which have no payload do not need to be entered here.
100+
payload_type_names = {
101+
'big_int_positive': 'BigInt',
102+
'big_int_negative': 'BigInt',
103+
104+
'extern_fn': 'ExternFn',
105+
106+
'decl_ref': 'Decl',
107+
108+
'repeated': 'SubValue',
109+
'eu_payload': 'SubValue',
110+
'opt_payload': 'SubValue',
111+
'empty_array_sentinel': 'SubValue',
112+
113+
'eu_payload_ptr': 'PayloadPtr',
114+
'opt_payload_ptr': 'PayloadPtr',
115+
116+
'bytes': 'Bytes',
117+
'enum_literal': 'Bytes',
118+
119+
'slice': 'Slice',
120+
121+
'enum_field_index': 'U32',
122+
123+
'ty': 'Ty',
124+
'int_type': 'IntType',
125+
'int_u64': 'U64',
126+
'int_i64': 'I64',
127+
'function': 'Function',
128+
'variable': 'Variable',
129+
'decl_ref_mut': 'DeclRefMut',
130+
'elem_ptr': 'ElemPtr',
131+
'field_ptr': 'FieldPtr',
132+
'float_16': 'Float_16',
133+
'float_32': 'Float_32',
134+
'float_64': 'Float_64',
135+
'float_80': 'Float_80',
136+
'float_128': 'Float_128',
137+
'error': 'Error',
138+
'inferred_alloc': 'InferredAlloc',
139+
'inferred_alloc_comptime': 'InferredAllocComptime',
140+
'aggregate': 'Aggregate',
141+
'union': 'Union',
142+
'bound_fn': 'BoundFn',
143+
}
144+
145+
def __init__(self, val):
146+
self.val = val
147+
148+
def tag(self):
149+
tag_if_small_enough = self.val['tag_if_small_enough']
150+
tag_type = tag_if_small_enough.type
151+
152+
if tag_if_small_enough < ValuePrinter.no_payload_count:
153+
return tag_if_small_enough
154+
else:
155+
return self.val['ptr_otherwise'].dereference()['tag']
156+
157+
def payload_type(self):
158+
tag = self.tag()
159+
if tag is None:
160+
return None
161+
162+
type_name = ValuePrinter.payload_type_names.get(str(tag))
163+
if type_name is None:
164+
return None
165+
return gdb.lookup_type('struct value.%s' % type_name)
166+
167+
def to_string(self):
168+
tag = self.tag()
169+
if tag is None:
170+
return '(invalid value)'
171+
if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
172+
return '.%s' % str(tag)
173+
return None
174+
175+
def children(self):
176+
if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
177+
return
178+
179+
yield ('tag', '.%s' % str(self.tag()))
180+
181+
payload_type = self.payload_type()
182+
if payload_type is not None:
183+
yield ('payload', self.val['ptr_otherwise'].cast(payload_type.pointer()).dereference()['data'])
184+
185+
pp = gdb.printing.RegexpCollectionPrettyPrinter('Zig stage2 compiler')
186+
pp.add_printer('Type', r'^type\.Type$', TypePrinter)
187+
pp.add_printer('Value', r'^value\.Value$', ValuePrinter)
188+
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)
189+

tools/std_gdb_pretty_printers.py

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# pretty printing for the standard library.
2+
# put "source /path/to/stage2_gdb_pretty_printers.py" in ~/.gdbinit to load it automatically.
3+
import re
4+
import gdb.printing
5+
6+
# Handles both ArrayList and ArrayListUnmanaged.
7+
class ArrayListPrinter:
8+
def __init__(self, val):
9+
self.val = val
10+
11+
def to_string(self):
12+
type = self.val.type.name[len('std.array_list.'):]
13+
type = re.sub(r'^ArrayListAligned(Unmanaged)?\((.*),null\)$', r'ArrayList\1(\2)', type)
14+
return '%s of length %s, capacity %s' % (type, self.val['items']['len'], self.val['capacity'])
15+
16+
def children(self):
17+
for i in range(self.val['items']['len']):
18+
item = self.val['items']['ptr'] + i
19+
yield ('[%d]' % i, item.dereference())
20+
21+
def display_hint(self):
22+
return 'array'
23+
24+
class MultiArrayListPrinter:
25+
def __init__(self, val):
26+
self.val = val
27+
28+
def child_type(self):
29+
(helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
30+
return helper_fn.type.fields()[1].type.target()
31+
32+
def to_string(self):
33+
type = self.val.type.name[len('std.multi_array_list.'):]
34+
return '%s of length %s, capacity %s' % (type, self.val['len'], self.val['capacity'])
35+
36+
def slice(self):
37+
fields = self.child_type().fields()
38+
base = self.val['bytes']
39+
cap = self.val['capacity']
40+
len = self.val['len']
41+
42+
if len == 0:
43+
return
44+
45+
fields = sorted(fields, key=lambda field: field.type.alignof, reverse=True)
46+
47+
for field in fields:
48+
ptr = base.cast(field.type.pointer()).dereference().cast(field.type.array(len - 1))
49+
base += field.type.sizeof * cap
50+
yield (field.name, ptr)
51+
52+
def children(self):
53+
for i, (name, ptr) in enumerate(self.slice()):
54+
yield ('[%d]' % i, name)
55+
yield ('[%d]' % i, ptr)
56+
57+
def display_hint(self):
58+
return 'map'
59+
60+
# Handles both HashMap and HashMapUnmanaged.
61+
class HashMapPrinter:
62+
def __init__(self, val):
63+
self.type = val.type
64+
is_managed = re.search(r'^std\.hash_map\.HashMap\(', self.type.name)
65+
self.val = val['unmanaged'] if is_managed else val
66+
67+
def header_ptr_type(self):
68+
(helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
69+
return helper_fn.type.fields()[1].type
70+
71+
def header(self):
72+
if self.val['metadata'] == 0:
73+
return None
74+
return (self.val['metadata'].cast(self.header_ptr_type()) - 1).dereference()
75+
76+
def to_string(self):
77+
type = self.type.name[len('std.hash_map.'):]
78+
type = re.sub(r'^HashMap(Unmanaged)?\((.*),std.hash_map.AutoContext\(.*$', r'AutoHashMap\1(\2)', type)
79+
hdr = self.header()
80+
if hdr is not None:
81+
cap = hdr['capacity']
82+
else:
83+
cap = 0
84+
return '%s of length %s, capacity %s' % (type, self.val['size'], cap)
85+
86+
def children(self):
87+
hdr = self.header()
88+
if hdr is None:
89+
return
90+
is_map = self.display_hint() == 'map'
91+
for i in range(hdr['capacity']):
92+
metadata = self.val['metadata'] + i
93+
if metadata.dereference()['used'] == 1:
94+
yield ('[%d]' % i, (hdr['keys'] + i).dereference())
95+
if is_map:
96+
yield ('[%d]' % i, (hdr['values'] + i).dereference())
97+
98+
def display_hint(self):
99+
for field in self.header_ptr_type().target().fields():
100+
if field.name == 'values':
101+
return 'map'
102+
return 'array'
103+
104+
# Handles both ArrayHashMap and ArrayHashMapUnmanaged.
105+
class ArrayHashMapPrinter:
106+
def __init__(self, val):
107+
self.type = val.type
108+
is_managed = re.search(r'^std\.array_hash_map\.ArrayHashMap\(', self.type.name)
109+
self.val = val['unmanaged'] if is_managed else val
110+
111+
def to_string(self):
112+
type = self.type.name[len('std.array_hash_map.'):]
113+
type = re.sub(r'^ArrayHashMap(Unmanaged)?\((.*),std.array_hash_map.AutoContext\(.*$', r'AutoArrayHashMap\1(\2)', type)
114+
return '%s of length %s' % (type, self.val['entries']['len'])
115+
116+
def children(self):
117+
entries = MultiArrayListPrinter(self.val['entries'])
118+
len = self.val['entries']['len']
119+
fields = {}
120+
for name, ptr in entries.slice():
121+
fields[str(name)] = ptr
122+
123+
for i in range(len):
124+
if 'key' in fields:
125+
yield ('[%d]' % i, fields['key'][i])
126+
else:
127+
yield ('[%d]' % i, '{}')
128+
if 'value' in fields:
129+
yield ('[%d]' % i, fields['value'][i])
130+
131+
def display_hint(self):
132+
for name, ptr in MultiArrayListPrinter(self.val['entries']).slice():
133+
if name == 'value':
134+
return 'map'
135+
return 'array'
136+
137+
pp = gdb.printing.RegexpCollectionPrettyPrinter('Zig standard library')
138+
pp.add_printer('ArrayList', r'^std\.array_list\.ArrayListAligned(Unmanaged)?\(.*\)$', ArrayListPrinter)
139+
pp.add_printer('MultiArrayList', r'^std\.multi_array_list\.MultiArrayList\(.*\)$', MultiArrayListPrinter)
140+
pp.add_printer('HashMap', r'^std\.hash_map\.HashMap(Unmanaged)?\(.*\)$', HashMapPrinter)
141+
pp.add_printer('ArrayHashMap', r'^std\.array_hash_map\.ArrayHashMap(Unmanaged)?\(.*\)$', ArrayHashMapPrinter)
142+
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)

0 commit comments

Comments
 (0)