diff --git a/src/config.rs b/src/config.rs index e1ebaa06..f65f9a61 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ pub struct Config { /// Setting this to false will reduce the performance impact on the target /// python process, but can lead to incorrect results like partial stack /// traces being returned or a higher sampling error rate - pub non_blocking: bool, + pub blocking: LockingStrategy, /// Whether or not to profile native extensions. Note: this option can not be /// used with the nonblocking option, as we have to pause the process to collect @@ -60,6 +60,14 @@ arg_enum!{ } } + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum LockingStrategy { + NonBlocking, + AlreadyLocked, + Lock +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum RecordDuration { Unlimited, @@ -72,7 +80,7 @@ impl Default for Config { fn default() -> Config { Config{pid: None, python_program: None, filename: None, format: None, command: String::from("top"), - non_blocking: false, show_line_numbers: false, sampling_rate: 100, + blocking: LockingStrategy::Lock, show_line_numbers: false, sampling_rate: 100, duration: RecordDuration::Unlimited, native: false, gil_only: false, include_idle: false, include_thread_ids: false, hide_progress: false, capture_output: true, dump_json: false, dump_locals: false, subprocesses: false} @@ -259,7 +267,6 @@ impl Config { config.gil_only = matches.occurrences_of("gil") > 0; config.include_thread_ids = matches.occurrences_of("threads") > 0; - config.non_blocking = matches.occurrences_of("nonblocking") > 0; config.native = matches.occurrences_of("native") > 0; config.hide_progress = matches.occurrences_of("hideprogress") > 0; config.dump_json = matches.occurrences_of("json") > 0; @@ -271,10 +278,13 @@ impl Config { config.hide_progress = true; } - // disable native profiling if invalidly asked for - if config.native && config.non_blocking { - eprintln!("Can't get native stack traces with the --nonblocking option."); - std::process::exit(1); + if matches.occurrences_of("nonblocking") > 0 { + // disable native profiling if invalidly asked for + if config.native { + eprintln!("Can't get native stack traces with the --nonblocking option."); + std::process::exit(1); + } + config.blocking = LockingStrategy::NonBlocking; } #[cfg(windows)] diff --git a/src/lib.rs b/src/lib.rs index c66949c9..1eb96fbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,8 +49,8 @@ extern crate rand; extern crate rand_distr; extern crate remoteprocess; -mod config; -mod binary_parser; +pub mod config; +pub mod binary_parser; #[cfg(unwind)] mod cython; #[cfg(unwind)] diff --git a/src/native_stack_trace.rs b/src/native_stack_trace.rs index 92c9bfcf..6ac11c07 100644 --- a/src/native_stack_trace.rs +++ b/src/native_stack_trace.rs @@ -35,7 +35,7 @@ impl NativeStack { python, libpython, process, - symbol_cache: LruCache::new(4096) + symbol_cache: LruCache::new(65536) }); } diff --git a/src/python_spy.rs b/src/python_spy.rs index 43bfe34a..fb2db4a0 100644 --- a/src/python_spy.rs +++ b/src/python_spy.rs @@ -17,7 +17,7 @@ use proc_maps::{get_process_maps, MapRange}; use crate::binary_parser::{parse_binary, BinaryInfo}; -use crate::config::Config; +use crate::config::{Config, LockingStrategy}; #[cfg(unwind)] use crate::native_stack_trace::NativeStack; use crate::python_bindings::{pyruntime, v2_7_15, v3_3_7, v3_5_5, v3_6_6, v3_7_0, v3_8_0}; @@ -186,10 +186,10 @@ impl PythonSpy { // activity status from the OS (otherwise each thread would report being inactive always). // This has the potential for race conditions (in that the thread activity could change // between getting the status and locking the thread, but seems unavoidable right now - let _lock = if self.config.non_blocking { - None - } else { + let _lock = if self.config.blocking == LockingStrategy::Lock { Some(self.process.lock().context("Failed to suspend process")?) + } else { + None }; let gil_thread_id = self._get_gil_threadid::()?; @@ -329,7 +329,7 @@ impl PythonSpy { fn _get_os_thread_id(&mut self, python_thread_id: u64, interp: &I) -> Result, Error> { // in nonblocking mode, we can't get the threadid reliably (method here requires reading the RBX // register which requires a ptrace attach). fallback to heuristic thread activity here - if self.config.non_blocking { + if self.config.blocking == LockingStrategy::NonBlocking { return Ok(None); }