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