xref: /aosp_15_r20/prebuilts/rust/darwin-x86/1.80.1/lib/rustlib/etc/gdb_providers.py (revision b40554a23088fb75aa6945dfe8e65169c8484da3)
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"]["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"]["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
99def _enumerate_array_elements(element_ptrs):
100    for (i, element_ptr) in enumerate(element_ptrs):
101        key = "[{}]".format(i)
102        element = element_ptr.dereference()
103
104        try:
105            # rust-lang/rust#64343: passing deref expr to `str` allows
106            # catching exception on garbage pointer
107            str(element)
108        except RuntimeError:
109            yield key, "inaccessible"
110
111            break
112
113        yield key, element
114
115class StdSliceProvider(printer_base):
116    def __init__(self, valobj):
117        self._valobj = valobj
118        self._length = int(valobj["length"])
119        self._data_ptr = valobj["data_ptr"]
120
121    def to_string(self):
122        return "{}(size={})".format(self._valobj.type, self._length)
123
124    def children(self):
125        return _enumerate_array_elements(
126            self._data_ptr + index for index in xrange(self._length)
127        )
128
129    @staticmethod
130    def display_hint():
131        return "array"
132
133class StdVecProvider(printer_base):
134    def __init__(self, valobj):
135        self._valobj = valobj
136        self._length = int(valobj["len"])
137        self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
138
139    def to_string(self):
140        return "Vec(size={})".format(self._length)
141
142    def children(self):
143        return _enumerate_array_elements(
144            self._data_ptr + index for index in xrange(self._length)
145        )
146
147    @staticmethod
148    def display_hint():
149        return "array"
150
151
152class StdVecDequeProvider(printer_base):
153    def __init__(self, valobj):
154        self._valobj = valobj
155        self._head = int(valobj["head"])
156        self._size = int(valobj["len"])
157        # BACKCOMPAT: rust 1.75
158        cap = valobj["buf"]["cap"]
159        if cap.type.code != gdb.TYPE_CODE_INT:
160            cap = cap[ZERO_FIELD]
161        self._cap = int(cap)
162        self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
163
164    def to_string(self):
165        return "VecDeque(size={})".format(self._size)
166
167    def children(self):
168        return _enumerate_array_elements(
169            (self._data_ptr + ((self._head + index) % self._cap)) for index in xrange(self._size)
170        )
171
172    @staticmethod
173    def display_hint():
174        return "array"
175
176
177class StdRcProvider(printer_base):
178    def __init__(self, valobj, is_atomic=False):
179        self._valobj = valobj
180        self._is_atomic = is_atomic
181        self._ptr = unwrap_unique_or_non_null(valobj["ptr"])
182        self._value = self._ptr["data" if is_atomic else "value"]
183        self._strong = self._ptr["strong"]["v" if is_atomic else "value"]["value"]
184        self._weak = self._ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
185
186    def to_string(self):
187        if self._is_atomic:
188            return "Arc(strong={}, weak={})".format(int(self._strong), int(self._weak))
189        else:
190            return "Rc(strong={}, weak={})".format(int(self._strong), int(self._weak))
191
192    def children(self):
193        yield "value", self._value
194        yield "strong", self._strong
195        yield "weak", self._weak
196
197
198class StdCellProvider(printer_base):
199    def __init__(self, valobj):
200        self._value = valobj["value"]["value"]
201
202    def to_string(self):
203        return "Cell"
204
205    def children(self):
206        yield "value", self._value
207
208
209class StdRefProvider(printer_base):
210    def __init__(self, valobj):
211        self._value = valobj["value"].dereference()
212        self._borrow = valobj["borrow"]["borrow"]["value"]["value"]
213
214    def to_string(self):
215        borrow = int(self._borrow)
216        if borrow >= 0:
217            return "Ref(borrow={})".format(borrow)
218        else:
219            return "Ref(borrow_mut={})".format(-borrow)
220
221    def children(self):
222        yield "*value", self._value
223        yield "borrow", self._borrow
224
225
226class StdRefCellProvider(printer_base):
227    def __init__(self, valobj):
228        self._value = valobj["value"]["value"]
229        self._borrow = valobj["borrow"]["value"]["value"]
230
231    def to_string(self):
232        borrow = int(self._borrow)
233        if borrow >= 0:
234            return "RefCell(borrow={})".format(borrow)
235        else:
236            return "RefCell(borrow_mut={})".format(-borrow)
237
238    def children(self):
239        yield "value", self._value
240        yield "borrow", self._borrow
241
242
243class StdNonZeroNumberProvider(printer_base):
244    def __init__(self, valobj):
245        fields = valobj.type.fields()
246        assert len(fields) == 1
247        field = list(fields)[0]
248
249        inner_valobj = valobj[field.name]
250
251        inner_fields = inner_valobj.type.fields()
252        assert len(inner_fields) == 1
253        inner_field = list(inner_fields)[0]
254
255        self._value = str(inner_valobj[inner_field.name])
256
257    def to_string(self):
258        return self._value
259
260
261# Yields children (in a provider's sense of the word) for a BTreeMap.
262def children_of_btree_map(map):
263    # Yields each key/value pair in the node and in any child nodes.
264    def children_of_node(node_ptr, height):
265        def cast_to_internal(node):
266            internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
267            internal_type = gdb.lookup_type(internal_type_name)
268            return node.cast(internal_type.pointer())
269
270        if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
271            # BACKCOMPAT: rust 1.49
272            node_ptr = node_ptr["ptr"]
273        node_ptr = unwrap_unique_or_non_null(node_ptr)
274        leaf = node_ptr.dereference()
275        keys = leaf["keys"]
276        vals = leaf["vals"]
277        edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
278        length = leaf["len"]
279
280        for i in xrange(0, length + 1):
281            if height > 0:
282                child_ptr = edges[i]["value"]["value"]
283                for child in children_of_node(child_ptr, height - 1):
284                    yield child
285            if i < length:
286                # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
287                key_type_size = keys.type.sizeof
288                val_type_size = vals.type.sizeof
289                key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()")
290                val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()")
291                yield key, val
292
293    if map["length"] > 0:
294        root = map["root"]
295        if root.type.name.startswith("core::option::Option<"):
296            root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
297        node_ptr = root["node"]
298        height = root["height"]
299        for child in children_of_node(node_ptr, height):
300            yield child
301
302
303class StdBTreeSetProvider(printer_base):
304    def __init__(self, valobj):
305        self._valobj = valobj
306
307    def to_string(self):
308        return "BTreeSet(size={})".format(self._valobj["map"]["length"])
309
310    def children(self):
311        inner_map = self._valobj["map"]
312        for i, (child, _) in enumerate(children_of_btree_map(inner_map)):
313            yield "[{}]".format(i), child
314
315    @staticmethod
316    def display_hint():
317        return "array"
318
319
320class StdBTreeMapProvider(printer_base):
321    def __init__(self, valobj):
322        self._valobj = valobj
323
324    def to_string(self):
325        return "BTreeMap(size={})".format(self._valobj["length"])
326
327    def children(self):
328        for i, (key, val) in enumerate(children_of_btree_map(self._valobj)):
329            yield "key{}".format(i), key
330            yield "val{}".format(i), val
331
332    @staticmethod
333    def display_hint():
334        return "map"
335
336
337# BACKCOMPAT: rust 1.35
338class StdOldHashMapProvider(printer_base):
339    def __init__(self, valobj, show_values=True):
340        self._valobj = valobj
341        self._show_values = show_values
342
343        self._table = self._valobj["table"]
344        self._size = int(self._table["size"])
345        self._hashes = self._table["hashes"]
346        self._hash_uint_type = self._hashes.type
347        self._hash_uint_size = self._hashes.type.sizeof
348        self._modulo = 2 ** self._hash_uint_size
349        self._data_ptr = self._hashes[ZERO_FIELD]["pointer"]
350
351        self._capacity_mask = int(self._table["capacity_mask"])
352        self._capacity = (self._capacity_mask + 1) % self._modulo
353
354        marker = self._table["marker"].type
355        self._pair_type = marker.template_argument(0)
356        self._pair_type_size = self._pair_type.sizeof
357
358        self._valid_indices = []
359        for idx in range(self._capacity):
360            data_ptr = self._data_ptr.cast(self._hash_uint_type.pointer())
361            address = data_ptr + idx
362            hash_uint = address.dereference()
363            hash_ptr = hash_uint[ZERO_FIELD]["pointer"]
364            if int(hash_ptr) != 0:
365                self._valid_indices.append(idx)
366
367    def to_string(self):
368        if self._show_values:
369            return "HashMap(size={})".format(self._size)
370        else:
371            return "HashSet(size={})".format(self._size)
372
373    def children(self):
374        start = int(self._data_ptr) & ~1
375
376        hashes = self._hash_uint_size * self._capacity
377        align = self._pair_type_size
378        len_rounded_up = (((((hashes + align) % self._modulo - 1) % self._modulo) & ~(
379                (align - 1) % self._modulo)) % self._modulo - hashes) % self._modulo
380
381        pairs_offset = hashes + len_rounded_up
382        pairs_start = gdb.Value(start + pairs_offset).cast(self._pair_type.pointer())
383
384        for index in range(self._size):
385            table_index = self._valid_indices[index]
386            idx = table_index & self._capacity_mask
387            element = (pairs_start + idx).dereference()
388            if self._show_values:
389                yield "key{}".format(index), element[ZERO_FIELD]
390                yield "val{}".format(index), element[FIRST_FIELD]
391            else:
392                yield "[{}]".format(index), element[ZERO_FIELD]
393
394    def display_hint(self):
395        return "map" if self._show_values else "array"
396
397
398class StdHashMapProvider(printer_base):
399    def __init__(self, valobj, show_values=True):
400        self._valobj = valobj
401        self._show_values = show_values
402
403        table = self._table()
404        table_inner = table["table"]
405        capacity = int(table_inner["bucket_mask"]) + 1
406        ctrl = table_inner["ctrl"]["pointer"]
407
408        self._size = int(table_inner["items"])
409        self._pair_type = table.type.template_argument(0).strip_typedefs()
410
411        self._new_layout = not table_inner.type.has_key("data")
412        if self._new_layout:
413            self._data_ptr = ctrl.cast(self._pair_type.pointer())
414        else:
415            self._data_ptr = table_inner["data"]["pointer"]
416
417        self._valid_indices = []
418        for idx in range(capacity):
419            address = ctrl + idx
420            value = address.dereference()
421            is_presented = value & 128 == 0
422            if is_presented:
423                self._valid_indices.append(idx)
424
425    def _table(self):
426        if self._show_values:
427            hashbrown_hashmap = self._valobj["base"]
428        elif self._valobj.type.fields()[0].name == "map":
429            # BACKCOMPAT: rust 1.47
430            # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap
431            hashbrown_hashmap = self._valobj["map"]["base"]
432        else:
433            # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap
434            hashbrown_hashmap = self._valobj["base"]["map"]
435        return hashbrown_hashmap["table"]
436
437    def to_string(self):
438        if self._show_values:
439            return "HashMap(size={})".format(self._size)
440        else:
441            return "HashSet(size={})".format(self._size)
442
443    def children(self):
444        pairs_start = self._data_ptr
445
446        for index in range(self._size):
447            idx = self._valid_indices[index]
448            if self._new_layout:
449                idx = -(idx + 1)
450            element = (pairs_start + idx).dereference()
451            if self._show_values:
452                yield "key{}".format(index), element[ZERO_FIELD]
453                yield "val{}".format(index), element[FIRST_FIELD]
454            else:
455                yield "[{}]".format(index), element[ZERO_FIELD]
456
457    def display_hint(self):
458        return "map" if self._show_values else "array"
459