}
if suite == "debuginfo" {
- // Skip debuginfo tests on MSVC
- if builder.config.build.contains("msvc") {
- return;
- }
-
+ let msvc = builder.config.build.contains("msvc");
if mode == "debuginfo" {
return builder.ensure(Compiletest {
- mode: "debuginfo-both",
+ mode: if msvc { "debuginfo-cdb" } else { "debuginfo-gdb+lldb" },
..self
});
}
// min-lldb-version: 310
+// ignore-cdb: Fails with exit code 0xc0000135 ("the application failed to initialize properly")
// aux-build:issue-13213-aux.rs
-// ignore-windows failing on win32 bot
// ignore-freebsd: gdb package too new
-// ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+// only-cdb // Test temporarily ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// compile-flags:-g
// min-gdb-version 7.7
// lldb-check:[...]$5 = None
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx slice,d
+// cdb-check:slice,d [...]
+// NOTE: While slices have a .natvis entry that works in VS & VS Code, it fails in CDB 10.0.18362.1
+
+// cdb-command: dx vec,d
+// cdb-check:vec,d [...] : { size=4 } [Type: [...]::Vec<u64>]
+// cdb-check: [size] : 4 [Type: [...]]
+// cdb-check: [capacity] : [...] [Type: [...]]
+// cdb-check: [0] : 4 [Type: unsigned __int64]
+// cdb-check: [1] : 5 [Type: unsigned __int64]
+// cdb-check: [2] : 6 [Type: unsigned __int64]
+// cdb-check: [3] : 7 [Type: unsigned __int64]
+
+// cdb-command: dx str_slice
+// cdb-check:str_slice [...]
+// NOTE: While string slices have a .natvis entry that works in VS & VS Code, it fails in CDB 10.0.18362.1
+
+// cdb-command: dx string
+// cdb-check:string : "IAMA string!" [Type: [...]::String]
+// cdb-check: [<Raw View>] [Type: [...]::String]
+// cdb-check: [size] : 0xc [Type: [...]]
+// cdb-check: [capacity] : 0xc [Type: [...]]
+// cdb-check: [0] : 73 'I' [Type: char]
+// cdb-check: [1] : 65 'A' [Type: char]
+// cdb-check: [2] : 77 'M' [Type: char]
+// cdb-check: [3] : 65 'A' [Type: char]
+// cdb-check: [4] : 32 ' ' [Type: char]
+// cdb-check: [5] : 115 's' [Type: char]
+// cdb-check: [6] : 116 't' [Type: char]
+// cdb-check: [7] : 114 'r' [Type: char]
+// cdb-check: [8] : 105 'i' [Type: char]
+// cdb-check: [9] : 110 'n' [Type: char]
+// cdb-check: [10] : 103 'g' [Type: char]
+// cdb-check: [11] : 33 '!' [Type: char]
+
+// cdb-command: dx os_string
+// cdb-check:os_string [Type: [...]::OsString]
+// NOTE: OsString doesn't have a .natvis entry yet.
+
+// cdb-command: dx some
+// cdb-check:some : { Some 8 } [Type: [...]::Option<i16>]
+// cdb-command: dx none
+// cdb-check:none : { None } [Type: [...]::Option<i64>]
+// cdb-command: dx some_string
+// cdb-check:some_string : { Some "IAMA optional string!" } [Type: [...]::Option<[...]::String>]
+
#![allow(unused_variables)]
use std::ffi::OsString;
// lldb-command:print x
// lldb-check:[...]$0 = 5
+// === CDB TESTS ==================================================================================
+
+// cdb-command:g
+
+// cdb-command:dx x
+// cdb-check:string [...] : 5 [Type: [...]]
+
fn main() {
let x = 1;
pub use self::Mode::*;
+use std::ffi::OsString;
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
RunPass,
RunPassValgrind,
Pretty,
- DebugInfoBoth,
+ DebugInfoCdb,
+ DebugInfoGdbLldb,
DebugInfoGdb,
DebugInfoLldb,
Codegen,
pub fn disambiguator(self) -> &'static str {
// Run-pass and pretty run-pass tests could run concurrently, and if they do,
// they need to keep their output segregated. Same is true for debuginfo tests that
- // can be run both on gdb and lldb.
+ // can be run on cdb, gdb, and lldb.
match self {
Pretty => ".pretty",
+ DebugInfoCdb => ".cdb",
DebugInfoGdb => ".gdb",
DebugInfoLldb => ".lldb",
_ => "",
"run-pass" => Ok(RunPass),
"run-pass-valgrind" => Ok(RunPassValgrind),
"pretty" => Ok(Pretty),
- "debuginfo-both" => Ok(DebugInfoBoth),
+ "debuginfo-cdb" => Ok(DebugInfoCdb),
+ "debuginfo-gdb+lldb" => Ok(DebugInfoGdbLldb),
"debuginfo-lldb" => Ok(DebugInfoLldb),
"debuginfo-gdb" => Ok(DebugInfoGdb),
"codegen" => Ok(Codegen),
RunPass => "run-pass",
RunPassValgrind => "run-pass-valgrind",
Pretty => "pretty",
- DebugInfoBoth => "debuginfo-both",
+ DebugInfoCdb => "debuginfo-cdb",
+ DebugInfoGdbLldb => "debuginfo-gdb+lldb",
DebugInfoGdb => "debuginfo-gdb",
DebugInfoLldb => "debuginfo-lldb",
Codegen => "codegen",
/// Host triple for the compiler being invoked
pub host: String,
+ /// Path to / name of the Microsoft Console Debugger (CDB) executable
+ pub cdb: Option<OsString>,
+
/// Path to / name of the GDB executable
pub gdb: Option<String>,
NoMatch,
/// Match.
Match,
- /// Mode was DebugInfoBoth and this matched gdb.
+ /// Mode was DebugInfoGdbLldb and this matched gdb.
MatchGdb,
- /// Mode was DebugInfoBoth and this matched lldb.
+ /// Mode was DebugInfoGdbLldb and this matched lldb.
MatchLldb,
}
revisions: vec![],
};
- if config.mode == common::DebugInfoBoth {
+ if config.mode == common::DebugInfoGdbLldb {
if config.lldb_python_dir.is_none() {
props.ignore = props.ignore.no_lldb();
}
if config.gdb_version.is_none() {
props.ignore = props.ignore.no_gdb();
}
+ } else if config.mode == common::DebugInfoCdb {
+ if config.cdb.is_none() {
+ props.ignore = Ignore::Ignore;
+ }
}
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
}
}
- if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoBoth) &&
+ if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
props.ignore.can_run_gdb() && ignore_gdb(config, ln) {
props.ignore = props.ignore.no_gdb();
}
- if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoBoth) &&
+ if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoGdbLldb) &&
props.ignore.can_run_lldb() && ignore_lldb(config, ln) {
props.ignore = props.ignore.no_lldb();
}
ParsedNameDirective::Match
} else {
match self.mode {
- common::DebugInfoBoth => {
+ common::DebugInfoGdbLldb => {
if name == "gdb" {
ParsedNameDirective::MatchGdb
} else if name == "lldb" {
ParsedNameDirective::NoMatch
}
},
+ common::DebugInfoCdb => if name == "cdb" {
+ ParsedNameDirective::Match
+ } else {
+ ParsedNameDirective::NoMatch
+ },
common::DebugInfoGdb => if name == "gdb" {
ParsedNameDirective::Match
} else {
#![crate_name = "compiletest"]
#![feature(test)]
+#![feature(path_buf_capacity)]
#![feature(vec_remove_item)]
#![deny(warnings, rust_2018_idioms)]
use crate::common::CompareMode;
use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
use crate::common::{Config, TestPaths};
-use crate::common::{DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
+use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
use getopts::Options;
use std::env;
use std::ffi::OsString;
.optopt("", "logfile", "file to log test execution to", "FILE")
.optopt("", "target", "the target to build for", "TARGET")
.optopt("", "host", "the host to build for", "HOST")
+ .optopt(
+ "",
+ "cdb",
+ "path to CDB to use for CDB debuginfo tests",
+ "PATH",
+ )
.optopt(
"",
"gdb",
let target = opt_str2(matches.opt_str("target"));
let android_cross_path = opt_path(matches, "android-cross-path");
+ let cdb = analyze_cdb(matches.opt_str("cdb"), &target);
let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target,
&android_cross_path);
let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
target_rustcflags: matches.opt_str("target-rustcflags"),
target: target,
host: opt_str2(matches.opt_str("host")),
+ cdb,
gdb,
gdb_version,
gdb_native_rust,
pub fn run_tests(config: &Config) {
if config.target.contains("android") {
- if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoGdb || config.mode == DebugInfoGdbLldb {
println!(
"{} debug-info test uses tcp 5039 port.\
please reserve it",
match config.mode {
// Note that we don't need to emit the gdb warning when
- // DebugInfoBoth, so it is ok to list that here.
- DebugInfoBoth | DebugInfoLldb => {
+ // DebugInfoGdbLldb, so it is ok to list that here.
+ DebugInfoGdbLldb | DebugInfoLldb => {
if let Some(lldb_version) = config.lldb_version.as_ref() {
if is_blacklisted_lldb_version(&lldb_version[..]) {
println!(
return;
}
}
- _ => { /* proceed */ }
+
+ DebugInfoCdb | _ => { /* proceed */ }
}
// FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
&early_props,
revision.map(|s| s.as_str()),
)
- || ((config.mode == DebugInfoBoth ||
+ || ((config.mode == DebugInfoGdbLldb || config.mode == DebugInfoCdb ||
config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
&& config.target.contains("emscripten"))
|| (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
revision: Option<&String>,
) -> test::TestFn {
let mut config = config.clone();
- if config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoGdbLldb {
// If both gdb and lldb were ignored, then the test as a whole
// would be ignored.
if !ignore.can_run_gdb() {
}
}
+/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
+fn is_pc_windows_msvc_target(target: &String) -> bool {
+ target.ends_with("-pc-windows-msvc")
+}
+
+fn find_cdb(target: &String) -> Option<OsString> {
+ if cfg!(windows) && is_pc_windows_msvc_target(target) {
+ let pf86 = env::var_os("ProgramFiles(x86)").or(env::var_os("ProgramFiles"))?;
+ let cdb_arch = if cfg!(target_arch="x86") {
+ "x86"
+ } else if cfg!(target_arch="x86_64") {
+ "x64"
+ } else if cfg!(target_arch="aarch64") {
+ "arm64"
+ } else if cfg!(target_arch="arm") {
+ "arm"
+ } else {
+ return None; // No compatible CDB.exe in the Windows 10 SDK
+ };
+
+ let mut path = PathBuf::with_capacity(64);
+ path.push(pf86);
+ path.push(r"Windows Kits\10\Debuggers"); // We could check more known install locations (8.1?)
+ path.push(cdb_arch);
+ path.push(r"cdb.exe");
+
+ if path.exists() {
+ Some(path.into_os_string())
+ } else {
+ None
+ }
+ }
+ else {
+ None
+ }
+}
+
+/// Returns Path to CDB
+fn analyze_cdb(cdb: Option<String>, target: &String) -> Option<OsString> {
+ cdb.map(|s| OsString::from(s)).or(find_cdb(target))
+}
+
/// Returns (Path to GDB, GDB Version, GDB has Rust Support)
fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
-> (Option<String>, Option<u32>, bool) {
use crate::common::CompareMode;
use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
use crate::common::{output_base_dir, output_base_name, output_testname_unique};
-use crate::common::{Codegen, CodegenUnits, DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Rustdoc};
+use crate::common::{Codegen, CodegenUnits, DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Rustdoc};
use crate::common::{CompileFail, Pretty, RunFail, RunPass, RunPassValgrind};
use crate::common::{Config, TestPaths};
use crate::common::{Incremental, MirOpt, RunMake, Ui, JsDocTest, Assembly};
let mut hash = DefaultHasher::new();
config.stage_id.hash(&mut hash);
- if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoCdb {
+ match config.cdb {
+ None => env::var_os("ProgramFiles(x86)").hash(&mut hash),
+ Some(ref s) if s.is_empty() => env::var_os("ProgramFiles(x86)").hash(&mut hash),
+ Some(ref s) => s.hash(&mut hash),
+ }
+ }
+
+ if config.mode == DebugInfoGdb || config.mode == DebugInfoGdbLldb {
match config.gdb {
None => env::var_os("PATH").hash(&mut hash),
Some(ref s) if s.is_empty() => env::var_os("PATH").hash(&mut hash),
};
}
- if config.mode == DebugInfoLldb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoLldb || config.mode == DebugInfoGdbLldb {
env::var_os("PATH").hash(&mut hash);
env::var_os("PYTHONPATH").hash(&mut hash);
}
RunFail => self.run_rfail_test(),
RunPassValgrind => self.run_valgrind_test(),
Pretty => self.run_pretty_test(),
- DebugInfoBoth => {
+ DebugInfoGdbLldb => {
self.run_debuginfo_gdb_test();
self.run_debuginfo_lldb_test();
},
+ DebugInfoCdb => self.run_debuginfo_cdb_test(),
DebugInfoGdb => self.run_debuginfo_gdb_test(),
DebugInfoLldb => self.run_debuginfo_lldb_test(),
Codegen => self.run_codegen_test(),
self.compose_and_run_compiler(rustc, Some(src))
}
+ fn run_debuginfo_cdb_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ let config = Config {
+ target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+ host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+ mode: DebugInfoCdb,
+ ..self.config.clone()
+ };
+
+ let test_cx = TestCx {
+ config: &config,
+ ..*self
+ };
+
+ test_cx.run_debuginfo_cdb_test_no_opt();
+ }
+
+ fn run_debuginfo_cdb_test_no_opt(&self) {
+ // compile test file (it should have 'compile-flags:-g' in the header)
+ let compile_result = self.compile_test();
+ if !compile_result.status.success() {
+ self.fatal_proc_rec("compilation failed!", &compile_result);
+ }
+
+ let exe_file = self.make_exe_name();
+
+ let prefixes = {
+ static PREFIXES: &'static [&'static str] = &["cdb", "cdbg"];
+ // No "native rust support" variation for CDB yet.
+ PREFIXES
+ };
+
+ // Parse debugger commands etc from test files
+ let DebuggerCommands {
+ commands,
+ check_lines,
+ breakpoint_lines,
+ ..
+ } = self.parse_debugger_commands(prefixes);
+
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
+ let mut script_str = String::with_capacity(2048);
+ script_str.push_str("version\n"); // List CDB (and more) version info in test output
+ script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files (bulk of MSVC debugger customizations)
+
+ // Set breakpoints on every line that contains the string "#break"
+ let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
+ for line in &breakpoint_lines {
+ script_str.push_str(&format!(
+ "bp `{}:{}`\n",
+ source_file_name, line
+ ));
+ }
+
+ // Append the other `cdb-command:`s
+ for line in &commands {
+ script_str.push_str(line);
+ script_str.push_str("\n");
+ }
+
+ script_str.push_str("\nqq\n"); // Quit the debugger (including remote debugger, if any)
+
+ // Write the script into a file
+ debug!("script_str = {}", script_str);
+ self.dump_output_file(&script_str, "debugger.script");
+ let debugger_script = self.make_out_name("debugger.script");
+
+ let cdb_path = &self.config.cdb.as_ref().unwrap();
+ let mut cdb = Command::new(cdb_path);
+ cdb
+ .arg("-lines") // Enable source line debugging.
+ .arg("-cf").arg(&debugger_script)
+ .arg(&exe_file);
+
+ let debugger_run_result = self.compose_and_run(
+ cdb,
+ self.config.run_lib_path.to_str().unwrap(),
+ None, // aux_path
+ None // input
+ );
+
+ if !debugger_run_result.status.success() {
+ self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
+ }
+
+ self.check_debugger_output(&debugger_run_result, &check_lines);
+ }
+
fn run_debuginfo_gdb_test(&self) {
assert!(self.revision.is_none(), "revisions not relevant here");
RunPass | Ui => self.should_run_successfully(),
Incremental => self.revision.unwrap().starts_with("r"),
RunFail | RunPassValgrind | MirOpt |
- DebugInfoBoth | DebugInfoGdb | DebugInfoLldb => true,
+ DebugInfoCdb | DebugInfoGdbLldb | DebugInfoGdb | DebugInfoLldb => true,
_ => false,
};
let output_file = if will_execute {
rustc.arg(dir_opt);
}
- RunFail | RunPassValgrind | Pretty | DebugInfoBoth | DebugInfoGdb | DebugInfoLldb
+ RunFail | RunPassValgrind | Pretty | DebugInfoCdb | DebugInfoGdbLldb | DebugInfoGdb | DebugInfoLldb
| Codegen | Rustdoc | RunMake | CodegenUnits | JsDocTest | Assembly => {
// do not use JSON output
}