]> git.lizzy.rs Git - rust.git/commitdiff
debuginfo: Emit different autotest debugger scripts depending on GDB version.
authorMichael Woerister <michaelwoerister@posteo>
Wed, 20 Aug 2014 10:53:50 +0000 (12:53 +0200)
committerMichael Woerister <michaelwoerister@posteo>
Wed, 27 Aug 2014 13:19:14 +0000 (15:19 +0200)
configure
mk/tests.mk
src/compiletest/common.rs
src/compiletest/compiletest.rs
src/compiletest/header.rs
src/compiletest/runtest.rs
src/etc/gdb_rust_pretty_printing.py
src/test/debuginfo/gdb-pretty-struct-and-enums-pre-gdb-7-7.rs [new file with mode: 0644]
src/test/debuginfo/gdb-pretty-struct-and-enums.rs

index 5a2f9b5f20c763d01cffad1a880ef4251764a470..1d6c387caa7c669811a7fa936bd4dd3b2840728d 100755 (executable)
--- 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
index f78243046801d6defaa9e560def866776fe575ff..d95f886e078896cb4649f3905883310401bcbe62 100644 (file)
@@ -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) \
index ba201a4a633c62708d2902513cb499de800974e0..afe2d071461a0317356c811f443b8f803e5bb8a8 100644 (file)
@@ -130,6 +130,9 @@ pub struct Config {
     // Host triple for the compiler being invoked
     pub host: String,
 
+    // Version of GDB
+    pub gdb_version: Option<String>,
+
     // Path to the android tools
     pub android_cross_path: Path,
 
index 583d9249b35476a72f59346dac2e8f544e94f1e5..31b37070d2f139f7f04ceb69bc76c9c25719e09b 100644 (file)
@@ -81,6 +81,7 @@ pub fn parse_config(args: Vec<String> ) -> 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 @@ fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
         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<String>) -> Option<String> {
+    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
index f6cd217b580cd24be78747dc23cca861906771ef..9ad2582dec8456d60c6d52d4aaf2534a6f6f1e8e 100644 (file)
@@ -12,6 +12,8 @@
 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<String> ,
@@ -142,23 +144,42 @@ fn ignore_stage(config: &Config) -> String {
         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;
+        }
 
-    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
+        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| {
+        !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;
+}
index 27f6fbcf9f63ab9fab29e8f679cdee0062fead13..6a1e1c6cc76bc07c3202550182a4167dfaf8790a 100644 (file)
@@ -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 debugger() -> String {
                 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(),
index c84dde92f8fe40549dc9cac51d097bbc8882c64d..e8a6427c1d7317cb6590835cc1e2347e1a7d7f1c 100644 (file)
@@ -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 (file)
index 0000000..e9daf31
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() { () }
index 51dad709b6f532ed7bb1ba24a981abeec71d2267..5ef63da71af8cf2ce2a894466824d6d1835d6132 100644 (file)
 // 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() { () }