]> git.lizzy.rs Git - rust.git/blob - src/etc/lldb_rust_formatters.py
Add a name for tuple fields in debuginfo so that they can be accessed in debuggers.
[rust.git] / src / etc / lldb_rust_formatters.py
1 # Copyright 2014 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 import lldb
12 import re
13
14 def print_val(val, internal_dict):
15     '''Prints the given value with Rust syntax'''
16     type_class = val.GetType().GetTypeClass()
17
18     if type_class == lldb.eTypeClassStruct:
19         return print_struct_val(val, internal_dict)
20
21     if type_class == lldb.eTypeClassUnion:
22         return print_enum_val(val, internal_dict)
23
24     if type_class == lldb.eTypeClassPointer:
25         return print_pointer_val(val, internal_dict)
26
27     if type_class == lldb.eTypeClassArray:
28         return print_fixed_size_vec_val(val, internal_dict)
29
30     return val.GetValue()
31
32
33 #=--------------------------------------------------------------------------------------------------
34 # Type-Specialized Printing Functions
35 #=--------------------------------------------------------------------------------------------------
36
37 def print_struct_val(val, internal_dict):
38     '''Prints a struct, tuple, or tuple struct value with Rust syntax'''
39     assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
40
41     if is_vec_slice(val):
42         return print_vec_slice_val(val, internal_dict)
43     elif is_std_vec(val):
44         return print_std_vec_val(val, internal_dict)
45     else:
46         return print_struct_val_starting_from(0, val, internal_dict)
47
48
49 def print_struct_val_starting_from(field_start_index, val, internal_dict):
50     '''
51     Prints a struct, tuple, or tuple struct value with Rust syntax.
52     Ignores any fields before field_start_index.
53     '''
54     assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
55
56     t = val.GetType()
57     type_name = extract_type_name(t.GetName())
58     num_children = val.num_children
59
60     if (num_children - field_start_index) == 0:
61         # The only field of this struct is the enum discriminant
62         return type_name
63
64     is_tuple_like = type_is_tuple_like(t)
65
66     if is_tuple_like:
67         template = "%(type_name)s(%(body)s)"
68         separator = ", "
69     else:
70         template = "%(type_name)s {\n%(body)s\n}"
71         separator = ", \n"
72
73     if type_name.startswith("("):
74         # this is a tuple, so don't print the type name
75         type_name = ""
76
77     def render_child(child_index):
78         this = ""
79         if not is_tuple_like:
80             field_name = t.GetFieldAtIndex(child_index).GetName()
81             this += field_name + ": "
82
83         field_val = val.GetChildAtIndex(child_index)
84
85         if not field_val.IsValid():
86             field = t.GetFieldAtIndex(child_index)
87             # LLDB is not good at handling zero-sized values, so we have to help
88             # it a little
89             if field.GetType().GetByteSize() == 0:
90                 return this + extract_type_name(field.GetType().GetName())
91             else:
92                 return this + "<invalid value>"
93
94         return this + print_val(field_val, internal_dict)
95
96     body = separator.join([render_child(idx) for idx in range(field_start_index, num_children)])
97
98     return template % {"type_name": type_name,
99                        "body": body}
100
101
102 def print_enum_val(val, internal_dict):
103     '''Prints an enum value with Rust syntax'''
104
105     assert val.GetType().GetTypeClass() == lldb.eTypeClassUnion
106
107     if val.num_children == 1:
108         # This is either an enum with just one variant, or it is an Option-like
109         # enum where the discriminant is encoded in a non-nullable pointer
110         # field. We find out which one it is by looking at the member name of
111         # the sole union variant. If it starts with "RUST$ENCODED$ENUM$" then
112         # we have an Option-like enum.
113         first_variant_name = val.GetChildAtIndex(0).GetName()
114         if first_variant_name and first_variant_name.startswith("RUST$ENCODED$ENUM$"):
115
116             # This is an Option-like enum. The position of the discriminator field is
117             # encoded in the name which has the format:
118             #  RUST$ENCODED$ENUM$<index of discriminator field>$<name of null variant>
119             last_separator_index = first_variant_name.rfind("$")
120             if last_separator_index == -1:
121                 return "<invalid enum encoding: %s>" % first_variant_name
122
123             start_index = len("RUST$ENCODED$ENUM$")
124
125             # Extract indices of the discriminator field
126             try:
127                 disr_field_indices = first_variant_name[start_index:last_separator_index].split("$")
128                 disr_field_indices = [int(index) for index in disr_field_indices]
129             except:
130                 return "<invalid enum encoding: %s>" % first_variant_name
131
132             # Read the discriminant
133             disr_val = val.GetChildAtIndex(0)
134             for index in disr_field_indices:
135                 disr_val = disr_val.GetChildAtIndex(index)
136
137             # If the discriminant field is a fat pointer we have to consider the
138             # first word as the true discriminant
139             if disr_val.GetType().GetTypeClass() == lldb.eTypeClassStruct:
140                 disr_val = disr_val.GetChildAtIndex(0)
141
142             if disr_val.GetValueAsUnsigned() == 0:
143                 # Null case: Print the name of the null-variant
144                 null_variant_name = first_variant_name[last_separator_index + 1:]
145                 return null_variant_name
146             else:
147                 # Non-null case: Interpret the data as a value of the non-null variant type
148                 return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
149         else:
150             # This is just a regular uni-variant enum without discriminator field
151             return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
152
153     # If we are here, this is a regular enum with more than one variant
154     disr_val = val.GetChildAtIndex(0).GetChildMemberWithName("RUST$ENUM$DISR")
155     disr_type = disr_val.GetType()
156
157     if disr_type.GetTypeClass() != lldb.eTypeClassEnumeration:
158         return "<Invalid enum value encountered: Discriminator is not an enum>"
159
160     variant_index = disr_val.GetValueAsUnsigned()
161     return print_struct_val_starting_from(1, val.GetChildAtIndex(variant_index), internal_dict)
162
163
164 def print_pointer_val(val, internal_dict):
165     '''Prints a pointer value with Rust syntax'''
166     assert val.GetType().IsPointerType()
167     sigil = "&"
168     type_name = extract_type_name(val.GetType().GetName())
169     if type_name and type_name[0:1] in ["&", "~", "*"]:
170         sigil = type_name[0:1]
171
172     return sigil + hex(val.GetValueAsUnsigned()) #print_val(val.Dereference(), internal_dict)
173
174
175 def print_fixed_size_vec_val(val, internal_dict):
176     assert val.GetType().GetTypeClass() == lldb.eTypeClassArray
177
178     output = "["
179
180     for i in range(val.num_children):
181         output += print_val(val.GetChildAtIndex(i), internal_dict)
182         if i != val.num_children - 1:
183             output += ", "
184
185     output += "]"
186     return output
187
188
189 def print_vec_slice_val(val, internal_dict):
190     length = val.GetChildAtIndex(1).GetValueAsUnsigned()
191
192     data_ptr_val = val.GetChildAtIndex(0)
193     data_ptr_type = data_ptr_val.GetType()
194
195     return "&[%s]" % print_array_of_values(val.GetName(),
196                                            data_ptr_val,
197                                            length,
198                                            internal_dict)
199
200
201 def print_std_vec_val(val, internal_dict):
202     length = val.GetChildAtIndex(1).GetValueAsUnsigned()
203
204     # Vec<> -> Unique<> -> NonZero<> -> *T
205     data_ptr_val = val.GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(0)
206     data_ptr_type = data_ptr_val.GetType()
207
208     return "vec![%s]" % print_array_of_values(val.GetName(),
209                                               data_ptr_val,
210                                               length,
211                                               internal_dict)
212
213 #=--------------------------------------------------------------------------------------------------
214 # Helper Functions
215 #=--------------------------------------------------------------------------------------------------
216
217 unqualified_type_markers = frozenset(["(", "[", "&", "*"])
218
219
220 def extract_type_name(qualified_type_name):
221     '''Extracts the type name from a fully qualified path'''
222     if qualified_type_name[0] in unqualified_type_markers:
223         return qualified_type_name
224
225     end_of_search = qualified_type_name.find("<")
226     if end_of_search < 0:
227         end_of_search = len(qualified_type_name)
228
229     index = qualified_type_name.rfind("::", 0, end_of_search)
230     if index < 0:
231         return qualified_type_name
232     else:
233         return qualified_type_name[index + 2:]
234
235
236 def type_is_tuple_like(ty):
237     '''Returns true of this is a type with field names (struct, struct-like enum variant)'''
238     for field in ty.fields:
239         if field.GetName() == "RUST$ENUM$DISR":
240             # Ignore the enum discriminant field if there is one.
241             continue
242         if (field.GetName() is None) or (re.match(r"__\d+$", field.GetName()) is None):
243             return False
244     return True
245
246
247 def is_vec_slice(val):
248     ty = val.GetType()
249     if ty.GetTypeClass() != lldb.eTypeClassStruct:
250         return False
251
252     if ty.GetNumberOfFields() != 2:
253         return False
254
255     if ty.GetFieldAtIndex(0).GetName() != "data_ptr":
256         return False
257
258     if ty.GetFieldAtIndex(1).GetName() != "length":
259         return False
260
261     type_name = extract_type_name(ty.GetName()).replace("&'static", "&").replace(" ", "")
262     return type_name.startswith("&[") and type_name.endswith("]")
263
264 def is_std_vec(val):
265     ty = val.GetType()
266     if ty.GetTypeClass() != lldb.eTypeClassStruct:
267         return False
268
269     if ty.GetNumberOfFields() != 3:
270         return False
271
272     if ty.GetFieldAtIndex(0).GetName() != "ptr":
273         return False
274
275     if ty.GetFieldAtIndex(1).GetName() != "len":
276         return False
277
278     if ty.GetFieldAtIndex(2).GetName() != "cap":
279         return False
280
281     return ty.GetName().startswith("collections::vec::Vec<")
282
283
284 def print_array_of_values(array_name, data_ptr_val, length, internal_dict):
285     '''Prints a contigous memory range, interpreting it as values of the
286        pointee-type of data_ptr_val.'''
287
288     data_ptr_type = data_ptr_val.GetType()
289     assert data_ptr_type.IsPointerType()
290
291     element_type = data_ptr_type.GetPointeeType()
292     element_type_size = element_type.GetByteSize()
293
294     start_address = data_ptr_val.GetValueAsUnsigned()
295
296     def render_element(i):
297         address = start_address + i * element_type_size
298         element_val = data_ptr_val.CreateValueFromAddress(array_name + ("[%s]" % i),
299                                                           address,
300                                                           element_type)
301         return print_val(element_val, internal_dict)
302
303     return ', '.join([render_element(i) for i in range(length)])