Sampling profiler for Python programs written in Rust with extremely low overhead
npx @tessl/cli install tessl/pypi-py-spy@0.4.0A sampling profiler for Python programs written in Rust that allows developers to visualize what their Python programs are spending time on without restarting the program or modifying code. py-spy operates outside the profiled Python process with extremely low overhead, making it safe for production use.
pip install py-spypy-spy is primarily used as a command-line tool installed via pip. For advanced users who want to embed py-spy functionality in Rust applications:
use py_spy::{Config, PythonSpy, StackTrace, Frame, Pid};
use py_spy::sampler::Sampler;
use anyhow::Error;# Profile existing process (record to flamegraph)
py-spy record -o profile.svg --pid 12345
# Profile new Python program
py-spy record -o profile.svg -- python myprogram.py
# Live console view (like top)
py-spy top --pid 12345
# Single stack trace snapshot
py-spy dump --pid 12345use py_spy::{Config, PythonSpy, Pid};
use anyhow::Error;
fn profile_python_process(pid: Pid) -> Result<(), Error> {
// Create configuration with defaults
let config = Config::default();
// Create profiler instance
let mut spy = PythonSpy::new(pid, &config)?;
// Get current stack traces
let traces = spy.get_stack_traces()?;
// Process each thread's stack trace
for trace in traces {
println!("Thread {:#X} ({})", trace.thread_id, trace.status_str());
for frame in &trace.frames {
println!(" {} ({}:{})", frame.name, frame.filename, frame.line);
}
}
Ok(())
}py-spy uses a multi-layered architecture for safe, cross-platform Python process profiling:
This design enables py-spy to profile Python processes safely without code modification or significant performance impact.
Complete command-line profiling tool with three main subcommands and extensive configuration options for production and development use.
py-spy record [OPTIONS] [--pid PID | -- PYTHON_PROGRAM...]
py-spy top [OPTIONS] [--pid PID | -- PYTHON_PROGRAM...]
py-spy dump [OPTIONS] [--pid PID | -- PYTHON_PROGRAM...]Programmatic profiling interface for integrating py-spy functionality into Rust applications and custom profiling tools.
struct PythonSpy;
impl PythonSpy {
fn new(pid: Pid, config: &Config) -> Result<PythonSpy, Error>;
fn get_stack_traces(&mut self) -> Result<Vec<StackTrace>, Error>;
}
struct Sampler;
impl Sampler {
fn new(pid: Pid, config: &Config) -> Result<Sampler, Error>;
}
impl Iterator for Sampler {
type Item = Sample;
}Comprehensive configuration system supporting multiple profiling modes, output formats, and platform-specific options.
struct Config {
blocking: LockingStrategy,
native: bool,
sampling_rate: u64,
format: Option<FileFormat>,
// ... additional fields
}
enum FileFormat {
flamegraph, // SVG flamegraph
speedscope, // JSON for speedscope.app
chrometrace, // Chrome trace format
raw, // Raw flamegraph data
}struct StackTrace {
pid: Pid,
thread_id: u64,
thread_name: Option<String>,
os_thread_id: Option<u64>,
active: bool,
owns_gil: bool,
frames: Vec<Frame>,
process_info: Option<Arc<ProcessInfo>>,
}
impl StackTrace {
fn status_str(&self) -> &str;
fn format_threadid(&self) -> String;
}struct Frame {
name: String,
filename: String,
module: Option<String>,
short_filename: Option<String>,
line: i32,
locals: Option<Vec<LocalVariable>>,
is_entry: bool,
is_shim_entry: bool,
}type Pid = remoteprocess::Pid;enum LockingStrategy {
Lock, // Standard blocking mode
NonBlocking, // Minimal performance impact
AlreadyLocked, // Process already locked
}
enum RecordDuration {
Unlimited,
Seconds(u64),
}
enum LineNo {
NoLine,
First,
LastInstruction,
}