blob: a6b09722e1c9497d2d70833dfdf3a2b4802108e4 [file] [log] [blame]
Matthew Maurerbd398542019-09-05 16:25:08 -07001import gdb
2import re
3import sys
4import 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.
9if sys.version_info[0] >= 3:
10 xrange = range
11
12rust_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
18gdb_81 = False
19_match = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION)
20if _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
28class 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
67class 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
91def register_printers(objfile):
92 """Registers Rust pretty printers for the given objfile"""
93 objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
94
95
96def 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#=------------------------------------------------------------------------------
195class 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
203class 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
238class 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
260class 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
273class 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 (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
288 gdb_ptr = data_ptr.get_wrapped_value()
289 for index in xrange(0, length):
290 yield (str(index), (gdb_ptr + index).dereference())
291
292
293class RustStdVecDequePrinter(object):
294 def __init__(self, val):
295 self.__val = val
296
297 @staticmethod
298 def display_hint():
299 return "array"
300
301 def to_string(self):
302 (tail, head, data_ptr, cap) = \
303 rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val)
304 if head >= tail:
305 size = head - tail
306 else:
307 size = cap + head - tail
308 return (self.__val.type.get_unqualified_type_name() +
309 ("(len: %i, cap: %i)" % (size, cap)))
310
311 def children(self):
312 (tail, head, data_ptr, cap) = \
313 rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val)
314 gdb_ptr = data_ptr.get_wrapped_value()
315 if head >= tail:
316 size = head - tail
317 else:
318 size = cap + head - tail
319 for index in xrange(0, size):
320 yield (str(index), (gdb_ptr + ((tail + index) % cap)).dereference())
321
322
323# Yield each key (and optionally value) from a BoxedNode.
324def children_of_node(boxed_node, height, want_values):
325 node_ptr = boxed_node['ptr']['pointer']
326 if height > 0:
327 type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode')
328 node_type = gdb.lookup_type(type_name)
329 node_ptr = node_ptr.cast(node_type.pointer())
330 leaf = node_ptr['data']
331 else:
332 leaf = node_ptr.dereference()
333 keys = leaf['keys']
334 if want_values:
335 values = leaf['vals']
336 length = int(leaf['len'])
337 for i in xrange(0, length + 1):
338 if height > 0:
339 child_ptr = node_ptr['edges'][i]['value']['value']
340 for child in children_of_node(child_ptr, height - 1, want_values):
341 yield child
342 if i < length:
343 if want_values:
344 yield (keys[i]['value']['value'], values[i]['value']['value'])
345 else:
346 yield keys[i]['value']['value']
347
348class RustStdBTreeSetPrinter(object):
349 def __init__(self, val):
350 self.__val = val
351
352 @staticmethod
353 def display_hint():
354 return "array"
355
356 def to_string(self):
357 return (self.__val.type.get_unqualified_type_name() +
358 ("(len: %i)" % self.__val.get_wrapped_value()['map']['length']))
359
360 def children(self):
361 root = self.__val.get_wrapped_value()['map']['root']
362 node_ptr = root['node']
363 i = 0
364 for child in children_of_node(node_ptr, root['height'], False):
365 yield (str(i), child)
366 i = i + 1
367
368
369class RustStdBTreeMapPrinter(object):
370 def __init__(self, val):
371 self.__val = val
372
373 @staticmethod
374 def display_hint():
375 return "map"
376
377 def to_string(self):
378 return (self.__val.type.get_unqualified_type_name() +
379 ("(len: %i)" % self.__val.get_wrapped_value()['length']))
380
381 def children(self):
382 root = self.__val.get_wrapped_value()['root']
383 node_ptr = root['node']
384 i = 0
385 for child in children_of_node(node_ptr, root['height'], True):
386 yield (str(i), child[0])
387 yield (str(i), child[1])
388 i = i + 1
389
390
391class RustStdStringPrinter(object):
392 def __init__(self, val):
393 self.__val = val
394
395 def to_string(self):
396 vec = self.__val.get_child_at_index(0)
397 (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec)
398 return data_ptr.get_wrapped_value().lazy_string(encoding="utf-8",
399 length=length)
400
401 def display_hint(self):
402 return "string"
403
404
405class RustOsStringPrinter(object):
406 def __init__(self, val):
407 self.__val = val
408
409 def to_string(self):
410 buf = self.__val.get_child_at_index(0)
411 vec = buf.get_child_at_index(0)
412 if vec.type.get_unqualified_type_name() == "Wtf8Buf":
413 vec = vec.get_child_at_index(0)
414
415 (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(
416 vec)
417 return data_ptr.get_wrapped_value().lazy_string(length=length)
418
419 def display_hint(self):
420 return "string"
421
422class RustCStyleVariantPrinter(object):
423 def __init__(self, val):
424 assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM
425 self.__val = val
426
427 def to_string(self):
428 return str(self.__val.get_wrapped_value())
429
430
431class IdentityPrinter(object):
432 def __init__(self, string):
433 self.string = string
434
435 def to_string(self):
436 return self.string
437
438
439def get_field_at_index(gdb_val, index):
440 i = 0
441 for field in gdb_val.type.fields():
442 if i == index:
443 return field
444 i += 1
445 return None