]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_providers.py
Auto merge of #100865 - compiler-errors:parent-substs-still, r=cjgillot
[rust.git] / src / etc / gdb_providers.py
1 from sys import version_info
2
3 import gdb
4
5 if version_info[0] >= 3:
6     xrange = range
7
8 ZERO_FIELD = "__0"
9 FIRST_FIELD = "__1"
10
11
12 def 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 class 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
45 class 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
60 class 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
77 class 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
90 def _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
106 class 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
124 class 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
143 class StdVecDequeProvider:
144     def __init__(self, valobj):
145         self.valobj = valobj
146         self.head = int(valobj["head"])
147         self.tail = int(valobj["tail"])
148         self.cap = int(valobj["buf"]["cap"])
149         self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
150         if self.head >= self.tail:
151             self.size = self.head - self.tail
152         else:
153             self.size = self.cap + self.head - self.tail
154
155     def to_string(self):
156         return "VecDeque(size={})".format(self.size)
157
158     def children(self):
159         return _enumerate_array_elements(
160             (self.data_ptr + ((self.tail + index) % self.cap)) for index in xrange(self.size)
161         )
162
163     @staticmethod
164     def display_hint():
165         return "array"
166
167
168 class StdRcProvider:
169     def __init__(self, valobj, is_atomic=False):
170         self.valobj = valobj
171         self.is_atomic = is_atomic
172         self.ptr = unwrap_unique_or_non_null(valobj["ptr"])
173         self.value = self.ptr["data" if is_atomic else "value"]
174         self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"]
175         self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
176
177     def to_string(self):
178         if self.is_atomic:
179             return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak))
180         else:
181             return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak))
182
183     def children(self):
184         yield "value", self.value
185         yield "strong", self.strong
186         yield "weak", self.weak
187
188
189 class StdCellProvider:
190     def __init__(self, valobj):
191         self.value = valobj["value"]["value"]
192
193     def to_string(self):
194         return "Cell"
195
196     def children(self):
197         yield "value", self.value
198
199
200 class StdRefProvider:
201     def __init__(self, valobj):
202         self.value = valobj["value"].dereference()
203         self.borrow = valobj["borrow"]["borrow"]["value"]["value"]
204
205     def to_string(self):
206         borrow = int(self.borrow)
207         if borrow >= 0:
208             return "Ref(borrow={})".format(borrow)
209         else:
210             return "Ref(borrow_mut={})".format(-borrow)
211
212     def children(self):
213         yield "*value", self.value
214         yield "borrow", self.borrow
215
216
217 class StdRefCellProvider:
218     def __init__(self, valobj):
219         self.value = valobj["value"]["value"]
220         self.borrow = valobj["borrow"]["value"]["value"]
221
222     def to_string(self):
223         borrow = int(self.borrow)
224         if borrow >= 0:
225             return "RefCell(borrow={})".format(borrow)
226         else:
227             return "RefCell(borrow_mut={})".format(-borrow)
228
229     def children(self):
230         yield "value", self.value
231         yield "borrow", self.borrow
232
233
234 class StdNonZeroNumberProvider:
235     def __init__(self, valobj):
236         fields = valobj.type.fields()
237         assert len(fields) == 1
238         field = list(fields)[0]
239         self.value = str(valobj[field.name])
240
241     def to_string(self):
242         return self.value
243
244
245 # Yields children (in a provider's sense of the word) for a BTreeMap.
246 def children_of_btree_map(map):
247     # Yields each key/value pair in the node and in any child nodes.
248     def children_of_node(node_ptr, height):
249         def cast_to_internal(node):
250             internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
251             internal_type = gdb.lookup_type(internal_type_name)
252             return node.cast(internal_type.pointer())
253
254         if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
255             # BACKCOMPAT: rust 1.49
256             node_ptr = node_ptr["ptr"]
257         node_ptr = unwrap_unique_or_non_null(node_ptr)
258         leaf = node_ptr.dereference()
259         keys = leaf["keys"]
260         vals = leaf["vals"]
261         edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
262         length = leaf["len"]
263
264         for i in xrange(0, length + 1):
265             if height > 0:
266                 child_ptr = edges[i]["value"]["value"]
267                 for child in children_of_node(child_ptr, height - 1):
268                     yield child
269             if i < length:
270                 # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
271                 key_type_size = keys.type.sizeof
272                 val_type_size = vals.type.sizeof
273                 key = keys[i]["value"]["value"] if key_type_size > 0 else gdb.parse_and_eval("()")
274                 val = vals[i]["value"]["value"] if val_type_size > 0 else gdb.parse_and_eval("()")
275                 yield key, val
276
277     if map["length"] > 0:
278         root = map["root"]
279         if root.type.name.startswith("core::option::Option<"):
280             root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
281         node_ptr = root["node"]
282         height = root["height"]
283         for child in children_of_node(node_ptr, height):
284             yield child
285
286
287 class StdBTreeSetProvider:
288     def __init__(self, valobj):
289         self.valobj = valobj
290
291     def to_string(self):
292         return "BTreeSet(size={})".format(self.valobj["map"]["length"])
293
294     def children(self):
295         inner_map = self.valobj["map"]
296         for i, (child, _) in enumerate(children_of_btree_map(inner_map)):
297             yield "[{}]".format(i), child
298
299     @staticmethod
300     def display_hint():
301         return "array"
302
303
304 class StdBTreeMapProvider:
305     def __init__(self, valobj):
306         self.valobj = valobj
307
308     def to_string(self):
309         return "BTreeMap(size={})".format(self.valobj["length"])
310
311     def children(self):
312         for i, (key, val) in enumerate(children_of_btree_map(self.valobj)):
313             yield "key{}".format(i), key
314             yield "val{}".format(i), val
315
316     @staticmethod
317     def display_hint():
318         return "map"
319
320
321 # BACKCOMPAT: rust 1.35
322 class StdOldHashMapProvider:
323     def __init__(self, valobj, show_values=True):
324         self.valobj = valobj
325         self.show_values = show_values
326
327         self.table = self.valobj["table"]
328         self.size = int(self.table["size"])
329         self.hashes = self.table["hashes"]
330         self.hash_uint_type = self.hashes.type
331         self.hash_uint_size = self.hashes.type.sizeof
332         self.modulo = 2 ** self.hash_uint_size
333         self.data_ptr = self.hashes[ZERO_FIELD]["pointer"]
334
335         self.capacity_mask = int(self.table["capacity_mask"])
336         self.capacity = (self.capacity_mask + 1) % self.modulo
337
338         marker = self.table["marker"].type
339         self.pair_type = marker.template_argument(0)
340         self.pair_type_size = self.pair_type.sizeof
341
342         self.valid_indices = []
343         for idx in range(self.capacity):
344             data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer())
345             address = data_ptr + idx
346             hash_uint = address.dereference()
347             hash_ptr = hash_uint[ZERO_FIELD]["pointer"]
348             if int(hash_ptr) != 0:
349                 self.valid_indices.append(idx)
350
351     def to_string(self):
352         if self.show_values:
353             return "HashMap(size={})".format(self.size)
354         else:
355             return "HashSet(size={})".format(self.size)
356
357     def children(self):
358         start = int(self.data_ptr) & ~1
359
360         hashes = self.hash_uint_size * self.capacity
361         align = self.pair_type_size
362         len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
363                 (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
364
365         pairs_offset = hashes + len_rounded_up
366         pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer())
367
368         for index in range(self.size):
369             table_index = self.valid_indices[index]
370             idx = table_index & self.capacity_mask
371             element = (pairs_start + idx).dereference()
372             if self.show_values:
373                 yield "key{}".format(index), element[ZERO_FIELD]
374                 yield "val{}".format(index), element[FIRST_FIELD]
375             else:
376                 yield "[{}]".format(index), element[ZERO_FIELD]
377
378     def display_hint(self):
379         return "map" if self.show_values else "array"
380
381
382 class StdHashMapProvider:
383     def __init__(self, valobj, show_values=True):
384         self.valobj = valobj
385         self.show_values = show_values
386
387         table = self.table()
388         table_inner = table["table"]
389         capacity = int(table_inner["bucket_mask"]) + 1
390         ctrl = table_inner["ctrl"]["pointer"]
391
392         self.size = int(table_inner["items"])
393         self.pair_type = table.type.template_argument(0).strip_typedefs()
394
395         self.new_layout = not table_inner.type.has_key("data")
396         if self.new_layout:
397             self.data_ptr = ctrl.cast(self.pair_type.pointer())
398         else:
399             self.data_ptr = table_inner["data"]["pointer"]
400
401         self.valid_indices = []
402         for idx in range(capacity):
403             address = ctrl + idx
404             value = address.dereference()
405             is_presented = value & 128 == 0
406             if is_presented:
407                 self.valid_indices.append(idx)
408
409     def table(self):
410         if self.show_values:
411             hashbrown_hashmap = self.valobj["base"]
412         elif self.valobj.type.fields()[0].name == "map":
413             # BACKCOMPAT: rust 1.47
414             # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap
415             hashbrown_hashmap = self.valobj["map"]["base"]
416         else:
417             # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap
418             hashbrown_hashmap = self.valobj["base"]["map"]
419         return hashbrown_hashmap["table"]
420
421     def to_string(self):
422         if self.show_values:
423             return "HashMap(size={})".format(self.size)
424         else:
425             return "HashSet(size={})".format(self.size)
426
427     def children(self):
428         pairs_start = self.data_ptr
429
430         for index in range(self.size):
431             idx = self.valid_indices[index]
432             if self.new_layout:
433                 idx = -(idx + 1)
434             element = (pairs_start + idx).dereference()
435             if self.show_values:
436                 yield "key{}".format(index), element[ZERO_FIELD]
437                 yield "val{}".format(index), element[FIRST_FIELD]
438             else:
439                 yield "[{}]".format(index), element[ZERO_FIELD]
440
441     def display_hint(self):
442         return "map" if self.show_values else "array"