Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect GIL on python 3.12 #713

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/coredump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
18 changes: 14 additions & 4 deletions src/python_interpreters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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.
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
12 changes: 11 additions & 1 deletion src/python_process_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,14 +529,24 @@ where
}

pub fn get_threadstate_address(
interpreter_address: usize,
python_info: &PythonProcessInfo,
version: &Version,
config: &Config,
) -> Result<usize, Error> {
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) => {
Expand Down
16 changes: 9 additions & 7 deletions src/python_spy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
6 changes: 5 additions & 1 deletion src/stack_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading