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