Skip to content

Commit

Permalink
Feature/fix cross compilation (#698)
Browse files Browse the repository at this point in the history
* Start working on making cross compilation work

* Made parts of the event logger that took linuxy types cfg(unix)
* Make ptrace_control cfg(unix)
* Make nix errors cfg(unix)
* Make libc, nix and procfs unix only dependencies

* Reshuffle and move to target_os instead of unix

* The unix feature would lead to things being compiled on macos and BSDs
that doesn't work so moved to more specific target_os = linux.
* Moved breakpoint and ptrace to the process_handling module
* Move some Pid usage to a ProcessHandle type

* Add windows and mac to CI

* Make it look like CI passes

Why doesn't github actions have allow-failures as a tag smh

* Get tarpaulin compiling on windows

* Added a bunch of stub functions that currently do nothing.
* So many warnings

* Hopefully fix apple compilation.

Remove the old apple executing code. It has nae function and we're going
llvm for apple anyway instead of fucking around with it's stupid system
APIs.

* Rearrange further

I didn't like calling into process_handling module, then back into
things in the root then back into process_handling. So a bit of a tidy
up.

More tidying up will be needed at some point, however I see a lot of
this changing as I add in the ability to switch between engines so I'm
okay with leaving it in this state

* fix multiple imports

* Add a dummy step so I can see the logging

* remove dummy

* Update changelog
  • Loading branch information
xd009642 authored Feb 26, 2021
1 parent b2611b8 commit 7d5f37f
Show file tree
Hide file tree
Showing 15 changed files with 300 additions and 202 deletions.
48 changes: 46 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
pull_request:

jobs:
build:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -31,7 +31,7 @@ jobs:
- name: clean
run: cargo clean
- name: build
run: cargo build --verbose
run: cargo build
env:
RUST_BACKTRACE: 1
- name: test
Expand All @@ -52,3 +52,47 @@ jobs:
--data '{"build": true}' \
https://registry.hub.docker.com/u/xd009642/tarpaulin/trigger/${{ secrets.DOCKER_TOKEN }}/
if: github.ref == 'ref/heads/master' || github.ref == 'refs/heads/develop'
windows:
runs-on: windows-latest
strategy:
matrix:
version:
- nightly
target:
- x86_64-pc-windows-gnu
- x86_64-pc-windows-msvc
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.version }}
override: true
- name: build
run: cargo build
- name: test
run: cargo test -- --test-threads 1 || true
env:
RUST_BACKTRACE: 1
mac:
runs-on: macos-latest
strategy:
matrix:
version:
- nightly
target:
- x86_64-pc-windows-gnu
- x86_64-pc-windows-msvc
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.version }}
override: true
- name: build
run: cargo build
- name: test
run: cargo test -- --test-threads 1 || true
env:
RUST_BACKTRACE: 1
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ start of the support work.
### Changed
- Make doctest prefix matching less specific as the naming convention changed again
- Ensure report is always generated if coverage is below failure threshold
- Rearrange crate internals and enable cross compilation for windows and macos.
This doesn't allow tarpaulin to work on these Operating Systems but it will
print an error and exit instead of failing to build

### Removed

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,10 @@ git2 = "0.13"
humantime-serde = "1"
indexmap = { version = "1.6.1", features = ["serde-1"] }
lazy_static = "1.0"
libc = "0.2.86"
tracing = { version = "0.1", default-features = false }
tracing-subscriber = {version = "0.2.16", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec", "tracing-log"]}
memmap = "0.7.0"
nix = "0.20.0"
object = "0.23"
procfs = "0.9"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
quick-xml = "0.22"
quote = "1.0"
Expand All @@ -47,6 +44,12 @@ serde_json = "1.0"
syn = { version = "1.0", features = ["full"]}
toml = "0.5"
walkdir = "2.3.1"
cfg-if = "1.0.0"

[target.'cfg(target_os = "linux")'.dependencies]
libc = "0.2.86"
nix = "0.20.0"
procfs = "0.9"

[features]
default = []
Expand Down
4 changes: 4 additions & 0 deletions src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::report::cobertura;
use std::fmt::{self, Display, Formatter};

/// Error states that could be returned from tarpaulin
#[derive(Debug)]
pub enum RunError {
Expand All @@ -23,6 +24,7 @@ pub enum RunError {
OutFormat(String),
IO(std::io::Error),
StateMachine(String),
#[cfg(target_os = "linux")]
NixError(nix::Error),
Html(String),
XML(cobertura::Error),
Expand Down Expand Up @@ -50,6 +52,7 @@ impl Display for RunError {
Self::OutFormat(e) => write!(f, "{}", e),
Self::IO(e) => write!(f, "{}", e),
Self::StateMachine(e) => write!(f, "Error running test: {}", e),
#[cfg(target_os = "linux")]
Self::NixError(e) => write!(f, "{}", e),
Self::Html(e) => write!(f, "Failed to generate HTML report! Error: {}", e),
Self::XML(e) => write!(f, "Failed to generate XML report! Error: {}", e),
Expand All @@ -69,6 +72,7 @@ impl From<std::io::Error> for RunError {
}
}

#[cfg(target_os = "linux")]
impl From<nix::Error> for RunError {
fn from(e: nix::Error) -> Self {
RunError::NixError(e)
Expand Down
25 changes: 16 additions & 9 deletions src/event_log.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::cargo::TestBinary;
#[cfg(target_os = "linux")]
use crate::ptrace_control::*;
use crate::statemachine::{ProcessInfo, TracerAction};
#[cfg(target_os = "linux")]
use crate::statemachine::ProcessInfo;
use crate::statemachine::TracerAction;
use crate::traces::{Location, TraceMap};
use chrono::offset::Local;
#[cfg(target_os = "linux")]
use nix::libc::*;
#[cfg(target_os = "linux")]
use nix::sys::{signal::Signal, wait::WaitStatus};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
Expand All @@ -20,8 +25,8 @@ pub enum Event {

#[derive(Clone, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct TraceEvent {
pid: Option<pid_t>,
child: Option<pid_t>,
pid: Option<i64>,
child: Option<i64>,
signal: Option<String>,
addr: Option<u64>,
return_val: Option<i64>,
Expand All @@ -30,27 +35,28 @@ pub struct TraceEvent {
}

impl TraceEvent {
#[cfg(target_os = "linux")]
pub(crate) fn new_from_action(action: &TracerAction<ProcessInfo>) -> Self {
match *action {
TracerAction::TryContinue(t) => TraceEvent {
pid: Some(t.pid.as_raw()),
pid: Some(t.pid.as_raw().into()),
signal: t.signal.map(|x| x.to_string()),
description: "Try continue child".to_string(),
..Default::default()
},
TracerAction::Continue(t) => TraceEvent {
pid: Some(t.pid.as_raw()),
pid: Some(t.pid.as_raw().into()),
signal: t.signal.map(|x| x.to_string()),
description: "Continue child".to_string(),
..Default::default()
},
TracerAction::Step(t) => TraceEvent {
pid: Some(t.pid.as_raw()),
pid: Some(t.pid.as_raw().into()),
description: "Step child".to_string(),
..Default::default()
},
TracerAction::Detach(t) => TraceEvent {
pid: Some(t.pid.as_raw()),
pid: Some(t.pid.as_raw().into()),
description: "Detach child".to_string(),
..Default::default()
},
Expand All @@ -61,8 +67,9 @@ impl TraceEvent {
}
}

#[cfg(target_os = "linux")]
pub(crate) fn new_from_wait(wait: &WaitStatus, offset: u64, traces: &TraceMap) -> Self {
let pid = wait.pid().map(|p| p.as_raw());
let pid = wait.pid().map(|p| p.as_raw().into());
let mut event = TraceEvent {
pid,
..Default::default()
Expand Down Expand Up @@ -94,7 +101,7 @@ impl TraceEvent {
PTRACE_EVENT_CLONE => {
event.description = "Ptrace Clone".to_string();
if *sig == Signal::SIGTRAP {
event.child = get_event_data(*pid).ok().map(|x| x as pid_t);
event.child = get_event_data(*pid).ok();
}
}
PTRACE_EVENT_FORK => {
Expand Down
131 changes: 1 addition & 130 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,13 @@ use crate::path_utils::*;
use crate::process_handling::*;
use crate::report::report_coverage;
use crate::source_analysis::{LineAnalysis, SourceAnalysis};
use crate::statemachine::*;
use crate::test_loader::*;
use crate::traces::*;
use nix::unistd::*;
use std::collections::HashMap;
use std::env;
use std::ffi::CString;
use std::fs::create_dir_all;
use std::path::{Path, PathBuf};
use tracing::{debug, error, info, trace_span, warn};
use tracing::{debug, error, info, warn};
use tracing_subscriber::{filter::LevelFilter, EnvFilter};

pub mod branching;
pub mod breakpoint;
pub mod cargo;
pub mod config;
pub mod errors;
Expand All @@ -32,8 +25,6 @@ mod statemachine;
pub mod test_loader;
pub mod traces;

mod ptrace_control;

const RUST_LOG_ENV: &str = "RUST_LOG";

pub fn setup_logging(color: Color, debug: bool, verbose: bool) {
Expand Down Expand Up @@ -227,123 +218,3 @@ pub fn launch_tarpaulin(
}
Ok((result, return_code))
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
test: &TestBinary,
analysis: &HashMap<PathBuf, LineAnalysis>,
config: &Config,
ignored: bool,
logger: &Option<EventLog>,
) -> Result<Option<(TraceMap, i32)>, RunError> {
if !test.path().exists() {
return Ok(None);
}
if let Err(e) = limit_affinity() {
warn!("Failed to set processor affinity {}", e);
}
if let Some(log) = logger.as_ref() {
log.push_binary(test.clone());
}
unsafe {
match fork() {
Ok(ForkResult::Parent { child }) => {
match collect_coverage(test.path(), child, analysis, config, logger) {
Ok(t) => Ok(Some(t)),
Err(e) => Err(RunError::TestCoverage(e.to_string())),
}
}
Ok(ForkResult::Child) => {
info!("Launching test");
execute_test(test, ignored, config)?;
Ok(None)
}
Err(err) => Err(RunError::TestCoverage(format!(
"Failed to run test {}, Error: {}",
test.path().display(),
err.to_string()
))),
}
}
}

/// Collects the coverage data from the launched test
fn collect_coverage(
test_path: &Path,
test: Pid,
analysis: &HashMap<PathBuf, LineAnalysis>,
config: &Config,
logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
let mut ret_code = 0;
let mut traces = generate_tracemap(test_path, analysis, config)?;
{
let span = trace_span!("Collect coverage", pid=%test);
let _enter = span.enter();
let (mut state, mut data) =
create_state_machine(test, &mut traces, analysis, config, logger);
loop {
state = state.step(&mut data, config)?;
if state.is_finished() {
if let TestState::End(i) = state {
ret_code = i;
}
break;
}
}
}
Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(test: &TestBinary, ignored: bool, config: &Config) -> Result<(), RunError> {
let exec_path = CString::new(test.path().to_str().unwrap()).unwrap();
info!("running {}", test.path().display());
let _ = match test.manifest_dir() {
Some(md) => env::set_current_dir(&md),
None => env::set_current_dir(&config.root()),
};

let mut envars: Vec<CString> = Vec::new();

for (key, value) in env::vars() {
let mut temp = String::new();
temp.push_str(key.as_str());
temp.push('=');
temp.push_str(value.as_str());
envars.push(CString::new(temp).unwrap());
}
let mut argv = if ignored {
vec![exec_path.clone(), CString::new("--ignored").unwrap()]
} else {
vec![exec_path.clone()]
};
if config.verbose {
envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
}
for s in &config.varargs {
argv.push(CString::new(s.as_bytes()).unwrap_or_default());
}
argv.push(CString::new("--color").unwrap_or_default());
argv.push(CString::new(config.color.to_string().to_ascii_lowercase()).unwrap_or_default());

if let Some(s) = test.pkg_name() {
envars.push(CString::new(format!("CARGO_PKG_NAME={}", s)).unwrap_or_default());
}
if let Some(s) = test.pkg_version() {
envars.push(CString::new(format!("CARGO_PKG_VERSION={}", s)).unwrap_or_default());
}
if let Some(s) = test.pkg_authors() {
envars.push(CString::new(format!("CARGO_PKG_AUTHORS={}", s.join(":"))).unwrap_or_default());
}
if let Some(s) = test.manifest_dir() {
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())
}
File renamed without changes.
Loading

0 comments on commit 7d5f37f

Please sign in to comment.