Skip to content

Commit

Permalink
Add option for generating coverage reports
Browse files Browse the repository at this point in the history
Add a `--coverage` option in the `test` subcommand of the miri script.
This option, when set, will generate a coverage report after running the
tests.

`cargo-binutils` is needed as a dependency to generate the reports.
  • Loading branch information
Mandragorian committed Oct 12, 2024
1 parent 9045930 commit 8f36706
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 3 deletions.
3 changes: 3 additions & 0 deletions ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,6 @@ case $HOST_TARGET in
exit 1
;;
esac

## Smoke test for coverage reports
./miri test --coverage do_not_match_a_test
72 changes: 70 additions & 2 deletions miri-script/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ 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),
Expand Down Expand Up @@ -458,16 +459,28 @@ impl Command {
Ok(())
}

fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
fn test(
bless: bool,
mut flags: Vec<String>,
target: Option<String>,
coverage: bool,
) -> Result<()> {
let mut e = MiriEnv::new()?;

if coverage {
let rustflags = e.sh.var("RUSTFLAGS")?;
let rustflags = format!("{rustflags} -C instrument-coverage");
e.sh.set_var("RUSTFLAGS", rustflags);
}

// Prepare a sysroot. (Also builds cargo-miri, which we need.)
e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;

// Forward information to test harness.
if bless {
e.sh.set_var("RUSTC_BLESS", "Gesundheit");
}

if let Some(target) = target {
// Tell the harness which target to test.
e.sh.set_var("MIRI_TEST_TARGET", target);
Expand All @@ -479,9 +492,64 @@ 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<_> = Self::profraw_files(".")?;

let sysroot = cmd!(e.sh, "rustc --print sysroot").read()?;

let rustlib = Self::rustlib(sysroot)?;
let mut profdata_bin = rustlib.clone();
profdata_bin.push("llvm-profdata");

// Merge the profraw files
let profraw_files_cloned = profraw_files.iter();
cmd!(e.sh, "{profdata_bin} merge -sparse {profraw_files_cloned...} -o merged.profdata")
.quiet()
.run()?;

// Create the coverage report.
let miri_dir = e.miri_dir.as_os_str();
let mut cov_bin = rustlib.clone();
cov_bin.push("llvm-cov");
let suffix = if cfg!(windows) { ".exe" } else { "" };
cmd!(
e.sh,
"{cov_bin} report --instr-profile=merged.profdata --object {miri_dir}/target/debug/miri{suffix} --sources src/"
).quiet().run()?;

// Delete artifacts.
let cargo_miri_profraw_files = Self::profraw_files("cargo-miri")?;
cmd!(e.sh, "rm {profraw_files...} {cargo_miri_profraw_files...} merged.profdata")
.quiet()
.run()?;
Ok(())
}

fn rustlib(sysroot: String) -> Result<PathBuf> {
let mut pathbuf = PathBuf::from(sysroot);
pathbuf.push("lib");
pathbuf.push("rustlib");
pathbuf.push(rustc_version::version_meta()?.host);
pathbuf.push("bin");
Ok(pathbuf)
}

fn profraw_files(path: &str) -> Result<Vec<OsString>> {
Ok(std::fs::read_dir(path)?
.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())
}

fn run(
dep: bool,
verbose: bool,
Expand Down
7 changes: 6 additions & 1 deletion miri-script/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub enum Command {
/// The cross-interpretation target.
/// If none then the host is the target.
target: Option<String>,
/// Produce coverage report if set.
coverage: bool,
/// Flags that are passed through to the test harness.
flags: Vec<String>,
},
Expand Down Expand Up @@ -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() {
Expand All @@ -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;
Expand Down

0 comments on commit 8f36706

Please sign in to comment.