Skip to content

Commit

Permalink
Get thread activity from the OS
Browse files Browse the repository at this point in the history
Rather than use a heuristic to figure out if a thread is active, query the OS
to see if the thread is running. This should provide a more accurate view on
what's actually happening.

The tricky part here is matching the OS thread id to the python thread id,
which we were already doing for the native code unwinding. This refactors to
allow getting the native thread id even when not getting the native stack.

#92
  • Loading branch information
benfred committed Jun 30, 2019
1 parent 288189d commit 4c53c95
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 251 deletions.
2 changes: 1 addition & 1 deletion remoteprocess/src/dylib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ macro_rules! dlsym {
static $name: self::dylib::Symbol<unsafe extern fn($($t),*) -> $ret> =
self::dylib::Symbol {
name: concat!(stringify!($name), "\0"),
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
addr: ::std::sync::atomic::AtomicUsize::new(0),
_marker: ::std::marker::PhantomData,
};
)*)
Expand Down
11 changes: 7 additions & 4 deletions remoteprocess/src/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ pub mod libunwind;
mod gimli_unwinder;
#[cfg(unwind)]
mod symbolication;
use libc::{c_void, pid_t};
use libc::pid_t;
#[cfg(unwind)]
use libc::c_void;

use nix::{self, sys::wait, sys::ptrace, {sched::{setns, CloneFlags}}};
use std::io::Read;
Expand All @@ -25,6 +27,7 @@ pub use self::libunwind::{LibUnwind};
use read_process_memory::{CopyAddress};

pub type Pid = pid_t;
pub type Tid = pid_t;

pub struct Process {
pub pid: Pid,
Expand Down Expand Up @@ -106,7 +109,7 @@ impl super::ProcessMemory for Process {
}

impl Thread {
pub fn new(threadid: i32) -> Thread{
pub fn new(threadid: i32) -> Thread {
Thread{tid: nix::unistd::Pid::from_raw(threadid)}
}

Expand All @@ -128,8 +131,8 @@ impl Thread {
}
}

pub fn id(&self) -> Result<u64, Error> {
Ok(self.tid.as_raw() as u64)
pub fn id(&self) -> Result<Tid, Error> {
Ok(self.tid.as_raw())
}

pub fn active(&self) -> Result<bool, Error> {
Expand Down
13 changes: 11 additions & 2 deletions remoteprocess/src/osx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub use self::unwinder::Unwinder;
use libproc::libproc::proc_pid::{pidpath, pidinfo, PIDInfo, PidInfoFlavor};

pub type Pid = pid_t;
pub type Tid = u32;

pub struct Process {
pub pid: Pid,
Expand All @@ -35,7 +36,7 @@ pub struct Process {

#[derive(Eq, PartialEq, Hash, Copy, Clone)]
pub struct Thread {
pub tid: u32
pub tid: Tid
}

impl Process {
Expand Down Expand Up @@ -95,7 +96,15 @@ use self::mach_thread_bindings::{thread_info, thread_basic_info, thread_identifi


impl Thread {
pub fn id(&self) -> Result<u64, Error> {
pub fn new(tid: Tid) -> Thread {
Thread{tid}
}

pub fn id(&self) -> Result<Tid, Error> {
Ok(self.tid)
}

pub fn thread_handle(&self) -> Result<u64, Error> {
let thread_id = self.get_thread_identifier_info()?;
Ok(thread_id.thread_handle)
}
Expand Down
15 changes: 14 additions & 1 deletion remoteprocess/src/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use winapi::um::processthreadsapi::{OpenProcess, GetThreadId, SuspendThread, ResumeThread};
use winapi::um::processthreadsapi::{OpenProcess, OpenThread, GetThreadId, SuspendThread, ResumeThread};
use winapi::um::winnt::{ACCESS_MASK, MAXIMUM_ALLOWED, PROCESS_QUERY_INFORMATION,
PROCESS_VM_READ, PROCESS_SUSPEND_RESUME, THREAD_QUERY_INFORMATION, THREAD_GET_CONTEXT,
WCHAR, HANDLE};
Expand All @@ -11,6 +11,8 @@ use winapi::shared::ntdef::{PVOID, NTSTATUS, USHORT, VOID, NULL};

pub use read_process_memory::{Pid, ProcessHandle, CopyAddress};

pub type Tid = Pid;

use super::Error;

mod unwinder;
Expand Down Expand Up @@ -119,6 +121,17 @@ pub struct Thread {
}

impl Thread {
pub fn new(tid: Tid) -> Result<Process, Error> {
// we can't just use try_into_prcess_handle here because we need some additional permissions
unsafe {
let thread = OpenThread(PROCESS_VM_READ | PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION
| THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT, FALSE, tid);
if thread == (0 as std::os::windows::io::RawHandle) {
return Err(Error::from(std::io::Error::last_os_error()));
}
Ok(Thread{thread})
}
}
pub fn lock(&self) -> Result<ThreadLock, Error> {
ThreadLock::new(self.thread)
}
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn print_traces(traces: &[StackTrace], show_idle: bool) {
}

if let Some(os_thread_id) = trace.os_thread_id {
println!("Thread {:#X}/{:#X} ({})", trace.thread_id, os_thread_id, trace.status_str());
println!("Thread {:#X}/{} ({})", trace.thread_id, os_thread_id, trace.status_str());
} else {
println!("Thread {:#X} ({})", trace.thread_id, trace.status_str());
}
Expand Down Expand Up @@ -283,6 +283,8 @@ fn pyspy_main() -> Result<(), Error> {
}

fn main() {
env_logger::init();

if let Err(err) = pyspy_main() {
#[cfg(unix)]
{
Expand Down
Loading

0 comments on commit 4c53c95

Please sign in to comment.