diff --git a/configure b/configure index 5a2f9b5f20c..1d6c387caa7 100755 --- a/configure +++ b/configure @@ -515,6 +515,13 @@ probe CFG_LUALATEX lualatex probe CFG_GDB gdb probe CFG_LLDB lldb +if [ ! -z "$CFG_GDB" ] +then + # Extract the version + CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1) + putvar CFG_GDB_VERSION +fi + if [ ! -z "$CFG_LLDB" ] then # If CFG_LLDB_PYTHON_DIR is not already set from the outside and valid, try to read it from diff --git a/mk/tests.mk b/mk/tests.mk index f7824304680..d95f886e078 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -623,6 +623,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --stage-id stage$(1)-$(2) \ --target $(2) \ --host $(3) \ + --gdb-version="$(CFG_GDB_VERSION)" \ --android-cross-path=$(CFG_ANDROID_CROSS_PATH) \ --adb-path=$(CFG_ADB) \ --adb-test-dir=$(CFG_ADB_TEST_DIR) \ diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index ba201a4a633..afe2d071461 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -130,6 +130,9 @@ pub struct Config { // Host triple for the compiler being invoked pub host: String, + // Version of GDB + pub gdb_version: Option, + // Path to the android tools pub android_cross_path: Path, diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 583d9249b35..31b37070d2f 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -81,6 +81,7 @@ pub fn parse_config(args: Vec ) -> Config { optflag("", "jit", "run tests under the JIT"), optopt("", "target", "the target to build for", "TARGET"), optopt("", "host", "the host to build for", "HOST"), + optopt("", "gdb-version", "the version of GDB used", "MAJOR.MINOR"), optopt("", "android-cross-path", "Android NDK standalone path", "PATH"), optopt("", "adb-path", "path to the android debugger", "PATH"), optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"), @@ -157,6 +158,7 @@ pub fn parse_config(args: Vec ) -> Config { jit: matches.opt_present("jit"), target: opt_str2(matches.opt_str("target")), host: opt_str2(matches.opt_str("host")), + gdb_version: extract_gdb_version(matches.opt_str("gdb-version")), android_cross_path: opt_path(matches, "android-cross-path"), adb_path: opt_str2(matches.opt_str("adb-path")), adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")), @@ -376,3 +378,20 @@ pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::Test runtest::run_metrics(config, testfile, mm) }) } + +fn extract_gdb_version(full_version_line: Option) -> Option { + match full_version_line { + Some(full_version_line) => { + let full_version_line = full_version_line.as_slice().trim(); + let re = Regex::new(r"[^0-9]([0-9]\.[0-9])([^0-9]|$)").unwrap(); + + match re.captures(full_version_line) { + Some(captures) => { + Some(captures.at(1).to_string()) + } + None => None + } + }, + None => None + } +} \ No newline at end of file diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index f6cd217b580..9ad2582dec8 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -12,6 +12,8 @@ use common::Config; use common; use util; +use std::from_str::FromStr; + pub struct TestProps { // Lines that should be expected, in order, on standard out pub error_patterns: Vec , @@ -142,23 +144,42 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { format!("ignore-{}", config.stage_id.as_slice().split('-').next().unwrap()) } + fn ignore_gdb(config: &Config, line: &str) -> bool { + if config.mode != common::DebugInfoGdb { + return false; + } + + if parse_name_directive(line, "ignore-gdb") { + return true; + } + + match config.gdb_version { + Some(ref actual_version) => { + if line.contains("min-gdb-version") { + let min_version = line.trim() + .split(' ') + .last() + .expect("Malformed GDB version directive"); + // Ignore if actual version is smaller the minimum required + // version + gdb_version_to_int(actual_version.as_slice()) < + gdb_version_to_int(min_version.as_slice()) + } else { + false + } + } + None => false + } + } let val = iter_header(testfile, |ln| { - if parse_name_directive(ln, "ignore-test") { - false - } else if parse_name_directive(ln, ignore_target(config).as_slice()) { - false - } else if parse_name_directive(ln, ignore_stage(config).as_slice()) { - false - } else if config.mode == common::Pretty && - parse_name_directive(ln, "ignore-pretty") { - false - } else if config.target != config.host && - parse_name_directive(ln, "ignore-cross-compile") { - false - } else { - true - } + !parse_name_directive(ln, "ignore-test") && + !parse_name_directive(ln, ignore_target(config).as_slice()) && + !parse_name_directive(ln, ignore_stage(config).as_slice()) && + !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) && + !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) && + !ignore_gdb(config, ln) && + !(config.mode == common::DebugInfoLldb && parse_name_directive(ln, "ignore-lldb")) }); !val @@ -278,3 +299,21 @@ pub fn parse_name_value_directive(line: &str, directive: &str) None => None } } + +pub fn gdb_version_to_int(version_string: &str) -> int { + let error_string = format!( + "Encountered GDB version string with unexpected format: {}", + version_string); + let error_string = error_string.as_slice(); + + let components: Vec<&str> = version_string.trim().split('.').collect(); + + if components.len() != 2 { + fail!("{}", error_string); + } + + let major: int = FromStr::from_str(components[0]).expect(error_string); + let minor: int = FromStr::from_str(components[1]).expect(error_string); + + return major * 1000 + minor; +} diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 27f6fbcf9f6..6a1e1c6cc76 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -466,11 +466,39 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { .unwrap() .to_string(); // write debugger script - let script_str = [ - "set charset UTF-8".to_string(), - cmds, - "quit\n".to_string() - ].connect("\n"); + let mut script_str = String::with_capacity(2048); + + script_str.push_str("set charset UTF-8\n"); + script_str.push_str("show version\n"); + + match config.gdb_version { + Some(ref version) => { + if header::gdb_version_to_int(version.as_slice()) > + header::gdb_version_to_int("7.4") { + // Add the directory containing the pretty printers to + // GDB's script auto loading safe path ... + script_str.push_str( + format!("add-auto-load-safe-path {}\n", + rust_pp_module_abs_path.as_slice()) + .as_slice()); + // ... and also the test directory + script_str.push_str( + format!("add-auto-load-safe-path {}\n", + config.build_base.as_str().unwrap()) + .as_slice()); + } + } + _ => { /* nothing to do */ } + } + + // Load the target executable + script_str.push_str(format!("file {}\n", + exe_file.as_str().unwrap()) + .as_slice()); + + script_str.push_str(cmds.as_slice()); + script_str.push_str("quit\n"); + debug!("script_str = {}", script_str); dump_output_file(config, testfile, @@ -500,15 +528,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { vec!("-quiet".to_string(), "-batch".to_string(), "-nx".to_string(), - // Add the directory containing the pretty printers to - // GDB's script auto loading safe path ... - format!("-iex=add-auto-load-safe-path {}", - rust_pp_module_abs_path.as_slice()), - // ... and also the test directory - format!("-iex=add-auto-load-safe-path {}", - config.build_base.as_str().unwrap()), - format!("-command={}", debugger_script.as_str().unwrap()), - exe_file.as_str().unwrap().to_string()); + format!("-command={}", debugger_script.as_str().unwrap())); let proc_args = ProcArgs { prog: debugger(), diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index c84dde92f8f..e8a6427c1d7 100644 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -80,8 +80,7 @@ def rust_pretty_printer_lookup_function(val): discriminant_name, discriminant_val = extract_discriminant_value(val) return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]]) - - + # No pretty printer has been found return None #=------------------------------------------------------------------------------ @@ -99,10 +98,17 @@ class RustStructPrinter: def children(self): cs = [] for field in self.val.type.fields(): - field_name = field.name; + field_name = field.name + # Normally the field name is used as a key to access the field value, + # because that's also supported in older versions of GDB... + field_key = field_name if field_name == None: field_name = "" - name_value_tuple = ( field_name, self.val[field] ) + # ... but for fields without a name (as in tuples), we have to fall back + # to the newer method of using the field object directly as key. In + # older versions of GDB, this will just fail. + field_key = field + name_value_tuple = ( field_name, self.val[field_key] ) cs.append( name_value_tuple ) if self.hide_first_field: @@ -222,4 +228,4 @@ def get_field_at_index(val, index): for field in val.type.fields(): if i == index: return field - return None \ No newline at end of file + return None diff --git a/src/test/debuginfo/gdb-pretty-struct-and-enums-pre-gdb-7-7.rs b/src/test/debuginfo/gdb-pretty-struct-and-enums-pre-gdb-7-7.rs new file mode 100644 index 00000000000..e9daf31be2c --- /dev/null +++ b/src/test/debuginfo/gdb-pretty-struct-and-enums-pre-gdb-7-7.rs @@ -0,0 +1,75 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test uses only GDB Python API features which should be available in +// older versions of GDB too. A more extensive test can be found in +// gdb-pretty-struct-and-enums.rs + +// ignore-tidy-linelength +// ignore-lldb +// ignore-android: FIXME(#10381) +// compile-flags:-g +// gdb-use-pretty-printer + +// The following line actually doesn't have to do anything with pretty printing, +// it just tells GDB to print values on one line: +// gdb-command: set print pretty off + +// gdb-command: rbreak zzz +// gdb-command: run +// gdb-command: finish + +// gdb-command: print regular_struct +// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false} + +// gdb-command: print empty_struct +// gdb-check:$2 = EmptyStruct + +// gdb-command: print c_style_enum1 +// gdb-check:$3 = CStyleEnumVar1 + +// gdb-command: print c_style_enum2 +// gdb-check:$4 = CStyleEnumVar2 + +// gdb-command: print c_style_enum3 +// gdb-check:$5 = CStyleEnumVar3 + +struct RegularStruct { + the_first_field: int, + the_second_field: f64, + the_third_field: bool, +} + +struct EmptyStruct; + +enum CStyleEnum { + CStyleEnumVar1, + CStyleEnumVar2, + CStyleEnumVar3, +} + +fn main() { + + let regular_struct = RegularStruct { + the_first_field: 101, + the_second_field: 102.5, + the_third_field: false + }; + + let empty_struct = EmptyStruct; + + let c_style_enum1 = CStyleEnumVar1; + let c_style_enum2 = CStyleEnumVar2; + let c_style_enum3 = CStyleEnumVar3; + + zzz(); +} + +fn zzz() { () } diff --git a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs index 51dad709b6f..5ef63da71af 100644 --- a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs +++ b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs @@ -14,6 +14,11 @@ // compile-flags:-g // gdb-use-pretty-printer +// This test uses some GDB Python API features (e.g. accessing anonymous fields) +// which are only available in newer GDB version. The following directive will +// case the test runner to ignore this test if an older GDB version is used: +// min-gdb-version 7.7 + // The following line actually doesn't have to do anything with pretty printing, // it just tells GDB to print values on one line: // gdb-command: set print pretty off @@ -164,4 +169,4 @@ fn main() { zzz(); } -fn zzz() { () } \ No newline at end of file +fn zzz() { () }