Skip to content

Commit c8e86e9

Browse files
committed
auto merge of #16322 : michaelwoerister/rust/gdb-pretty, r=alexcrichton
Also extends the autotest framework to let a test case choose if pretty printing should be enabled.
2 parents 5419b2c + e72e4df commit c8e86e9

File tree

11 files changed

+697
-30
lines changed

11 files changed

+697
-30
lines changed

configure

+7
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,13 @@ probe CFG_LUALATEX lualatex
515515
probe CFG_GDB gdb
516516
probe CFG_LLDB lldb
517517

518+
if [ ! -z "$CFG_GDB" ]
519+
then
520+
# Extract the version
521+
CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1)
522+
putvar CFG_GDB_VERSION
523+
fi
524+
518525
if [ ! -z "$CFG_LLDB" ]
519526
then
520527
# If CFG_LLDB_PYTHON_DIR is not already set from the outside and valid, try to read it from

mk/tests.mk

+1
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \
623623
--stage-id stage$(1)-$(2) \
624624
--target $(2) \
625625
--host $(3) \
626+
--gdb-version="$(CFG_GDB_VERSION)" \
626627
--android-cross-path=$(CFG_ANDROID_CROSS_PATH) \
627628
--adb-path=$(CFG_ADB) \
628629
--adb-test-dir=$(CFG_ADB_TEST_DIR) \

src/compiletest/common.rs

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ pub struct Config {
130130
// Host triple for the compiler being invoked
131131
pub host: String,
132132

133+
// Version of GDB
134+
pub gdb_version: Option<String>,
135+
133136
// Path to the android tools
134137
pub android_cross_path: Path,
135138

src/compiletest/compiletest.rs

+25
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
8181
optflag("", "jit", "run tests under the JIT"),
8282
optopt("", "target", "the target to build for", "TARGET"),
8383
optopt("", "host", "the host to build for", "HOST"),
84+
optopt("", "gdb-version", "the version of GDB used", "MAJOR.MINOR"),
8485
optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
8586
optopt("", "adb-path", "path to the android debugger", "PATH"),
8687
optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
@@ -157,6 +158,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
157158
jit: matches.opt_present("jit"),
158159
target: opt_str2(matches.opt_str("target")),
159160
host: opt_str2(matches.opt_str("host")),
161+
gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
160162
android_cross_path: opt_path(matches, "android-cross-path"),
161163
adb_path: opt_str2(matches.opt_str("adb-path")),
162164
adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
@@ -376,3 +378,26 @@ pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::Test
376378
runtest::run_metrics(config, testfile, mm)
377379
})
378380
}
381+
382+
fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
383+
match full_version_line {
384+
Some(ref full_version_line)
385+
if full_version_line.as_slice().trim().len() > 0 => {
386+
let full_version_line = full_version_line.as_slice().trim();
387+
388+
let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
389+
390+
match re.captures(full_version_line) {
391+
Some(captures) => {
392+
Some(captures.at(2).to_string())
393+
}
394+
None => {
395+
println!("Could not extract GDB version from line '{}'",
396+
full_version_line);
397+
None
398+
}
399+
}
400+
},
401+
_ => None
402+
}
403+
}

src/compiletest/header.rs

+54-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use common::Config;
1212
use common;
1313
use util;
1414

15+
use std::from_str::FromStr;
16+
1517
pub struct TestProps {
1618
// Lines that should be expected, in order, on standard out
1719
pub error_patterns: Vec<String> ,
@@ -142,23 +144,42 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool {
142144
format!("ignore-{}",
143145
config.stage_id.as_slice().split('-').next().unwrap())
144146
}
147+
fn ignore_gdb(config: &Config, line: &str) -> bool {
148+
if config.mode != common::DebugInfoGdb {
149+
return false;
150+
}
145151

146-
let val = iter_header(testfile, |ln| {
147-
if parse_name_directive(ln, "ignore-test") {
148-
false
149-
} else if parse_name_directive(ln, ignore_target(config).as_slice()) {
150-
false
151-
} else if parse_name_directive(ln, ignore_stage(config).as_slice()) {
152-
false
153-
} else if config.mode == common::Pretty &&
154-
parse_name_directive(ln, "ignore-pretty") {
155-
false
156-
} else if config.target != config.host &&
157-
parse_name_directive(ln, "ignore-cross-compile") {
158-
false
159-
} else {
160-
true
152+
if parse_name_directive(line, "ignore-gdb") {
153+
return true;
161154
}
155+
156+
match config.gdb_version {
157+
Some(ref actual_version) => {
158+
if line.contains("min-gdb-version") {
159+
let min_version = line.trim()
160+
.split(' ')
161+
.last()
162+
.expect("Malformed GDB version directive");
163+
// Ignore if actual version is smaller the minimum required
164+
// version
165+
gdb_version_to_int(actual_version.as_slice()) <
166+
gdb_version_to_int(min_version.as_slice())
167+
} else {
168+
false
169+
}
170+
}
171+
None => false
172+
}
173+
}
174+
175+
let val = iter_header(testfile, |ln| {
176+
!parse_name_directive(ln, "ignore-test") &&
177+
!parse_name_directive(ln, ignore_target(config).as_slice()) &&
178+
!parse_name_directive(ln, ignore_stage(config).as_slice()) &&
179+
!(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) &&
180+
!(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) &&
181+
!ignore_gdb(config, ln) &&
182+
!(config.mode == common::DebugInfoLldb && parse_name_directive(ln, "ignore-lldb"))
162183
});
163184

164185
!val
@@ -278,3 +299,21 @@ pub fn parse_name_value_directive(line: &str, directive: &str)
278299
None => None
279300
}
280301
}
302+
303+
pub fn gdb_version_to_int(version_string: &str) -> int {
304+
let error_string = format!(
305+
"Encountered GDB version string with unexpected format: {}",
306+
version_string);
307+
let error_string = error_string.as_slice();
308+
309+
let components: Vec<&str> = version_string.trim().split('.').collect();
310+
311+
if components.len() != 2 {
312+
fail!("{}", error_string);
313+
}
314+
315+
let major: int = FromStr::from_str(components[0]).expect(error_string);
316+
let minor: int = FromStr::from_str(components[1]).expect(error_string);
317+
318+
return major * 1000 + minor;
319+
}

src/compiletest/runtest.rs

+101-13
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,12 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
323323
};
324324

325325
let config = &mut config;
326-
let DebuggerCommands { commands, check_lines, .. } = parse_debugger_commands(testfile, "gdb");
326+
let DebuggerCommands {
327+
commands,
328+
check_lines,
329+
use_gdb_pretty_printer,
330+
..
331+
} = parse_debugger_commands(testfile, "gdb");
327332
let mut cmds = commands.connect("\n");
328333

329334
// compile test file (it should have 'compile-flags:-g' in the header)
@@ -334,7 +339,6 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
334339

335340
let exe_file = make_exe_name(config, testfile);
336341

337-
let mut proc_args;
338342
let debugger_run_result;
339343
match config.target.as_slice() {
340344
"arm-linux-androideabi" => {
@@ -454,18 +458,65 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
454458
}
455459

456460
_=> {
461+
let rust_src_root = find_rust_src_root(config)
462+
.expect("Could not find Rust source root");
463+
let rust_pp_module_rel_path = Path::new("./src/etc");
464+
let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
465+
.as_str()
466+
.unwrap()
467+
.to_string();
457468
// write debugger script
458-
let script_str = [
459-
"set charset UTF-8".to_string(),
460-
cmds,
461-
"quit\n".to_string()
462-
].connect("\n");
469+
let mut script_str = String::with_capacity(2048);
470+
471+
script_str.push_str("set charset UTF-8\n");
472+
script_str.push_str("show version\n");
473+
474+
match config.gdb_version {
475+
Some(ref version) => {
476+
println!("NOTE: compiletest thinks it is using GDB version {}",
477+
version.as_slice());
478+
479+
if header::gdb_version_to_int(version.as_slice()) >
480+
header::gdb_version_to_int("7.4") {
481+
// Add the directory containing the pretty printers to
482+
// GDB's script auto loading safe path ...
483+
script_str.push_str(
484+
format!("add-auto-load-safe-path {}\n",
485+
rust_pp_module_abs_path.as_slice())
486+
.as_slice());
487+
// ... and also the test directory
488+
script_str.push_str(
489+
format!("add-auto-load-safe-path {}\n",
490+
config.build_base.as_str().unwrap())
491+
.as_slice());
492+
}
493+
}
494+
_ => {
495+
println!("NOTE: compiletest does not know which version of \
496+
GDB it is using");
497+
}
498+
}
499+
500+
// Load the target executable
501+
script_str.push_str(format!("file {}\n",
502+
exe_file.as_str().unwrap())
503+
.as_slice());
504+
505+
script_str.push_str(cmds.as_slice());
506+
script_str.push_str("quit\n");
507+
463508
debug!("script_str = {}", script_str);
464509
dump_output_file(config,
465510
testfile,
466511
script_str.as_slice(),
467512
"debugger.script");
468513

514+
if use_gdb_pretty_printer {
515+
// Only emit the gdb auto-loading script if pretty printers
516+
// should actually be loaded
517+
dump_gdb_autoload_script(config, testfile);
518+
}
519+
469520
// run debugger script with gdb
470521
#[cfg(windows)]
471522
fn debugger() -> String {
@@ -483,16 +534,19 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
483534
vec!("-quiet".to_string(),
484535
"-batch".to_string(),
485536
"-nx".to_string(),
486-
format!("-command={}", debugger_script.as_str().unwrap()),
487-
exe_file.as_str().unwrap().to_string());
488-
proc_args = ProcArgs {
537+
format!("-command={}", debugger_script.as_str().unwrap()));
538+
539+
let proc_args = ProcArgs {
489540
prog: debugger(),
490541
args: debugger_opts,
491542
};
543+
544+
let environment = vec![("PYTHONPATH".to_string(), rust_pp_module_abs_path)];
545+
492546
debugger_run_result = compose_and_run(config,
493547
testfile,
494548
proc_args,
495-
Vec::new(),
549+
environment,
496550
config.run_lib_path.as_slice(),
497551
None,
498552
None);
@@ -504,6 +558,32 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
504558
}
505559

506560
check_debugger_output(&debugger_run_result, check_lines.as_slice());
561+
562+
fn dump_gdb_autoload_script(config: &Config, testfile: &Path) {
563+
let mut script_path = output_base_name(config, testfile);
564+
let mut script_file_name = script_path.filename().unwrap().to_vec();
565+
script_file_name.push_all("-gdb.py".as_bytes());
566+
script_path.set_filename(script_file_name.as_slice());
567+
568+
let script_content = "import gdb_rust_pretty_printing\n\
569+
gdb_rust_pretty_printing.register_printers(gdb.current_objfile())\n"
570+
.as_bytes();
571+
572+
File::create(&script_path).write(script_content).unwrap();
573+
}
574+
}
575+
576+
fn find_rust_src_root(config: &Config) -> Option<Path> {
577+
let mut path = config.src_base.clone();
578+
let path_postfix = Path::new("src/etc/lldb_batchmode.py");
579+
580+
while path.pop() {
581+
if path.join(path_postfix.clone()).is_file() {
582+
return Some(path);
583+
}
584+
}
585+
586+
return None;
507587
}
508588

509589
fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
@@ -533,7 +613,8 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
533613
let DebuggerCommands {
534614
commands,
535615
check_lines,
536-
breakpoint_lines
616+
breakpoint_lines,
617+
..
537618
} = parse_debugger_commands(testfile, "lldb");
538619

539620
// Write debugger script:
@@ -619,6 +700,7 @@ struct DebuggerCommands {
619700
commands: Vec<String>,
620701
check_lines: Vec<String>,
621702
breakpoint_lines: Vec<uint>,
703+
use_gdb_pretty_printer: bool
622704
}
623705

624706
fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
@@ -631,6 +713,7 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
631713
let mut breakpoint_lines = vec!();
632714
let mut commands = vec!();
633715
let mut check_lines = vec!();
716+
let mut use_gdb_pretty_printer = false;
634717
let mut counter = 1;
635718
let mut reader = BufferedReader::new(File::open(file_path).unwrap());
636719
for line in reader.lines() {
@@ -640,6 +723,10 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
640723
breakpoint_lines.push(counter);
641724
}
642725

726+
if line.as_slice().contains("gdb-use-pretty-printer") {
727+
use_gdb_pretty_printer = true;
728+
}
729+
643730
header::parse_name_value_directive(
644731
line.as_slice(),
645732
command_directive.as_slice()).map(|cmd| {
@@ -663,7 +750,8 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
663750
DebuggerCommands {
664751
commands: commands,
665752
check_lines: check_lines,
666-
breakpoint_lines: breakpoint_lines
753+
breakpoint_lines: breakpoint_lines,
754+
use_gdb_pretty_printer: use_gdb_pretty_printer,
667755
}
668756
}
669757

0 commit comments

Comments
 (0)