diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 36175c8dd2..ffba09d32f 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -172,7 +172,7 @@ impl Command { Command::Install { flags } => Self::install(flags), Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), - Command::Test { bless, flags, target } => Self::test(bless, flags, target), + Command::Test { bless, flags, target, coverage } => Self::test(bless, flags, target, coverage), Command::Run { dep, verbose, many_seeds, target, edition, flags } => Self::run(dep, verbose, many_seeds, target, edition, flags), Command::Doc { flags } => Self::doc(flags), @@ -458,7 +458,7 @@ impl Command { Ok(()) } - fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { + fn test(bless: bool, mut flags: Vec, target: Option, coverage: bool) -> Result<()> { let mut e = MiriEnv::new()?; // Prepare a sysroot. (Also builds cargo-miri, which we need.) @@ -468,6 +468,9 @@ impl Command { if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + if coverage { + e.sh.set_var("RUSTFLAGS", "-C instrument-coverage"); + } if let Some(target) = target { // Tell the harness which target to test. e.sh.set_var("MIRI_TEST_TARGET", target); @@ -479,6 +482,41 @@ impl Command { // Then test, and let caller control flags. // Only in root project as `cargo-miri` has no tests. e.test(".", &flags)?; + + if coverage { + Self::show_coverage_report(&e)?; + } + Ok(()) + } + + fn show_coverage_report(e: &MiriEnv) -> Result<()> { + let profraw_files: Vec<_> = std::fs::read_dir(".")? + .filter_map(|r| r.ok()) + .map(|e| e.path()) + .filter(|p| p.extension().map(|e| e == "profraw").unwrap_or(false)) + .map(|p| p.as_os_str().to_os_string()) + .collect(); + + // Merge the profraw files + let profraw_files_cloned= profraw_files.iter(); + cmd!( + e.sh, + "cargo-profdata -- merge -sparse {profraw_files_cloned...} -o merged.profdata" + ).quiet().run()?; + + // Create the coverage report. + let home = std::env::var("HOME")?; + let ignored = format!("{home}/*|miri/target/*|target/debug/*|rust/deps/*"); + cmd!( + e.sh, + "cargo-cov -- report --instr-profile=merged.profdata --object target/debug/miri -ignore-filename-regex={ignored}" + ).run()?; + + // Delete artifacts. + cmd!( + e.sh, + "rm {profraw_files...} merged.profdata" + ).quiet().run()?; Ok(()) } diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index 0620f3aaf0..ed41ed85aa 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -34,6 +34,8 @@ pub enum Command { /// The cross-interpretation target. /// If none then the host is the target. target: Option, + /// Produce coverage report if set. + coverage: bool, /// Flags that are passed through to the test harness. flags: Vec, }, @@ -158,9 +160,12 @@ fn main() -> Result<()> { let mut target = None; let mut bless = false; let mut flags = Vec::new(); + let mut coverage = false; loop { if args.get_long_flag("bless")? { bless = true; + } else if args.get_long_flag("coverage")? { + coverage = true; } else if let Some(val) = args.get_long_opt("target")? { target = Some(val); } else if let Some(flag) = args.get_other() { @@ -169,7 +174,7 @@ fn main() -> Result<()> { break; } } - Command::Test { bless, flags, target } + Command::Test { bless, flags, target, coverage } } Some("run") => { let mut dep = false;