master
  1# pretty printing for the standard library.
  2# put "source /path/to/stage2_gdb_pretty_printers.py" in ~/.gdbinit to load it automatically.
  3import re
  4import gdb.printing
  5
  6# Handles both ArrayList and ArrayListUnmanaged.
  7class 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
 24class MultiArrayListPrinter:
 25    def __init__(self, val):
 26        self.val = val
 27
 28    def child_type(self):
 29        (helper_fn, _) = gdb.lookup_symbol('%s.dbHelper' % 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.
 61class 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.dbHelper' % 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.
105class 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
137pp = gdb.printing.RegexpCollectionPrettyPrinter('Zig standard library')
138pp.add_printer('ArrayList', r'^std\.array_list\.ArrayListAligned(Unmanaged)?\(.*\)$', ArrayListPrinter)
139pp.add_printer('MultiArrayList', r'^std\.multi_array_list\.MultiArrayList\(.*\)$', MultiArrayListPrinter)
140pp.add_printer('HashMap', r'^std\.hash_map\.HashMap(Unmanaged)?\(.*\)$', HashMapPrinter)
141pp.add_printer('ArrayHashMap', r'^std\.array_hash_map\.ArrayHashMap(Unmanaged)?\(.*\)$', ArrayHashMapPrinter)
142gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)