Skip to content

Commit

Permalink
Allow use as a rust library
Browse files Browse the repository at this point in the history
This change lets py-spy be used as a rust library. This allows you to write
rust code to connect to a python program and take a stack trace from it.

Most of this change is in adding some basic documentation on how to do this,
and adding a src/lib.rs that exports a couple of the core py-spy classes.
Note that we're only exposing a very minimal set of functionality right now,
and leaving things like the consoleviewer/flamegraph code out of this library.

benfred/py-spy#110
  • Loading branch information
benfred committed Jul 7, 2019
1 parent ba7e13e commit 98a5563
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 4 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
name = "remoteprocess"
version = "0.1.0"
authors = ["Ben Frederickson <github@benfrederickson.com>"]
repository = "https://github.com/benfred/py-spy"
homepage = "https://github.com/benfred/py-spy/tree/master/remoteprocess"
description = "cross platform api for getting information on a running processes"
readme = "README.md"
license = "GPL-3.0"
build="build.rs"

[dependencies]
Expand All @@ -28,4 +33,3 @@ winapi = {version = "0.3", features = ["winbase", "consoleapi", "wincon", "handl

[dev-dependencies]
env_logger = "0.6.1"

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Features:
- Listing all the threads in the process
- Getting a stack trace for a thread in the target process
- Resolve symbols for an address in the other process
- Figure out if a thread is active or not

This crate provides implementations for Linux, OSX and Windows.

Expand Down
3 changes: 1 addition & 2 deletions examples/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> {
println!("Thread {} - {}", thread.id()?, if thread.active()? { "running" } else { "idle" });

// lock the thread to get a consistent snapshot (unwinding will fail otherwise)
// Note: the thread will appear idle when locked, so wee are calling
// Note: the thread will appear idle when locked, so we are calling
// thread.active() before this
let _lock = thread.lock()?;

Expand Down Expand Up @@ -53,4 +53,3 @@ fn main() {
fn main() {
panic!("unwind not supported!");
}

56 changes: 55 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,54 @@
//! This crate provides a cross platform way of querying information about other processes running
//! on the system. This let's you build profiling and debugging tools.
//!
//! Features:
//!
//! * Getting the process executable name and current working directory
//! * Listing all the threads in the process
//! * Suspending the execution of a process or thread
//! * Returning if a thread is running or not
//! * Getting a stack trace for a thread in the target process
//! * Resolve symbols for an address in the other process
//! * Copy memory from the other process (using the read_process_memory crate)
//!
//! This crate provides implementations for Linux, OSX and Windows. However this crate is still
//! very much in alpha stage, and the following caveats apply:
//!
//! * Stack unwinding only works on x86_64 processors right now, and is disabled for arm/x86
//! * the OSX stack unwinding code is very unstable and shouldn't be relied on
//! * Getting the cwd on windows returns incorrect results
//!
//! # Example
//!
//! ```rust,no_run
//! fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> {
//! // Create a new handle to the process
//! let process = remoteprocess::Process::new(pid)?;
//! // Create a stack unwind object, and use it to get the stack for each thread
//! let unwinder = process.unwinder()?;
//! for thread in process.threads()?.iter() {
//! println!("Thread {} - {}", thread.id()?, if thread.active()? { "running" } else { "idle" });
//!
//! // lock the thread to get a consistent snapshot (unwinding will fail otherwise)
//! // Note: the thread will appear idle when locked, so we are calling
//! // thread.active() before this
//! let _lock = thread.lock()?;
//!
//! // Iterate over the callstack for the current thread
//! for ip in unwinder.cursor(&thread)? {
//! let ip = ip?;
//!
//! // Lookup the current stack frame containing a filename/function/linenumber etc
//! // for the current address
//! unwinder.symbolicate(ip, true, &mut |sf| {
//! println!("\t{}", sf);
//! })?;
//! }
//! }
//! Ok(())
//! }
//! ```
extern crate proc_maps;
extern crate goblin;
extern crate benfred_read_process_memory as read_process_memory;
Expand Down Expand Up @@ -38,7 +89,6 @@ mod linux;
#[cfg(target_os="linux")]
pub use linux::*;


#[cfg(target_os="windows")]
mod windows;
#[cfg(target_os="windows")]
Expand Down Expand Up @@ -176,6 +226,8 @@ impl std::fmt::Display for StackFrame {
}

pub trait ProcessMemory {
/// Copies memory from another process into an already allocated
/// byte buffer
fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error>;

/// Copies a series of bytes from another process. Main difference
Expand All @@ -199,6 +251,7 @@ pub trait ProcessMemory {
}
}

#[doc(hidden)]
/// Mock for using ProcessMemory on the local process.
pub struct LocalProcess;
impl ProcessMemory for LocalProcess {
Expand All @@ -210,6 +263,7 @@ impl ProcessMemory for LocalProcess {
}
}


#[cfg(test)]
pub mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions src/osx/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[doc(hidden)]
pub mod compact_unwind;
mod utils;
mod symbolication;
Expand Down

0 comments on commit 98a5563

Please sign in to comment.