Skip to content

Commit

Permalink
Merge branch 'thread_support' fixes #21
Browse files Browse the repository at this point in the history
  • Loading branch information
xd009642 committed Aug 27, 2017
2 parents d476999 + 3ac5f3a commit ecf97f9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-tarpaulin"
version = "0.4.0"
version = "0.4.1"
authors = ["Daniel McKenna <danielmckenna93@gmail.com>"]
description = "Cargo-Tarpaulin is a tool to determine code coverage achieved via tests"
repository = "https://github.com/xd009642/tarpaulin"
Expand Down
71 changes: 38 additions & 33 deletions src/breakpoint.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use nix::libc::{pid_t, c_long};
use nix::Result;
use ptrace_control::*;
Expand All @@ -11,10 +12,6 @@ const INT: u64 = 0xCC;
/// breakpoint implementations.
#[derive(Debug)]
pub struct Breakpoint {
/// Current process id
pub pid: pid_t,
/// process or thread id to use at present time
uid: pid_t,
/// Program counter
pub pc: u64,
/// Bottom byte of address data.
Expand All @@ -23,8 +20,8 @@ pub struct Breakpoint {
/// Reading from memory with ptrace gives addresses aligned to bytes.
/// We therefore need to know the shift to place the breakpoint in the right place
shift: u64,
/// When execution pauses.
is_running: bool
/// Map of the state of the breakpoint on each thread/process
is_running: HashMap<pid_t, bool>
}

impl Breakpoint {
Expand All @@ -34,65 +31,73 @@ impl Breakpoint {
let data = read_address(pid, aligned)?;
let shift = 8 * (pc - aligned);
let data = ((data >> shift) & 0xFF) as u8;

let mut b = Breakpoint{
pid: pid,
uid: pid,
pc: pc,
data: data,
shift: shift,
is_running: true,
is_running: HashMap::new(),
};
match b.enable() {
match b.enable(pid) {
Ok(_) => Ok(b),
Err(e) => Err(e)
}
}

/// Attaches the current breakpoint.
pub fn enable(&mut self) -> Result<c_long> {
let data = read_address(self.uid, self.aligned_address())?;
self.is_running = true;
pub fn enable(&mut self, pid: pid_t) -> Result<c_long> {
let data = read_address(pid, self.aligned_address())?;
self.is_running.insert(pid, true);
let mut intdata = data & (!(0xFFu64 << self.shift) as i64);
intdata |= (INT << self.shift) as i64;
write_to_address(self.uid, self.aligned_address(), intdata)
write_to_address(pid, self.aligned_address(), intdata)
}

fn disable(&self) -> Result<c_long> {
fn disable(&self, pid: pid_t) -> Result<c_long> {
// I require the bit fiddlin this end.
let data = read_address(self.uid, self.aligned_address())?;
let data = read_address(pid, self.aligned_address())?;
let mut orgdata = data & (!(0xFFu64 << self.shift) as i64);
orgdata |= (self.data as i64) << self.shift;
write_to_address(self.uid, self.aligned_address(), orgdata)
write_to_address(pid, self.aligned_address(), orgdata)
}

/// Processes the breakpoint. This steps over the breakpoint
pub fn process(&mut self, tid: Option<pid_t>) -> Result<bool> {
if let Some(tid) = tid {
self.uid = tid;
}
if self.is_running {
self.step()?;
self.is_running = false;
self.uid = self.pid;
pub fn process(&mut self, pid: pid_t, reenable: bool) -> Result<bool> {
let is_running = match self.is_running.get(&pid) {
Some(r) => *r,
None => true,
};
if is_running {
self.enable(pid)?;
self.step(pid)?;
self.is_running.insert(pid, false);
Ok(true)
} else {
self.enable()?;
continue_exec(self.uid, None)?;
self.is_running = true;
self.uid = self.pid;
self.disable(pid)?;
if reenable {
self.enable(pid)?;
}
continue_exec(pid, None)?;
self.is_running.insert(pid, true);
Ok(false)
}
}

/// Call this when a ptrace thread is killed. Won't reenable the breakpoint
/// so may lose the ability to instrument this line.
pub fn thread_killed(&mut self, pid: pid_t) {
self.is_running.remove(&pid);
}

/// Steps past the current breakpoint.
/// For more advanced coverage may interrogate the variables of a branch.
fn step(&mut self) -> Result<c_long> {
fn step(&mut self, pid: pid_t) -> Result<c_long> {
// Remove the breakpoint, reset the program counter to step before it
// hit the breakpoint then step to execute the original instruction.
self.disable()?;
self.disable(pid)?;
// Need to set the program counter back one.
set_instruction_pointer(self.uid, self.pc)?;
single_step(self.uid)
set_instruction_pointer(pid, self.pc)?;
single_step(pid)
}

fn aligned_address(&self) -> u64 {
Expand Down
24 changes: 21 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ use tracer::*;
use breakpoint::*;
use ptrace_control::*;


const PIE_ERROR: &'static str = "ERROR: Tarpaulin cannot find code addresses check that \
pie is disabled for your linker. If linking with gcc try adding -C link-args=-no-pie \
to your rust flags";
Expand Down Expand Up @@ -315,11 +314,17 @@ fn run_function(pid: pid_t,
mut traces: &mut Vec<TracerData>,
mut breakpoints: &mut HashMap<u64, Breakpoint>) -> Result<i8, Error> {
let mut res = 0i8;
// Thread count, don't count initial thread of execution
let mut thread_count = 0isize;
let mut unwarned = true;
// Start the function running.
continue_exec(pid, None)?;
loop {
match waitpid(-1, Some(__WALL)) {
Ok(WaitStatus::Exited(child, sig)) => {
for (_, ref mut value) in breakpoints.iter_mut() {
value.thread_killed(child);
}
res = sig;
// If test executable exiting break, else continue the program
// to launch the next test function
Expand All @@ -335,7 +340,13 @@ fn run_function(pid: pid_t,
let rip = (rip - 1) as u64;
if breakpoints.contains_key(&rip) {
let bp = &mut breakpoints.get_mut(&rip).unwrap();
let updated = if let Ok(x) = bp.process(Some(child)) {
let enable = thread_count < 2;
if !enable && unwarned {
println!("Code is mulithreaded, disabling hit count");
unwarned = false;
}
// Don't reenable if multithreaded as can't yet sort out segfault issue
let updated = if let Ok(x) = bp.process(child, enable) {
x
} else {
rip == end
Expand All @@ -354,6 +365,9 @@ fn run_function(pid: pid_t,
Ok(WaitStatus::Stopped(child, signal::SIGSTOP)) => {
continue_exec(child, None)?;
},
Ok(WaitStatus::Stopped(_, signal::SIGSEGV)) => {
break;
},
Ok(WaitStatus::Stopped(child, sig)) => {
let s = if forward_signals {
Some(sig)
Expand All @@ -364,20 +378,24 @@ fn run_function(pid: pid_t,
},
Ok(WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_CLONE)) => {
if get_event_data(child).is_ok() {
thread_count += 1;
continue_exec(child, None)?;
}
},
Ok(WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK)) => {
continue_exec(child, None)?;
},
Ok(WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK)) => {
continue_exec(child, None)?;
},
Ok(WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC)) => {
detach_child(child)?;
},
Ok(WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT)) => {
thread_count -= 1;
continue_exec(child, None)?;
},
Ok(WaitStatus::Signaled(child, signal::SIGTRAP, true)) => {
println!("unexpected SIGTRAP attempting to continue");
continue_exec(child, None)?;
},
Ok(s) => {
Expand Down
2 changes: 0 additions & 2 deletions src/ptrace_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use nix::libc::{pid_t, c_void, c_long};
use nix::Result;
use std::mem;


const RIP: u8 = 128;


Expand Down Expand Up @@ -67,4 +66,3 @@ pub fn get_event_data(pid: pid_t) -> Result<c_long> {
}
}


0 comments on commit ecf97f9

Please sign in to comment.