]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_rust_pretty_printing.py
Auto merge of #69156 - ecstatic-morse:unified-dataflow-impls2, r=eddyb
[rust.git] / src / etc / gdb_rust_pretty_printing.py
1 import gdb
2 import re
3 import sys
4 import debugger_pretty_printers_common as rustpp
5
6 # We want a version of `range` which doesn't allocate an intermediate list,
7 # specifically it should use a lazy iterator. In Python 2 this was `xrange`, but
8 # if we're running with Python 3 then we need to use `range` instead.
9 if sys.version_info[0] >= 3:
10     xrange = range
11
12 rust_enabled = 'set language rust' in gdb.execute('complete set language ru', to_string=True)
13
14 # The btree pretty-printers fail in a confusing way unless
15 # https://sourceware.org/bugzilla/show_bug.cgi?id=21763 is fixed.
16 # This fix went in 8.1, so check for that.
17 # See https://github.com/rust-lang/rust/issues/56730
18 gdb_81 = False
19 _match = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION)
20 if _match:
21     if int(_match.group(1)) > 8 or (int(_match.group(1)) == 8 and int(_match.group(2)) >= 1):
22         gdb_81 = True
23
24 # ===============================================================================
25 # GDB Pretty Printing Module for Rust
26 # ===============================================================================
27
28
29 class GdbType(rustpp.Type):
30
31     def __init__(self, ty):
32         super(GdbType, self).__init__()
33         self.ty = ty
34         self.fields = None
35
36     def get_unqualified_type_name(self):
37         tag = self.ty.tag
38
39         if tag is None:
40             return tag
41
42         return rustpp.extract_type_name(tag).replace("&'static ", "&")
43
44     def get_dwarf_type_kind(self):
45         if self.ty.code == gdb.TYPE_CODE_STRUCT:
46             return rustpp.DWARF_TYPE_CODE_STRUCT
47
48         if self.ty.code == gdb.TYPE_CODE_UNION:
49             return rustpp.DWARF_TYPE_CODE_UNION
50
51         if self.ty.code == gdb.TYPE_CODE_PTR:
52             return rustpp.DWARF_TYPE_CODE_PTR
53
54         if self.ty.code == gdb.TYPE_CODE_ENUM:
55             return rustpp.DWARF_TYPE_CODE_ENUM
56
57     def get_fields(self):
58         assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or
59                 (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION))
60         if self.fields is None:
61             self.fields = list(self.ty.fields())
62         return self.fields
63
64     def get_wrapped_value(self):
65         return self.ty
66
67
68 class GdbValue(rustpp.Value):
69     def __init__(self, gdb_val):
70         super(GdbValue, self).__init__(GdbType(gdb_val.type))
71         self.gdb_val = gdb_val
72         self.children = {}
73
74     def get_child_at_index(self, index):
75         child = self.children.get(index)
76         if child is None:
77             gdb_field = get_field_at_index(self.gdb_val, index)
78             child = GdbValue(self.gdb_val[gdb_field])
79             self.children[index] = child
80         return child
81
82     def as_integer(self):
83         if self.gdb_val.type.code == gdb.TYPE_CODE_PTR:
84             as_str = rustpp.compat_str(self.gdb_val).split()[0]
85             return int(as_str, 0)
86         return int(self.gdb_val)
87
88     def get_wrapped_value(self):
89         return self.gdb_val
90
91
92 def register_printers(objfile):
93     """Registers Rust pretty printers for the given objfile"""
94     objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
95
96
97 def rust_pretty_printer_lookup_function(gdb_val):
98     """
99     Returns the correct Rust pretty printer for the given value
100     if there is one
101     """
102
103     val = GdbValue(gdb_val)
104     type_kind = val.type.get_type_kind()
105
106     if type_kind == rustpp.TYPE_KIND_SLICE:
107         return RustSlicePrinter(val)
108
109     if type_kind == rustpp.TYPE_KIND_STD_VEC:
110         return RustStdVecPrinter(val)
111
112     if type_kind == rustpp.TYPE_KIND_STD_VECDEQUE:
113         return RustStdVecDequePrinter(val)
114
115     if type_kind == rustpp.TYPE_KIND_STD_BTREESET and gdb_81:
116         return RustStdBTreeSetPrinter(val)
117
118     if type_kind == rustpp.TYPE_KIND_STD_BTREEMAP and gdb_81:
119         return RustStdBTreeMapPrinter(val)
120
121     if type_kind == rustpp.TYPE_KIND_STD_STRING:
122         return RustStdStringPrinter(val)
123
124     if type_kind == rustpp.TYPE_KIND_OS_STRING:
125         return RustOsStringPrinter(val)
126
127     # Checks after this point should only be for "compiler" types --
128     # things that gdb's Rust language support knows about.
129     if rust_enabled:
130         return None
131
132     if type_kind == rustpp.TYPE_KIND_EMPTY:
133         return RustEmptyPrinter(val)
134
135     if type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT:
136         return RustStructPrinter(val,
137                                  omit_first_field=False,
138                                  omit_type_name=False,
139                                  is_tuple_like=False)
140
141     if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
142         return RustStructPrinter(val,
143                                  omit_first_field=True,
144                                  omit_type_name=False,
145                                  is_tuple_like=False)
146
147     if type_kind == rustpp.TYPE_KIND_STR_SLICE:
148         return RustStringSlicePrinter(val)
149
150     if type_kind == rustpp.TYPE_KIND_TUPLE:
151         return RustStructPrinter(val,
152                                  omit_first_field=False,
153                                  omit_type_name=True,
154                                  is_tuple_like=True)
155
156     if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
157         return RustStructPrinter(val,
158                                  omit_first_field=False,
159                                  omit_type_name=False,
160                                  is_tuple_like=True)
161
162     if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
163         return RustCStyleVariantPrinter(val.get_child_at_index(0))
164
165     if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT:
166         return RustStructPrinter(val,
167                                  omit_first_field=True,
168                                  omit_type_name=False,
169                                  is_tuple_like=True)
170
171     if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM:
172         variant = get_field_at_index(gdb_val, 0)
173         return rust_pretty_printer_lookup_function(gdb_val[variant])
174
175     if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM:
176         # This is a regular enum, extract the discriminant
177         discriminant_val = rustpp.get_discriminant_value_as_integer(val)
178         variant = get_field_at_index(gdb_val, discriminant_val)
179         return rust_pretty_printer_lookup_function(gdb_val[variant])
180
181     if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM:
182         encoded_enum_info = rustpp.EncodedEnumInfo(val)
183         if encoded_enum_info.is_null_variant():
184             return IdentityPrinter(encoded_enum_info.get_null_variant_name())
185
186         non_null_val = encoded_enum_info.get_non_null_variant_val()
187         return rust_pretty_printer_lookup_function(non_null_val.get_wrapped_value())
188
189     # No pretty printer has been found
190     return None
191
192
193 # =------------------------------------------------------------------------------
194 # Pretty Printer Classes
195 # =------------------------------------------------------------------------------
196 class RustEmptyPrinter(object):
197     def __init__(self, val):
198         self.__val = val
199
200     def to_string(self):
201         return self.__val.type.get_unqualified_type_name()
202
203
204 class RustStructPrinter(object):
205     def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like):
206         self.__val = val
207         self.__omit_first_field = omit_first_field
208         self.__omit_type_name = omit_type_name
209         self.__is_tuple_like = is_tuple_like
210
211     def to_string(self):
212         if self.__omit_type_name:
213             return None
214         return self.__val.type.get_unqualified_type_name()
215
216     def children(self):
217         cs = []
218         wrapped_value = self.__val.get_wrapped_value()
219
220         for number, field in enumerate(self.__val.type.get_fields()):
221             field_value = wrapped_value[field.name]
222             if self.__is_tuple_like:
223                 cs.append((str(number), field_value))
224             else:
225                 cs.append((field.name, field_value))
226
227         if self.__omit_first_field:
228             cs = cs[1:]
229
230         return cs
231
232     def display_hint(self):
233         if self.__is_tuple_like:
234             return "array"
235         else:
236             return ""
237
238
239 class RustSlicePrinter(object):
240     def __init__(self, val):
241         self.__val = val
242
243     @staticmethod
244     def display_hint():
245         return "array"
246
247     def to_string(self):
248         (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
249         return (self.__val.type.get_unqualified_type_name() +
250                 ("(len: %i)" % length))
251
252     def children(self):
253         (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
254         assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
255         raw_ptr = data_ptr.get_wrapped_value()
256
257         for index in xrange(0, length):
258             yield (str(index), (raw_ptr + index).dereference())
259
260
261 class RustStringSlicePrinter(object):
262     def __init__(self, val):
263         self.__val = val
264
265     def to_string(self):
266         (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
267         raw_ptr = data_ptr.get_wrapped_value()
268         return raw_ptr.lazy_string(encoding="utf-8", length=length)
269
270     def display_hint(self):
271         return "string"
272
273
274 class RustStdVecPrinter(object):
275     def __init__(self, val):
276         self.__val = val
277
278     @staticmethod
279     def display_hint():
280         return "array"
281
282     def to_string(self):
283         (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
284         return (self.__val.type.get_unqualified_type_name() +
285                 ("(len: %i, cap: %i)" % (length, cap)))
286
287     def children(self):
288         saw_inaccessible = False
289         (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
290         gdb_ptr = data_ptr.get_wrapped_value()
291         for index in xrange(0, length):
292             if saw_inaccessible:
293                 return
294             try:
295                 # rust-lang/rust#64343: passing deref expr to `str` allows
296                 # catching exception on garbage pointer
297                 str((gdb_ptr + index).dereference())
298                 yield (str(index), (gdb_ptr + index).dereference())
299             except RuntimeError:
300                 saw_inaccessible = True
301                 yield (str(index), "inaccessible")
302
303
304 class RustStdVecDequePrinter(object):
305     def __init__(self, val):
306         self.__val = val
307
308     @staticmethod
309     def display_hint():
310         return "array"
311
312     def to_string(self):
313         (tail, head, data_ptr, cap) = \
314             rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val)
315         if head >= tail:
316             size = head - tail
317         else:
318             size = cap + head - tail
319         return (self.__val.type.get_unqualified_type_name() +
320                 ("(len: %i, cap: %i)" % (size, cap)))
321
322     def children(self):
323         (tail, head, data_ptr, cap) = \
324             rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val)
325         gdb_ptr = data_ptr.get_wrapped_value()
326         if head >= tail:
327             size = head - tail
328         else:
329             size = cap + head - tail
330         for index in xrange(0, size):
331             yield (str(index), (gdb_ptr + ((tail + index) % cap)).dereference())
332
333
334 # Yield each key (and optionally value) from a BoxedNode.
335 def children_of_node(boxed_node, height, want_values):
336     node_ptr = boxed_node['ptr']['pointer']
337     if height > 0:
338         type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode')
339         node_type = gdb.lookup_type(type_name)
340         node_ptr = node_ptr.cast(node_type.pointer())
341         leaf = node_ptr['data']
342     else:
343         leaf = node_ptr.dereference()
344     keys = leaf['keys']
345     if want_values:
346         values = leaf['vals']
347     length = int(leaf['len'])
348     for i in xrange(0, length + 1):
349         if height > 0:
350             child_ptr = node_ptr['edges'][i]['value']['value']
351             for child in children_of_node(child_ptr, height - 1, want_values):
352                 yield child
353         if i < length:
354             if want_values:
355                 yield (keys[i]['value']['value'], values[i]['value']['value'])
356             else:
357                 yield keys[i]['value']['value']
358
359
360 class RustStdBTreeSetPrinter(object):
361     def __init__(self, val):
362         self.__val = val
363
364     @staticmethod
365     def display_hint():
366         return "array"
367
368     def to_string(self):
369         return (self.__val.type.get_unqualified_type_name() +
370                 ("(len: %i)" % self.__val.get_wrapped_value()['map']['length']))
371
372     def children(self):
373         root = self.__val.get_wrapped_value()['map']['root']
374         node_ptr = root['node']
375         i = 0
376         for child in children_of_node(node_ptr, root['height'], False):
377             yield (str(i), child)
378             i = i + 1
379
380
381 class RustStdBTreeMapPrinter(object):
382     def __init__(self, val):
383         self.__val = val
384
385     @staticmethod
386     def display_hint():
387         return "map"
388
389     def to_string(self):
390         return (self.__val.type.get_unqualified_type_name() +
391                 ("(len: %i)" % self.__val.get_wrapped_value()['length']))
392
393     def children(self):
394         root = self.__val.get_wrapped_value()['root']
395         node_ptr = root['node']
396         i = 0
397         for child in children_of_node(node_ptr, root['height'], True):
398             yield (str(i), child[0])
399             yield (str(i), child[1])
400             i = i + 1
401
402
403 class RustStdStringPrinter(object):
404     def __init__(self, val):
405         self.__val = val
406
407     def to_string(self):
408         vec = self.__val.get_child_at_index(0)
409         (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec)
410         return data_ptr.get_wrapped_value().lazy_string(encoding="utf-8",
411                                                         length=length)
412
413     def display_hint(self):
414         return "string"
415
416
417 class RustOsStringPrinter(object):
418     def __init__(self, val):
419         self.__val = val
420
421     def to_string(self):
422         buf = self.__val.get_child_at_index(0)
423         vec = buf.get_child_at_index(0)
424         if vec.type.get_unqualified_type_name() == "Wtf8Buf":
425             vec = vec.get_child_at_index(0)
426
427         (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(
428             vec)
429         return data_ptr.get_wrapped_value().lazy_string(length=length)
430
431     def display_hint(self):
432         return "string"
433
434
435 class RustCStyleVariantPrinter(object):
436     def __init__(self, val):
437         assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM
438         self.__val = val
439
440     def to_string(self):
441         return str(self.__val.get_wrapped_value())
442
443
444 class IdentityPrinter(object):
445     def __init__(self, string):
446         self.string = string
447
448     def to_string(self):
449         return self.string
450
451
452 def get_field_at_index(gdb_val, index):
453     i = 0
454     for field in gdb_val.type.fields():
455         if i == index:
456             return field
457         i += 1
458     return None