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