Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

llvm-cov support pt1 #653

Merged
merged 10 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ target/
*.log
*.profdata
*.profraw
*.profdata
# Generated by IntelliJ IDEs
.idea
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@
From 2019 onwards, all notable changes to tarpaulin will be documented in this
file.

## [Unreleased]
### Added
- Check if user sets -Cdebuginfo and remove it #601
- INTERNAL Added ability to build with LLVM coverage instrumentation and detect
compiler support. This isn't enabled so should have no effect it's just the
start of the support work.

### Changed
- Make doctest prefix matching less specific as the naming convention changed again

### Removed

## [0.18.0-alpha1] 2021-02-14
### Added
- Added `--color` option matching cargo arg
- `--follow-exec` option making exec tracing non-default
- `--jobs` option matching the one in cargo test
- Check if user sets -Cdebuginfo and remove it #601

### Changed
- Check through memory map for the first entry belonging to the executable [FIX]
Expand All @@ -21,7 +32,6 @@ file.
- Hidden file filtering only applied for folders inside project directory not
any folder on path. Fixes #682
- Removed unimplemented `toml` report
- Make doctest prefix matching less specific as the naming convention changed again

### Removed

Expand Down
97 changes: 89 additions & 8 deletions src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,29 @@ use std::process::{Command, Stdio};
use tracing::{error, trace, warn};
use walkdir::{DirEntry, WalkDir};

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
enum Channel {
Stable,
Beta,
Nightly,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct CargoVersionInfo {
major: usize,
minor: usize,
channel: Channel,
year: usize,
month: usize,
day: usize,
}

impl CargoVersionInfo {
fn supports_llvm_cov(&self) -> bool {
self.minor >= 50 && self.channel == Channel::Nightly
}
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct TestBinary {
path: PathBuf,
Expand Down Expand Up @@ -108,9 +122,10 @@ impl DocTestBinaryMeta {

lazy_static! {
static ref CARGO_VERSION_INFO: Option<CargoVersionInfo> = {
let version_info =
Regex::new(r"cargo (\d)\.(\d+)\.\d+\-nightly \([[:alnum:]]+ (\d{4})-(\d{2})-(\d{2})\)")
.unwrap();
let version_info = Regex::new(
r"cargo (\d)\.(\d+)\.\d+([\-betanightly]*) \([[:alnum:]]+ (\d{4})-(\d{2})-(\d{2})\)",
)
.unwrap();
Command::new("cargo")
.arg("--version")
.output()
Expand All @@ -119,12 +134,20 @@ lazy_static! {
if let Some(cap) = version_info.captures(&s) {
let major = cap[1].parse().unwrap();
let minor = cap[2].parse().unwrap();
let year = cap[3].parse().unwrap();
let month = cap[4].parse().unwrap();
let day = cap[5].parse().unwrap();
// We expect a string like `cargo 1.50.0-nightly (a0f433460 2020-02-01)
// the version number either has `-nightly` `-beta` or empty for stable
let channel = match &cap[3] {
"-nightly" => Channel::Nightly,
"-beta" => Channel::Beta,
_ => Channel::Stable,
};
let year = cap[4].parse().unwrap();
let month = cap[5].parse().unwrap();
let day = cap[6].parse().unwrap();
Some(CargoVersionInfo {
major,
minor,
channel,
year,
month,
day,
Expand Down Expand Up @@ -493,6 +516,16 @@ fn clean_doctest_folder<P: AsRef<Path>>(doctest_dir: P) {
}
}

fn handle_llvm_flags(value: &mut String, config: &Config) {
if (config.engine == TraceEngine::Auto || config.engine == TraceEngine::Llvm)
&& supports_llvm_coverage()
{
value.push_str("-Z instrument-coverage ");
} else if config.engine == TraceEngine::Llvm {
error!("unable to utilise llvm coverage, due to compiler support. Falling back to Ptrace");
}
}

pub fn rustdoc_flags(config: &Config) -> String {
const RUSTDOC: &str = "RUSTDOCFLAGS";
let common_opts = " -C link-dead-code -C debuginfo=2 --cfg=tarpaulin ";
Expand All @@ -506,18 +539,20 @@ pub fn rustdoc_flags(config: &Config) -> String {
value.push_str(vtemp.as_ref());
}
}
handle_llvm_flags(&mut value, config);
value
}

pub fn rust_flags(config: &Config) -> String {
const RUSTFLAGS: &str = "RUSTFLAGS";
let mut value = " -C link-dead-code -C debuginfo=2 ".to_string();
if !config.avoid_cfg_tarpaulin {
value = format!("{}--cfg=tarpaulin ", value);
value.push_str("--cfg=tarpaulin ");
}
if config.release {
value = format!("{}-C debug-assertions=off ", value);
value.push_str("-C debug-assertions=off ");
}
handle_llvm_flags(&mut value, config);
if let Ok(vtemp) = env::var(RUSTFLAGS) {
lazy_static! {
static ref DEBUG_INFO: Regex = Regex::new(r#"\-C\s*debuginfo=\d"#).unwrap();
Expand All @@ -538,3 +573,49 @@ fn setup_environment(cmd: &mut Command, config: &Config) {
trace!("Setting RUSTDOCFLAGS='{}'", value);
cmd.env(rustdoc, value);
}

fn supports_llvm_coverage() -> bool {
if let Some(version) = CARGO_VERSION_INFO.as_ref() {
version.supports_llvm_cov()
} else {
false
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn llvm_cov_compatible_version() {
let version = CargoVersionInfo {
major: 1,
minor: 50,
channel: Channel::Nightly,
year: 2020,
month: 12,
day: 22,
};
assert!(version.supports_llvm_cov());
}

#[test]
fn llvm_cov_incompatible_version() {
let mut version = CargoVersionInfo {
major: 1,
minor: 48,
channel: Channel::Stable,
year: 2020,
month: 10,
day: 14,
};
assert!(!version.supports_llvm_cov());
version.channel = Channel::Beta;
assert!(!version.supports_llvm_cov());
version.minor = 50;
assert!(!version.supports_llvm_cov());
version.minor = 58;
version.channel = Channel::Stable;
assert!(!version.supports_llvm_cov());
}
}
4 changes: 4 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ pub struct Config {
pub follow_exec: bool,
/// Number of jobs used for building the tests
pub jobs: Option<usize>,
/// Engine to use to collect coverage
pub engine: TraceEngine,
}

fn default_test_timeout() -> Duration {
Expand Down Expand Up @@ -227,6 +229,7 @@ impl Default for Config {
avoid_cfg_tarpaulin: false,
jobs: None,
color: Color::Auto,
engine: TraceEngine::Ptrace,
}
}
}
Expand Down Expand Up @@ -300,6 +303,7 @@ impl<'a> From<&'a ArgMatches<'a>> for ConfigWrapper {
profile: get_profile(args),
metadata: RefCell::new(None),
avoid_cfg_tarpaulin: args.is_present("avoid-cfg-tarpaulin"),
engine: TraceEngine::Ptrace,
};
if args.is_present("ignore-config") {
Self(vec![args_config])
Expand Down
9 changes: 9 additions & 0 deletions src/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ arg_enum! {
}
}

arg_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum TraceEngine {
Auto,
Ptrace,
Llvm,
}
}

arg_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub enum Mode {
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ fn execute_test(test: &TestBinary, ignored: bool, config: &Config) -> Result<(),
envars
.push(CString::new(format!("CARGO_MANIFEST_DIR={}", s.display())).unwrap_or_default());
}
if config.engine == TraceEngine::Llvm || config.engine == TraceEngine::Auto {
// Used for llvm coverage to avoid report naming clashes
envars.push(CString::new("LLVM_PROFILE_FILE=default_%p.profraw").unwrap_or_default());
}

execute(exec_path, &argv, envars.as_slice())
}