• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from sys import version_info
2
3import gdb
4
5if version_info[0] >= 3:
6    xrange = range
7
8ZERO_FIELD = "__0"
9FIRST_FIELD = "__1"
10
11
12def unwrap_unique_or_non_null(unique_or_nonnull):
13    # BACKCOMPAT: rust 1.32
14    # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067
15    # BACKCOMPAT: rust 1.60
16    # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f
17    ptr = unique_or_nonnull["pointer"]
18    return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]]
19
20
21# GDB 14 has a tag class that indicates that extension methods are ok
22# to call.  Use of this tag only requires that printers hide local
23# attributes and methods by prefixing them with "_".
24if hasattr(gdb, 'ValuePrinter'):
25    printer_base = gdb.ValuePrinter
26else:
27    printer_base = object
28
29
30class EnumProvider(printer_base):
31    def __init__(self, valobj):
32        content = valobj[valobj.type.fields()[0]]
33        fields = content.type.fields()
34        self._empty = len(fields) == 0
35        if not self._empty:
36            if len(fields) == 1:
37                discriminant = 0
38            else:
39                discriminant = int(content[fields[0]]) + 1
40            self._active_variant = content[fields[discriminant]]
41            self._name = fields[discriminant].name
42            self._full_name = "{}::{}".format(valobj.type.name, self._name)
43        else:
44            self._full_name = valobj.type.name
45
46    def to_string(self):
47        return self._full_name
48
49    def children(self):
50        if not self._empty:
51            yield self._name, self._active_variant
52
53
54class StdStringProvider(printer_base):
55    def __init__(self, valobj):
56        self._valobj = valobj
57        vec = valobj["vec"]
58        self._length = int(vec["len"])
59        self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"])
60
61    def to_string(self):
62        return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
63
64    @staticmethod
65    def display_hint():
66        return "string"
67
68
69class StdOsStringProvider(printer_base):
70    def __init__(self, valobj):
71        self._valobj = valobj
72        buf = self._valobj["inner"]["inner"]
73        is_windows = "Wtf8Buf" in buf.type.name
74        vec = buf[ZERO_FIELD] if is_windows else buf
75
76        self._length = int(vec["len"])
77        self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"])
78
79    def to_string(self):
80        return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
81
82    def display_hint(self):
83        return "string"
84
85
86class StdStrProvider(printer_base):
87    def __init__(self, valobj):
88        self._valobj = valobj
89        self._length = int(valobj["length"])
90        self._data_ptr = valobj["data_ptr"]
91
92    def to_string(self):
93        return self._data_ptr.lazy_string(encoding="utf-8", length=self._length)
94
95    @staticmethod
96    def display_hint():
97        return "string"
98
99
100def _enumerate_array_elements(element_ptrs):
101    for (i, element_ptr) in enumerate(element_ptrs):
102        key = "[{}]".format(i)
103        element = element_ptr.dereference()
104
105        try:
106            # rust-lang/rust#64343: passing deref expr to `str` allows
107            # catching exception on garbage pointer
108            str(element)
109        except RuntimeError:
110            yield key, "inaccessible"
111
112            break
113
114        yield key, element
115
116
117class StdSliceProvider(printer_base):
118    def __init__(self, valobj):
119        self._valobj = valobj
120        self._length = int(valobj["length"])
121        self._data_ptr = valobj["data_ptr"]
122
123    def to_string(self):
124        return "{}(size={})".format(self._valobj.type, self._length)
125
126    def children(self):
127        return _enumerate_array_elements(
128            self._data_ptr + index for index in xrange(self._length)
129        )
130
131    @staticmethod
132    def display_hint():
133        return "array"
134
135
136class StdVecProvider(printer_base):
137    def __init__(self, valobj):
138        self._valobj = valobj
139        self._length = int(valobj["len"])
140        self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"])
141        ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0))
142        self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty)
143
144    def to_string(self):
145        return "Vec(size={})".format(self._length)
146
147    def children(self):
148        return _enumerate_array_elements(
149            self._data_ptr + index for index in xrange(self._length)
150        )
151
152    @staticmethod
153    def display_hint():
154        return "array"
155
156
157class StdVecDequeProvider(printer_base):
158    def __init__(self, valobj):
159        self._valobj = valobj
160        self._head = int(valobj["head"])
161        self._size = int(valobj["len"])
162        # BACKCOMPAT: rust 1.75
163        cap = valobj["buf"]["inner"]["cap"]
164        if cap.type.code != gdb.TYPE_CODE_INT:
165            cap = cap[ZERO_FIELD]
166        self._cap = int(cap)
167        self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"])
168        ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0))
169        self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty)
170
171    def to_string(self):
172        return "VecDeque(size={})".format(self._size)
173
174    def children(self):
175        return _enumerate_array_elements(
176            (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size)
177        )
178
179    @staticmethod
180    def display_hint():
181        return "array"
182
183
184class StdRcProvider(printer_base):
185    def __init__(self, valobj, is_atomic=False):
186        self._valobj = valobj
187        self._is_atomic = is_atomic
188        self._ptr = unwrap_unique_or_non_null(valobj["ptr"])
189        self._value = self._ptr["data" if is_atomic else "value"]
190        self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"]
191        self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
192
193    def to_string(self):
194        if self._is_atomic:
195            return "Arc(strong={}, weak={})".format(int(self._strong), int(self._weak))
196        else:
197            return "Rc(strong={}, weak={})".format(int(self._strong), int(self._weak))
198
199    def children(self):
200        yield "value", self._value
201        yield "strong", self._strong
202        yield "weak", self._weak
203
204
205class StdCellProvider(printer_base):
206    def __init__(self, valobj):
207        self._value = valobj["value"]["value"]
208
209    def to_string(self):
210        return "Cell"
211
212    def children(self):
213        yield "value", self._value
214
215
216class StdRefProvider(printer_base):
217    def __init__(self, valobj):
218        self._value = valobj["value"].dereference()
219        self._borrow = valobj["borrow"]["borrow"]["value"]["value"]
220
221    def to_string(self):
222        borrow = int(self._borrow)
223        if borrow >= 0:
224            return "Ref(borrow={})".format(borrow)
225        else:
226            return "Ref(borrow_mut={})".format(-borrow)
227
228    def children(self):
229        yield "*value", self._value
230        yield "borrow", self._borrow
231
232
233class StdRefCellProvider(printer_base):
234    def __init__(self, valobj):
235        self._value = valobj["value"]["value"]
236        self._borrow = valobj["borrow"]["value"]["value"]
237
238    def to_string(self):
239        borrow = int(self._borrow)
240        if borrow >= 0:
241            return "RefCell(borrow={})".format(borrow)
242        else:
243            return "RefCell(borrow_mut={})".format(-borrow)
244
245    def children(self):
246        yield "value", self._value
247        yield "borrow", self._borrow
248
249
250class StdNonZeroNumberProvider(printer_base):
251    def __init__(self, valobj):
252        fields = valobj.type.fields()
253        assert len(fields) == 1
254        field = list(fields)[0]
255
256        inner_valobj = valobj[field.name]
257
258        inner_fields = inner_valobj.type.fields()
259        assert len(inner_fields) == 1
260        inner_field = list(inner_fields)[0]
261
262        self._value = str(inner_valobj[inner_field.name])
263
264    def to_string(self):
265        return self._value
266
267
268# Yields children (in a provider's sense of the word) for a BTreeMap.
269def children_of_btree_map(map):
270    # Yields each key/value pair in the node and in any child nodes.
271    def children_of_node(node_ptr, height):
272        def cast_to_internal(node):
273            internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
274            internal_type = gdb.lookup_type(internal_type_name)
275            return node.cast(internal_type.pointer())
276
277        if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
278            # BACKCOMPAT: rust 1.49
279            node_ptr = node_ptr["ptr"]
280        node_ptr = unwrap_unique_or_non_null(node_ptr)
281        leaf = node_ptr.dereference()
282        keys = leaf["keys"]
283        vals = leaf["vals"]
284        edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
285        length = leaf["len"]
286
287        for i in xrange(0, length + 1):
288            if height > 0:
289                child_ptr = edges[i]["value"]["value"]
290                for child in children_of_node(child_ptr, height - 1):
291                    yield child
292            if i < length:
293                # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
294                key_type_size = keys.type.sizeof
295                val_type_size = vals.type.sizeof
296                key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()")
297                val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()")
298                yield key, val
299
300    if map["length"] > 0:
301        root = map["root"]
302        if root.type.name.startswith("core::option::Option<"):
303            root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
304        node_ptr = root["node"]
305        height = root["height"]
306        for child in children_of_node(node_ptr, height):
307            yield child
308
309
310class StdBTreeSetProvider(printer_base):
311    def __init__(self, valobj):
312        self._valobj = valobj
313
314    def to_string(self):
315        return "BTreeSet(size={})".format(self._valobj["map"]["length"])
316
317    def children(self):
318        inner_map = self._valobj["map"]
319        for i, (child, _) in enumerate(children_of_btree_map(inner_map)):
320            yield "[{}]".format(i), child
321
322    @staticmethod
323    def display_hint():
324        return "array"
325
326
327class StdBTreeMapProvider(printer_base):
328    def __init__(self, valobj):
329        self._valobj = valobj
330
331    def to_string(self):
332        return "BTreeMap(size={})".format(self._valobj["length"])
333
334    def children(self):
335        for i, (key, val) in enumerate(children_of_btree_map(self._valobj)):
336            yield "key{}".format(i), key
337            yield "val{}".format(i), val
338
339    @staticmethod
340    def display_hint():
341        return "map"
342
343
344# BACKCOMPAT: rust 1.35
345class StdOldHashMapProvider(printer_base):
346    def __init__(self, valobj, show_values=True):
347        self._valobj = valobj
348        self._show_values = show_values
349
350        self._table = self._valobj["table"]
351        self._size = int(self._table["size"])
352        self._hashes = self._table["hashes"]
353        self._hash_uint_type = self._hashes.type
354        self._hash_uint_size = self._hashes.type.sizeof
355        self._modulo = 2 ** self._hash_uint_size
356        self._data_ptr = self._hashes[ZERO_FIELD]["pointer"]
357
358        self._capacity_mask = int(self._table["capacity_mask"])
359        self._capacity = (self._capacity_mask + 1) % self._modulo
360
361        marker = self._table["marker"].type
362        self._pair_type = marker.template_argument(0)
363        self._pair_type_size = self._pair_type.sizeof
364
365        self._valid_indices = []
366        for idx in range(self._capacity):
367            data_ptr = self._data_ptr.cast(self._hash_uint_type.pointer())
368            address = data_ptr + idx
369            hash_uint = address.dereference()
370            hash_ptr = hash_uint[ZERO_FIELD]["pointer"]
371            if int(hash_ptr) != 0:
372                self._valid_indices.append(idx)
373
374    def to_string(self):
375        if self._show_values:
376            return "HashMap(size={})".format(self._size)
377        else:
378            return "HashSet(size={})".format(self._size)
379
380    def children(self):
381        start = int(self._data_ptr) & ~1
382
383        hashes = self._hash_uint_size * self._capacity
384        align = self._pair_type_size
385        len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~(
386                (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo
387
388        pairs_offset = hashes + len_rounded_up
389        pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer())
390
391        for index in range(self._size):
392            table_index = self._valid_indices[index]
393            idx = table_index & self._capacity_mask
394            element = (pairs_start + idx).dereference()
395            if self._show_values:
396                yield "key{}".format(index), element[ZERO_FIELD]
397                yield "val{}".format(index), element[FIRST_FIELD]
398            else:
399                yield "[{}]".format(index), element[ZERO_FIELD]
400
401    def display_hint(self):
402        return "map" if self._show_values else "array"
403
404
405class StdHashMapProvider(printer_base):
406    def __init__(self, valobj, show_values=True):
407        self._valobj = valobj
408        self._show_values = show_values
409
410        table = self._table()
411        table_inner = table["table"]
412        capacity = int(table_inner["bucket_mask"]) + 1
413        ctrl = table_inner["ctrl"]["pointer"]
414
415        self._size = int(table_inner["items"])
416        self._pair_type = table.type.template_argument(0).strip_typedefs()
417
418        self._new_layout = not table_inner.type.has_key("data")
419        if self._new_layout:
420            self._data_ptr = ctrl.cast(self._pair_type.pointer())
421        else:
422            self._data_ptr = table_inner["data"]["pointer"]
423
424        self._valid_indices = []
425        for idx in range(capacity):
426            address = ctrl + idx
427            value = address.dereference()
428            is_presented = value & 128 == 0
429            if is_presented:
430                self._valid_indices.append(idx)
431
432    def _table(self):
433        if self._show_values:
434            hashbrown_hashmap = self._valobj["base"]
435        elif self._valobj.type.fields()[0].name == "map":
436            # BACKCOMPAT: rust 1.47
437            # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap
438            hashbrown_hashmap = self._valobj["map"]["base"]
439        else:
440            # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap
441            hashbrown_hashmap = self._valobj["base"]["map"]
442        return hashbrown_hashmap["table"]
443
444    def to_string(self):
445        if self._show_values:
446            return "HashMap(size={})".format(self._size)
447        else:
448            return "HashSet(size={})".format(self._size)
449
450    def children(self):
451        pairs_start = self._data_ptr
452
453        for index in range(self._size):
454            idx = self._valid_indices[index]
455            if self._new_layout:
456                idx = -(idx + 1)
457            element = (pairs_start + idx).dereference()
458            if self._show_values:
459                yield "key{}".format(index), element[ZERO_FIELD]
460                yield "val{}".format(index), element[FIRST_FIELD]
461            else:
462                yield "[{}]".format(index), element[ZERO_FIELD]
463
464    def display_hint(self):
465        return "map" if self._show_values else "array"
466