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