gdb_print_extension.py 7.55 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""gdb printing extension for Numba types.
"""
import re

try:
    import gdb.printing
    import gdb
except ImportError:
    raise ImportError("GDB python support is not available.")


class NumbaArrayPrinter:

    def __init__(self, val):
        self.val = val

    def to_string(self):
        try:
            import numpy as np
            HAVE_NUMPY = True
        except ImportError:
            HAVE_NUMPY = False

        try:
            NULL = 0x0

            # Raw data references, these need unpacking/interpreting.

            # Member "data" is...
            # DW_TAG_member of DIDerivedType, tag of DW_TAG_pointer_type
            # encoding e.g. DW_ATE_float
            data = self.val["data"]

            # Member "itemsize" is...
            # DW_TAG_member of DIBasicType encoding DW_ATE_signed
            itemsize = self.val["itemsize"]

            # Members "shape" and "strides" are...
            # DW_TAG_member of DIDerivedType, the type is a DICompositeType
            # (it's a Numba UniTuple) with tag: DW_TAG_array_type, i.e. it's an
            # array repr, it has a basetype of e.g. DW_ATE_unsigned and also
            # "elements" which are referenced with a DISubrange(count: <const>)
            # to say how many elements are in the array.
            rshp = self.val["shape"]
            rstrides = self.val["strides"]

            # bool on whether the data is aligned.
            is_aligned = False

            # type information decode, simple type:
            ty_str = str(self.val.type)
            if HAVE_NUMPY and ('aligned' in ty_str or 'Record' in ty_str):
                ty_str = ty_str.replace('unaligned ','').strip()
                matcher = re.compile(r"array\((Record.*), (.*), (.*)\)\ \(.*")
                # NOTE: need to deal with "Alignment" else dtype size is wrong
                arr_info = [x.strip() for x in matcher.match(ty_str).groups()]
                dtype_str, ndim_str, order_str = arr_info
                rstr = 'Record\\((.*\\[.*\\]);([0-9]+);(True|False)'
                rstr_match = re.match(rstr, dtype_str)
                # balign is unused, it's the alignment
                fields, balign, is_aligned_str = rstr_match.groups()
                is_aligned = is_aligned_str == 'True'
                field_dts = fields.split(',')
                struct_entries = []
                for f in field_dts:
                    splitted = f.split('[')
                    name = splitted[0]
                    dt_part = splitted[1:]
                    if len(dt_part) > 1:
                        raise TypeError('Unsupported sub-type: %s' % f)
                    else:
                        dt_part = dt_part[0]
                        if "nestedarray" in dt_part:
                            raise TypeError('Unsupported sub-type: %s' % f)
                        dt_as_str = dt_part.split(';')[0].split('=')[1]
                        dtype = np.dtype(dt_as_str)
                    struct_entries.append((name, dtype))
                    # The dtype is actually a record of some sort
                dtype_str = struct_entries
            else:  # simple type
                matcher = re.compile(r"array\((.*),(.*),(.*)\)\ \(.*")
                arr_info = [x.strip() for x in matcher.match(ty_str).groups()]
                dtype_str, ndim_str, order_str = arr_info
                # fix up unichr dtype
                if 'unichr x ' in dtype_str:
                    dtype_str = dtype_str[1:-1].replace('unichr x ', '<U')

            def dwarr2inttuple(dwarr):
                # Converts a gdb handle to a dwarf array to a tuple of ints
                fields = dwarr.type.fields()
                lo, hi = fields[0].type.range()
                return tuple([int(dwarr[x]) for x in range(lo, hi + 1)])

            # shape/strides extraction
            shape = dwarr2inttuple(rshp)
            strides = dwarr2inttuple(rstrides)

            # if data is not NULL
            if data != NULL:
                if HAVE_NUMPY:
                    # The data extent in bytes is:
                    # sum(shape * strides)
                    # get the data, then wire to as_strided
                    shp_arr = np.array([max(0, x - 1) for x in shape])
                    strd_arr = np.array(strides)
                    extent = np.sum(shp_arr * strd_arr)
                    extent += int(itemsize)
                    dtype_clazz = np.dtype(dtype_str, align=is_aligned)
                    dtype = dtype_clazz
                    this_proc = gdb.selected_inferior()
                    mem = this_proc.read_memory(int(data), extent)
                    arr_data = np.frombuffer(mem, dtype=dtype)
                    new_arr = np.lib.stride_tricks.as_strided(arr_data,
                                                              shape=shape,
                                                              strides=strides,)
                    return '\n' + str(new_arr)
                # Catch all for no NumPy
                return "array([...], dtype=%s, shape=%s)" % (dtype_str, shape)
            else:
                # Not yet initialized or NULLed out data
                buf = list(["NULL/Uninitialized"])
                return "array([" + ', '.join(buf) + "]" + ")"
        except Exception as e:
            return 'array[Exception: Failed to parse. %s]' % e


class NumbaComplexPrinter:

    def __init__(self, val):
        self.val = val

    def to_string(self):
        return "%s+%sj" % (self.val['real'], self.val['imag'])


class NumbaTuplePrinter:

    def __init__(self, val):
        self.val = val

    def to_string(self):
        buf = []
        fields = self.val.type.fields()
        for f in fields:
            buf.append(str(self.val[f.name]))
        return "(%s)" % ', '.join(buf)


class NumbaUniTuplePrinter:

    def __init__(self, val):
        self.val = val

    def to_string(self):
        # unituples are arrays
        fields = self.val.type.fields()
        lo, hi = fields[0].type.range()
        buf = []
        for i in range(lo, hi + 1):
            buf.append(str(self.val[i]))
        return "(%s)" % ', '.join(buf)


class NumbaUnicodeTypePrinter:

    def __init__(self, val):
        self.val = val

    def to_string(self):
        NULL = 0x0
        data = self.val["data"]
        nitems = self.val["length"]
        kind = self.val["kind"]
        if data != NULL:
            # This needs sorting out, encoding is wrong
            this_proc = gdb.selected_inferior()
            mem = this_proc.read_memory(int(data), nitems * kind)
            if isinstance(mem, memoryview):
                buf = bytes(mem).decode()
            else:
                buf = mem.decode('utf-8')
        else:
            buf = str(data)
        return "'%s'" % buf


def _create_printers():
    printer = gdb.printing.RegexpCollectionPrettyPrinter("Numba")
    printer.add_printer('Numba unaligned array printer', '^unaligned array\\(',
                        NumbaArrayPrinter)
    printer.add_printer('Numba array printer', '^array\\(', NumbaArrayPrinter)
    printer.add_printer('Numba complex printer', '^complex[0-9]+\\ ',
                        NumbaComplexPrinter)
    printer.add_printer('Numba Tuple printer', '^Tuple\\(',
                        NumbaTuplePrinter)
    printer.add_printer('Numba UniTuple printer', '^UniTuple\\(',
                        NumbaUniTuplePrinter)
    printer.add_printer('Numba unicode_type printer', '^unicode_type\\s+\\(',
                        NumbaUnicodeTypePrinter)
    return printer


# register the Numba pretty printers for the current object
gdb.printing.register_pretty_printer(gdb.current_objfile(), _create_printers())