Skip to content

Commit 175f113

Browse files
committed
auto merge of #15573 : michaelwoerister/rust/lldb-tests-rebased-09-Jul, r=alexcrichton
This PR adds the LLDB autotests to the debuginfo test suite so I don't have to keep rebasing them locally. They are still disabled by default in `tests.mk`. One of the commits also contains a Python pretty printer which can make LLDB print values with Rust syntax. This was mainly added to deal with output format differences between LLDB versions but you can also use it for your normal LLDB debugging sessions. ``` // The following LLDB commands will load and activate the Rust printers command script import ./src/etc/lldb_rust_formatters.py type summary add --no-value --python-function lldb_rust_formatters.print_val -x .* --category Rust type category enable Rust ``` Expect some rough edges with these, they have not been tested apart from there use in the autotests...
2 parents 316719e + 3b336b2 commit 175f113

File tree

86 files changed

+3569
-265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3569
-265
lines changed

src/compiletest/compiletest.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use std::io::fs;
3030
use std::from_str::FromStr;
3131
use getopts::{optopt, optflag, reqopt};
3232
use common::Config;
33-
use common::{Pretty, DebugInfoGdb, Codegen};
33+
use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
3434
use util::logv;
3535
use regex::Regex;
3636

@@ -241,6 +241,16 @@ pub fn run_tests(config: &Config) {
241241
os::setenv("RUST_TEST_TASKS","1");
242242
}
243243

244+
match config.mode {
245+
DebugInfoLldb => {
246+
// Some older versions of LLDB seem to have problems with multiple
247+
// instances running in parallel, so only run one test task at a
248+
// time.
249+
os::setenv("RUST_TEST_TASKS", "1");
250+
}
251+
_ => { /* proceed */ }
252+
}
253+
244254
let opts = test_opts(config);
245255
let tests = make_tests(config);
246256
// sadly osx needs some file descriptor limits raised for running tests in

src/compiletest/runtest.rs

+10
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,16 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
536536
// We don't want to hang when calling `quit` while the process is still running
537537
let mut script_str = String::from_str("settings set auto-confirm true\n");
538538

539+
// Make LLDB emit its version, so we have it documented in the test output
540+
script_str.push_str("version\n");
541+
542+
// Switch LLDB into "Rust mode"
543+
script_str.push_str("command script import ./src/etc/lldb_rust_formatters.py\n");
544+
script_str.push_str("type summary add --no-value ");
545+
script_str.push_str("--python-function lldb_rust_formatters.print_val ");
546+
script_str.push_str("-x \".*\" --category Rust\n");
547+
script_str.push_str("type category enable Rust\n");
548+
539549
// Set breakpoints on every line that contains the string "#break"
540550
for line in breakpoint_lines.iter() {
541551
script_str.push_str(format!("breakpoint set --line {}\n",

src/etc/lldb_batchmode.py

-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@
3131
import re
3232
import atexit
3333

34-
# Terminate the debugger
35-
atexit.register(lambda: lldb.SBDebugger.Terminate())
36-
3734
# Set this to True for additional output
3835
DEBUG_OUTPUT = False
3936

src/etc/lldb_rust_formatters.py

+232
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("]")

src/test/debuginfo/basic-types.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
// ignore-android: FIXME(#10381)
1818

1919
// compile-flags:-g
20+
21+
// === GDB TESTS ===================================================================================
22+
2023
// gdb-command:rbreak zzz
2124
// gdb-command:run
2225
// gdb-command:finish
@@ -49,6 +52,42 @@
4952
// gdb-command:print f64
5053
// gdb-check:$14 = 3.5
5154

55+
56+
// === LLDB TESTS ==================================================================================
57+
58+
// lldb-command:run
59+
// lldb-command:print b
60+
// lldb-check:[...]$0 = false
61+
// lldb-command:print i
62+
// lldb-check:[...]$1 = -1
63+
64+
// NOTE: LLDB does not support 32bit chars
65+
// d ebugger:print (uint)(c)
66+
// c heck:$3 = 97
67+
68+
// lldb-command:print i8
69+
// lldb-check:[...]$2 = 'D'
70+
// lldb-command:print i16
71+
// lldb-check:[...]$3 = -16
72+
// lldb-command:print i32
73+
// lldb-check:[...]$4 = -32
74+
// lldb-command:print i64
75+
// lldb-check:[...]$5 = -64
76+
// lldb-command:print u
77+
// lldb-check:[...]$6 = 1
78+
// lldb-command:print u8
79+
// lldb-check:[...]$7 = 'd'
80+
// lldb-command:print u16
81+
// lldb-check:[...]$8 = 16
82+
// lldb-command:print u32
83+
// lldb-check:[...]$9 = 32
84+
// lldb-command:print u64
85+
// lldb-check:[...]$10 = 64
86+
// lldb-command:print f32
87+
// lldb-check:[...]$11 = 2.5
88+
// lldb-command:print f64
89+
// lldb-check:[...]$12 = 3.5
90+
5291
#![allow(unused_variable)]
5392

5493
fn main() {
@@ -66,7 +105,7 @@ fn main() {
66105
let u64: u64 = 64;
67106
let f32: f32 = 2.5;
68107
let f64: f64 = 3.5;
69-
_zzz();
108+
_zzz(); // #break
70109
}
71110

72111
fn _zzz() {()}

0 commit comments

Comments
 (0)