Skip to content

Commit c800b22

Browse files
committed
Auto merge of #25905 - michaelwoerister:lldb-pp-strings, r=brson
GDB and LLDB pretty printers have some common functionality and also access some common information, such as the layout of standard library types. So far, this information has been duplicated in the two pretty printing python modules. This PR introduces a common module used by both debuggers. This PR also implements proper rendering of `String` and `&str` values in LLDB.
2 parents 48e9ef6 + d136714 commit c800b22

6 files changed

+737
-421
lines changed

mk/debuggers.mk

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
## GDB ##
1717
DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB=gdb_load_rust_pretty_printers.py \
18-
gdb_rust_pretty_printing.py
18+
gdb_rust_pretty_printing.py \
19+
debugger_pretty_printers_common.py
1920
DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB_ABS=\
2021
$(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB), \
2122
$(CFG_SRC_DIR)src/etc/$(script))
@@ -27,7 +28,8 @@ DEBUGGER_BIN_SCRIPTS_GDB_ABS=\
2728

2829

2930
## LLDB ##
30-
DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py
31+
DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py \
32+
debugger_pretty_printers_common.py
3133
DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB_ABS=\
3234
$(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB), \
3335
$(CFG_SRC_DIR)src/etc/$(script))
+328
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
# Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
# file at the top-level directory of this distribution and at
3+
# http://rust-lang.org/COPYRIGHT.
4+
#
5+
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
# option. This file may not be copied, modified, or distributed
9+
# except according to those terms.
10+
11+
"""
12+
This module provides an abstraction layer over common Rust pretty printing
13+
functionality needed by both GDB and LLDB.
14+
"""
15+
16+
import re
17+
18+
# Type codes that indicate the kind of type as it appears in DWARF debug
19+
# information. This code alone is not sufficient to determine the Rust type.
20+
# For example structs, tuples, fat pointers, or enum variants will all have
21+
# DWARF_TYPE_CODE_STRUCT.
22+
DWARF_TYPE_CODE_STRUCT = 1
23+
DWARF_TYPE_CODE_UNION = 2
24+
DWARF_TYPE_CODE_PTR = 3
25+
DWARF_TYPE_CODE_ARRAY = 4
26+
DWARF_TYPE_CODE_ENUM = 5
27+
28+
# These constants specify the most specific kind of type that could be
29+
# determined for a given value.
30+
TYPE_KIND_UNKNOWN = -1
31+
TYPE_KIND_EMPTY = 0
32+
TYPE_KIND_SLICE = 1
33+
TYPE_KIND_REGULAR_STRUCT = 2
34+
TYPE_KIND_TUPLE = 3
35+
TYPE_KIND_TUPLE_STRUCT = 4
36+
TYPE_KIND_CSTYLE_VARIANT = 5
37+
TYPE_KIND_TUPLE_VARIANT = 6
38+
TYPE_KIND_STRUCT_VARIANT = 7
39+
TYPE_KIND_STR_SLICE = 8
40+
TYPE_KIND_STD_VEC = 9
41+
TYPE_KIND_STD_STRING = 10
42+
TYPE_KIND_REGULAR_ENUM = 11
43+
TYPE_KIND_COMPRESSED_ENUM = 12
44+
TYPE_KIND_SINGLETON_ENUM = 13
45+
TYPE_KIND_CSTYLE_ENUM = 14
46+
TYPE_KIND_PTR = 15
47+
TYPE_KIND_FIXED_SIZE_VEC = 16
48+
49+
ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$"
50+
ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR"
51+
52+
# Slice related constants
53+
SLICE_FIELD_NAME_DATA_PTR = "data_ptr"
54+
SLICE_FIELD_NAME_LENGTH = "length"
55+
SLICE_FIELD_NAMES = [SLICE_FIELD_NAME_DATA_PTR, SLICE_FIELD_NAME_LENGTH]
56+
57+
# std::Vec<> related constants
58+
STD_VEC_FIELD_NAME_DATA_PTR = "ptr"
59+
STD_VEC_FIELD_NAME_LENGTH = "len"
60+
STD_VEC_FIELD_NAME_CAPACITY = "cap"
61+
STD_VEC_FIELD_NAMES = [STD_VEC_FIELD_NAME_DATA_PTR,
62+
STD_VEC_FIELD_NAME_LENGTH,
63+
STD_VEC_FIELD_NAME_CAPACITY]
64+
65+
# std::String related constants
66+
STD_STRING_FIELD_NAMES = ["vec"]
67+
68+
69+
class Type(object):
70+
"""
71+
This class provides a common interface for type-oriented operations.
72+
Sub-classes are supposed to wrap a debugger-specific type-object and
73+
provide implementations for the abstract methods in this class.
74+
"""
75+
76+
def __init__(self):
77+
self.__type_kind = None
78+
79+
def get_unqualified_type_name(self):
80+
"""
81+
Implementations of this method should return the unqualified name of the
82+
type-object they are wrapping. Some examples:
83+
84+
'int' -> 'int'
85+
'std::vec::Vec<std::string::String>' -> 'Vec<std::string::String>'
86+
'&std::option::Option<std::string::String>' -> '&std::option::Option<std::string::String>'
87+
88+
As you can see, type arguments stay fully qualified.
89+
"""
90+
raise NotImplementedError("Override this method")
91+
92+
def get_dwarf_type_kind(self):
93+
"""
94+
Implementations of this method should return the correct
95+
DWARF_TYPE_CODE_* value for the wrapped type-object.
96+
"""
97+
raise NotImplementedError("Override this method")
98+
99+
def get_fields(self):
100+
"""
101+
Implementations of this method should return a list of field-objects of
102+
this type. For Rust-enums (i.e. with DWARF_TYPE_CODE_UNION) these field-
103+
objects represent the variants of the enum. Field-objects must have a
104+
`name` attribute that gives their name as specified in DWARF.
105+
"""
106+
assert ((self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT) or
107+
(self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION))
108+
raise NotImplementedError("Override this method")
109+
110+
def get_wrapped_value(self):
111+
"""
112+
Returns the debugger-specific type-object wrapped by this object. This
113+
is sometimes needed for doing things like pointer-arithmetic in GDB.
114+
"""
115+
raise NotImplementedError("Override this method")
116+
117+
def get_type_kind(self):
118+
"""This method returns the TYPE_KIND_* value for this type-object."""
119+
if self.__type_kind is None:
120+
dwarf_type_code = self.get_dwarf_type_kind()
121+
122+
if dwarf_type_code == DWARF_TYPE_CODE_STRUCT:
123+
self.__type_kind = self.__classify_struct()
124+
elif dwarf_type_code == DWARF_TYPE_CODE_UNION:
125+
self.__type_kind = self.__classify_union()
126+
elif dwarf_type_code == DWARF_TYPE_CODE_PTR:
127+
self.__type_kind = TYPE_KIND_PTR
128+
elif dwarf_type_code == DWARF_TYPE_CODE_ARRAY:
129+
self.__type_kind = TYPE_KIND_FIXED_SIZE_VEC
130+
else:
131+
self.__type_kind = TYPE_KIND_UNKNOWN
132+
return self.__type_kind
133+
134+
def __classify_struct(self):
135+
assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT
136+
137+
unqualified_type_name = self.get_unqualified_type_name()
138+
139+
# STR SLICE
140+
if unqualified_type_name == "&str":
141+
return TYPE_KIND_STR_SLICE
142+
143+
# REGULAR SLICE
144+
if (unqualified_type_name.startswith("&[") and
145+
unqualified_type_name.endswith("]") and
146+
self.__conforms_to_field_layout(SLICE_FIELD_NAMES)):
147+
return TYPE_KIND_SLICE
148+
149+
fields = self.get_fields()
150+
field_count = len(fields)
151+
152+
# EMPTY STRUCT
153+
if field_count == 0:
154+
return TYPE_KIND_EMPTY
155+
156+
# STD VEC
157+
if (unqualified_type_name.startswith("Vec<") and
158+
self.__conforms_to_field_layout(STD_VEC_FIELD_NAMES)):
159+
return TYPE_KIND_STD_VEC
160+
161+
# STD STRING
162+
if (unqualified_type_name.startswith("String") and
163+
self.__conforms_to_field_layout(STD_STRING_FIELD_NAMES)):
164+
return TYPE_KIND_STD_STRING
165+
166+
# ENUM VARIANTS
167+
if fields[0].name == ENUM_DISR_FIELD_NAME:
168+
if field_count == 1:
169+
return TYPE_KIND_CSTYLE_VARIANT
170+
elif self.__all_fields_conform_to_tuple_field_naming(1):
171+
return TYPE_KIND_TUPLE_VARIANT
172+
else:
173+
return TYPE_KIND_STRUCT_VARIANT
174+
175+
# TUPLE
176+
if self.__all_fields_conform_to_tuple_field_naming(0):
177+
if unqualified_type_name.startswith("("):
178+
return TYPE_KIND_TUPLE
179+
else:
180+
return TYPE_KIND_TUPLE_STRUCT
181+
182+
# REGULAR STRUCT
183+
return TYPE_KIND_REGULAR_STRUCT
184+
185+
186+
def __classify_union(self):
187+
assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION
188+
189+
union_members = self.get_fields()
190+
union_member_count = len(union_members)
191+
if union_member_count == 0:
192+
return TYPE_KIND_EMPTY
193+
elif union_member_count == 1:
194+
first_variant_name = union_members[0].name
195+
if first_variant_name is None:
196+
return TYPE_KIND_SINGLETON_ENUM
197+
else:
198+
assert first_variant_name.startswith(ENCODED_ENUM_PREFIX)
199+
return TYPE_KIND_COMPRESSED_ENUM
200+
else:
201+
return TYPE_KIND_REGULAR_ENUM
202+
203+
204+
def __conforms_to_field_layout(self, expected_fields):
205+
actual_fields = self.get_fields()
206+
actual_field_count = len(actual_fields)
207+
208+
if actual_field_count != len(expected_fields):
209+
return False
210+
211+
for i in range(0, actual_field_count):
212+
if actual_fields[i].name != expected_fields[i]:
213+
return False
214+
215+
return True
216+
217+
def __all_fields_conform_to_tuple_field_naming(self, start_index):
218+
fields = self.get_fields()
219+
field_count = len(fields)
220+
221+
for i in range(start_index, field_count):
222+
field_name = fields[i].name
223+
if (field_name is None) or (re.match(r"__\d+$", field_name) is None):
224+
return False
225+
return True
226+
227+
228+
class Value(object):
229+
"""
230+
This class provides a common interface for value-oriented operations.
231+
Sub-classes are supposed to wrap a debugger-specific value-object and
232+
provide implementations for the abstract methods in this class.
233+
"""
234+
def __init__(self, ty):
235+
self.type = ty
236+
237+
def get_child_at_index(self, index):
238+
"""Returns the value of the field, array element or variant at the given index"""
239+
raise NotImplementedError("Override this method")
240+
241+
def as_integer(self):
242+
"""
243+
Try to convert the wrapped value into a Python integer. This should
244+
always succeed for values that are pointers or actual integers.
245+
"""
246+
raise NotImplementedError("Override this method")
247+
248+
def get_wrapped_value(self):
249+
"""
250+
Returns the debugger-specific value-object wrapped by this object. This
251+
is sometimes needed for doing things like pointer-arithmetic in GDB.
252+
"""
253+
raise NotImplementedError("Override this method")
254+
255+
256+
class EncodedEnumInfo(object):
257+
"""
258+
This class provides facilities for handling enum values with compressed
259+
encoding where a non-null field in one variant doubles as the discriminant.
260+
"""
261+
262+
def __init__(self, enum_val):
263+
assert enum_val.type.get_type_kind() == TYPE_KIND_COMPRESSED_ENUM
264+
variant_name = enum_val.type.get_fields()[0].name
265+
last_separator_index = variant_name.rfind("$")
266+
start_index = len(ENCODED_ENUM_PREFIX)
267+
indices_substring = variant_name[start_index:last_separator_index].split("$")
268+
self.__enum_val = enum_val
269+
self.__disr_field_indices = [int(index) for index in indices_substring]
270+
self.__null_variant_name = variant_name[last_separator_index + 1:]
271+
272+
def is_null_variant(self):
273+
ty = self.__enum_val.type
274+
sole_variant_val = self.__enum_val.get_child_at_index(0)
275+
discriminant_val = sole_variant_val
276+
for disr_field_index in self.__disr_field_indices:
277+
discriminant_val = discriminant_val.get_child_at_index(disr_field_index)
278+
279+
# If the discriminant field is a fat pointer we have to consider the
280+
# first word as the true discriminant
281+
if discriminant_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT:
282+
discriminant_val = discriminant_val.get_child_at_index(0)
283+
284+
return discriminant_val.as_integer() == 0
285+
286+
def get_non_null_variant_val(self):
287+
return self.__enum_val.get_child_at_index(0)
288+
289+
def get_null_variant_name(self):
290+
return self.__null_variant_name
291+
292+
293+
def get_discriminant_value_as_integer(enum_val):
294+
assert enum_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION
295+
# we can take any variant here because the discriminant has to be the same
296+
# for all of them.
297+
variant_val = enum_val.get_child_at_index(0)
298+
disr_val = variant_val.get_child_at_index(0)
299+
return disr_val.as_integer()
300+
301+
302+
def extract_length_ptr_and_cap_from_std_vec(vec_val):
303+
assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VEC
304+
length_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_LENGTH)
305+
ptr_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_DATA_PTR)
306+
cap_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_CAPACITY)
307+
308+
length = vec_val.get_child_at_index(length_field_index).as_integer()
309+
vec_ptr_val = vec_val.get_child_at_index(ptr_field_index)
310+
capacity = vec_val.get_child_at_index(cap_field_index).as_integer()
311+
312+
unique_ptr_val = vec_ptr_val.get_child_at_index(0)
313+
data_ptr = unique_ptr_val.get_child_at_index(0)
314+
assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR
315+
return (length, data_ptr, capacity)
316+
317+
def extract_length_and_ptr_from_slice(slice_val):
318+
assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or
319+
slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE)
320+
321+
length_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_LENGTH)
322+
ptr_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_DATA_PTR)
323+
324+
length = slice_val.get_child_at_index(length_field_index).as_integer()
325+
data_ptr = slice_val.get_child_at_index(ptr_field_index)
326+
327+
assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR
328+
return (length, data_ptr)

0 commit comments

Comments
 (0)