Skip to content

Commit 83fe455

Browse files
debuginfo: Add python formatters that allow LLDB to print values with Rust syntax
1 parent 6c35d51 commit 83fe455

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

src/etc/lldb_rust_formatters.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
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+
13+
def print_val(val, internal_dict):
14+
'''Prints the given value with Rust syntax'''
15+
type_class = val.GetType().GetTypeClass()
16+
17+
if type_class == lldb.eTypeClassStruct:
18+
return print_struct_val(val, internal_dict)
19+
20+
if type_class == lldb.eTypeClassUnion:
21+
return print_enum_val(val, internal_dict)
22+
23+
if type_class == lldb.eTypeClassPointer:
24+
return print_pointer_val(val, internal_dict)
25+
26+
if type_class == lldb.eTypeClassArray:
27+
return print_fixed_size_vec_val(val, internal_dict)
28+
29+
return val.GetValue()
30+
31+
32+
#=--------------------------------------------------------------------------------------------------
33+
# Type-Specialized Printing Functions
34+
#=--------------------------------------------------------------------------------------------------
35+
36+
def print_struct_val(val, internal_dict):
37+
'''Prints a struct, tuple, or tuple struct value with Rust syntax'''
38+
assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
39+
40+
if is_vec_slice(val):
41+
return print_vec_slice_val(val, internal_dict)
42+
else:
43+
return print_struct_val_starting_from(0, val, internal_dict)
44+
45+
def print_vec_slice_val(val, internal_dict):
46+
output = "&["
47+
48+
length = val.GetChildAtIndex(1).GetValueAsUnsigned()
49+
50+
data_ptr_val = val.GetChildAtIndex(0)
51+
data_ptr_type = data_ptr_val.GetType()
52+
assert data_ptr_type.IsPointerType()
53+
54+
element_type = data_ptr_type.GetPointeeType()
55+
element_type_size = element_type.GetByteSize()
56+
57+
start_address = data_ptr_val.GetValueAsUnsigned()
58+
59+
for i in range(length):
60+
address = start_address + i * element_type_size
61+
element_val = val.CreateValueFromAddress( val.GetName() + ("[%s]" % i), address, element_type )
62+
output += print_val(element_val, internal_dict)
63+
64+
if i != length - 1:
65+
output += ", "
66+
67+
output += "]"
68+
return output
69+
70+
def print_struct_val_starting_from(field_start_index, val, internal_dict):
71+
'''
72+
Prints a struct, tuple, or tuple struct value with Rust syntax.
73+
Ignores any fields before field_start_index.
74+
'''
75+
assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
76+
77+
t = val.GetType()
78+
has_field_names = type_has_field_names(t)
79+
type_name = extract_type_name(t.GetName())
80+
output = ""
81+
82+
if not type_name.startswith("("):
83+
# this is a tuple, so don't print the type name
84+
output += type_name
85+
86+
if has_field_names:
87+
output += " { \n"
88+
else:
89+
output += "("
90+
91+
num_children = val.num_children
92+
93+
for child_index in range(field_start_index, num_children):
94+
if has_field_names:
95+
field_name = t.GetFieldAtIndex(child_index).GetName()
96+
output += field_name + ": "
97+
98+
field_val = val.GetChildAtIndex(child_index)
99+
output += print_val(field_val, internal_dict)
100+
101+
if child_index != num_children - 1:
102+
output += ", "
103+
104+
if has_field_names:
105+
output += "\n"
106+
107+
if has_field_names:
108+
output += "}"
109+
else:
110+
output += ")"
111+
112+
return output
113+
114+
115+
def print_enum_val(val, internal_dict):
116+
'''Prints an enum value with Rust syntax'''
117+
118+
assert val.GetType().GetTypeClass() == lldb.eTypeClassUnion
119+
120+
if val.num_children == 1:
121+
first_variant_name = val.GetChildAtIndex(0).GetName()
122+
if first_variant_name and first_variant_name.startswith("RUST$ENCODED$ENUM$"):
123+
# Try to extract the
124+
125+
last_separator_index = first_variant_name.rfind("$")
126+
if last_separator_index == -1:
127+
return "<invalid enum encoding: %s>" % first_variant_name
128+
129+
second_last_separator_index = first_variant_name.rfind("$", 0, last_separator_index)
130+
if second_last_separator_index == -1:
131+
return "<invalid enum encoding: %s>" % first_variant_name
132+
133+
try:
134+
disr_field_index = first_variant_name[second_last_separator_index + 1 :
135+
last_separator_index]
136+
disr_field_index = int(disr_field_index)
137+
except:
138+
return "<invalid enum encoding: %s>" % first_variant_name
139+
140+
disr_val = val.GetChildAtIndex(0).GetChildAtIndex(disr_field_index).GetValueAsUnsigned()
141+
142+
if disr_val == 0:
143+
null_variant_name = first_variant_name[last_separator_index + 1:]
144+
return null_variant_name
145+
else:
146+
return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
147+
else:
148+
return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
149+
150+
# extract the discriminator value by
151+
disr_val = val.GetChildAtIndex(0).GetChildAtIndex(0)
152+
disr_type = disr_val.GetType()
153+
154+
if disr_type.GetTypeClass() != lldb.eTypeClassEnumeration:
155+
return "<Invalid enum value encountered: Discriminator is not an enum>"
156+
157+
variant_index = disr_val.GetValueAsUnsigned()
158+
return print_struct_val_starting_from(1, val.GetChildAtIndex(variant_index), internal_dict)
159+
160+
161+
def print_pointer_val(val, internal_dict):
162+
'''Prints a pointer value with Rust syntax'''
163+
assert val.GetType().IsPointerType()
164+
sigil = "&"
165+
type_name = extract_type_name(val.GetType().GetName())
166+
if type_name and type_name[0:1] in ["&", "~", "*"]:
167+
sigil = type_name[0:1]
168+
169+
return sigil + hex(val.GetValueAsUnsigned()) #print_val(val.Dereference(), internal_dict)
170+
171+
172+
def print_fixed_size_vec_val(val, internal_dict):
173+
assert val.GetType().GetTypeClass() == lldb.eTypeClassArray
174+
175+
output = "["
176+
177+
for i in range(val.num_children):
178+
output += print_val(val.GetChildAtIndex(i), internal_dict)
179+
if i != val.num_children - 1:
180+
output += ", "
181+
182+
output += "]"
183+
return output
184+
185+
186+
#=--------------------------------------------------------------------------------------------------
187+
# Helper Functions
188+
#=--------------------------------------------------------------------------------------------------
189+
190+
unqualified_type_markers = frozenset(["(", "[", "&", "*"])
191+
192+
def extract_type_name(qualified_type_name):
193+
'''Extracts the type name from a fully qualified path'''
194+
if qualified_type_name[0] in unqualified_type_markers:
195+
return qualified_type_name
196+
197+
end_of_search = qualified_type_name.find("<")
198+
if end_of_search < 0:
199+
end_of_search = len(qualified_type_name)
200+
201+
index = qualified_type_name.rfind("::", 0, end_of_search)
202+
if index < 0:
203+
return qualified_type_name
204+
else:
205+
return qualified_type_name[index + 2:]
206+
207+
208+
def type_has_field_names(ty):
209+
'''Returns true of this is a type with field names (struct, struct-like enum variant)'''
210+
# This may also be an enum variant where the first field doesn't have a name but the rest has
211+
if ty.GetNumberOfFields() > 1:
212+
return ty.GetFieldAtIndex(1).GetName() != None
213+
else:
214+
return ty.GetFieldAtIndex(0).GetName() != None
215+
216+
217+
def is_vec_slice(val):
218+
ty = val.GetType()
219+
if ty.GetTypeClass() != lldb.eTypeClassStruct:
220+
return False
221+
222+
if ty.GetNumberOfFields() != 2:
223+
return False
224+
225+
if ty.GetFieldAtIndex(0).GetName() != "data_ptr":
226+
return False
227+
228+
if ty.GetFieldAtIndex(1).GetName() != "length":
229+
return False
230+
231+
type_name = extract_type_name(ty.GetName()).replace("&'static", "&").replace(" ", "")
232+
return type_name.startswith("&[") and type_name.endswith("]")

0 commit comments

Comments
 (0)