]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_providers.py
Auto merge of #105008 - Dylan-DPC:rollup-wcd19yu, r=Dylan-DPC
[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.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
164 class 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
185 class 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
196 class 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
213 class 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
230 class 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.
242 def 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
283 class 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
300 class 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
318 class 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
378 class 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"