diff --git a/src/coredump.rs b/src/coredump.rs index 7df1e010..e0118437 100644 --- a/src/coredump.rs +++ b/src/coredump.rs @@ -246,7 +246,8 @@ impl PythonCoreDump { // lets us figure out which thread has the GIL let config = Config::default(); - let threadstate_address = get_threadstate_address(&python_info, &version, &config)?; + let threadstate_address = + get_threadstate_address(interpreter_address, &python_info, &version, &config)?; info!("found threadstate at 0x{:016x}", threadstate_address); Ok(PythonCoreDump { diff --git a/src/python_interpreters.rs b/src/python_interpreters.rs index 609ca7ba..bd055791 100644 --- a/src/python_interpreters.rs +++ b/src/python_interpreters.rs @@ -14,6 +14,7 @@ This means we can't dereference them directly. use crate::python_bindings::{ v2_7_15, v3_10_0, v3_11_0, v3_12_0, v3_3_7, v3_5_5, v3_6_6, v3_7_0, v3_8_0, v3_9_5, }; +use crate::utils::offset_of; pub trait InterpreterState { type ThreadState: ThreadState; @@ -22,6 +23,7 @@ pub trait InterpreterState { type ListObject: ListObject; type TupleObject: TupleObject; fn head(&self) -> *mut Self::ThreadState; + fn gil_locked(&self) -> Option<bool>; fn modules(&self) -> *mut Self::Object; } @@ -100,10 +102,6 @@ pub trait TypeObject { fn flags(&self) -> usize; } -fn offset_of<T, M>(object: *const T, member: *const M) -> usize { - member as usize - object as usize -} - /// This macro provides a common impl for PyThreadState/PyFrameObject/PyCodeObject traits /// (this code is identical across python versions, we are only abstracting the struct layouts here). /// String handling changes substantially between python versions, and is handled separately. @@ -115,9 +113,13 @@ macro_rules! PythonCommonImpl { type StringObject = $py::$stringobject; type ListObject = $py::PyListObject; type TupleObject = $py::PyTupleObject; + fn head(&self) -> *mut Self::ThreadState { self.tstate_head } + fn gil_locked(&self) -> Option<bool> { + None + } fn modules(&self) -> *mut Self::Object { self.modules } @@ -415,9 +417,14 @@ impl InterpreterState for v3_12_0::PyInterpreterState { type StringObject = v3_12_0::PyUnicodeObject; type ListObject = v3_12_0::PyListObject; type TupleObject = v3_12_0::PyTupleObject; + fn head(&self) -> *mut Self::ThreadState { self.threads.head } + fn gil_locked(&self) -> Option<bool> { + Some(self._gil.locked._value != 0) + } + fn modules(&self) -> *mut Self::Object { self.imports.modules } @@ -506,6 +513,9 @@ impl InterpreterState for v3_11_0::PyInterpreterState { fn head(&self) -> *mut Self::ThreadState { self.threads.head } + fn gil_locked(&self) -> Option<bool> { + None + } fn modules(&self) -> *mut Self::Object { self.modules } diff --git a/src/python_process_info.rs b/src/python_process_info.rs index 2270556c..5ecb4cc2 100644 --- a/src/python_process_info.rs +++ b/src/python_process_info.rs @@ -529,6 +529,7 @@ where } pub fn get_threadstate_address( + interpreter_address: usize, python_info: &PythonProcessInfo, version: &Version, config: &Config, @@ -536,7 +537,16 @@ pub fn get_threadstate_address( let threadstate_address = match version { Version { major: 3, - minor: 7..=12, + minor: 12, + .. + } => { + let interp: v3_12_0::_is = Default::default(); + let offset = crate::utils::offset_of(&interp, &interp._gil.last_holder._value); + interpreter_address + offset + } + Version { + major: 3, + minor: 7..=11, .. } => match python_info.get_symbol("_PyRuntime") { Some(&addr) => { diff --git a/src/python_spy.rs b/src/python_spy.rs index 531616ab..40361577 100644 --- a/src/python_spy.rs +++ b/src/python_spy.rs @@ -62,7 +62,8 @@ impl PythonSpy { info!("Found interpreter at 0x{:016x}", interpreter_address); // lets us figure out which thread has the GIL - let threadstate_address = get_threadstate_address(&python_info, &version, config)?; + let threadstate_address = + get_threadstate_address(interpreter_address, &python_info, &version, config)?; #[cfg(feature = "unwind")] let native = if config.native { @@ -206,18 +207,19 @@ impl PythonSpy { None }; - // TODO: hoist most of this code out to stack_trace.rs, and - // then annotate the output of that with things like native stack traces etc - // have moved in gil / locals etc - let gil_thread_id = - get_gil_threadid::<I, Process>(self.threadstate_address, &self.process)?; - // Get the python interpreter, and loop over all the python threads let interp: I = self .process .copy_struct(self.interpreter_address) .context("Failed to copy PyInterpreterState from process")?; + // get the threadid of the gil if appropriate + let gil_thread_id = if interp.gil_locked().unwrap_or(true) { + get_gil_threadid::<I, Process>(self.threadstate_address, &self.process)? + } else { + 0 + }; + let mut traces = Vec::new(); let mut threads = interp.head(); while !threads.is_null() { diff --git a/src/stack_trace.rs b/src/stack_trace.rs index dce648e9..27d44a96 100644 --- a/src/stack_trace.rs +++ b/src/stack_trace.rs @@ -77,7 +77,11 @@ where I: InterpreterState, P: ProcessMemory, { - let gil_thread_id = get_gil_threadid::<I, P>(threadstate_address, process)?; + let gil_thread_id = if interpreter.gil_locked().unwrap_or(true) { + get_gil_threadid::<I, P>(threadstate_address, process)? + } else { + 0 + }; let mut ret = Vec::new(); let mut threads = interpreter.head(); diff --git a/src/utils.rs b/src/utils.rs index 2ca1374f..a354f151 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,3 +20,7 @@ pub fn resolve_filename(filename: &str, modulename: &str) -> Option<String> { None } + +pub fn offset_of<T, M>(object: *const T, member: *const M) -> usize { + member as usize - object as usize +}