Skip to content

Commit 8f2c994

Browse files
authored
Fix profiling on windows with python3.12 (#774)
* Fix profiling on windows with python3.12 Windows was failing on python 3.12 with an error message like ``` Only part of a ReadProcessMemory or WriteProcessMemory request was completed. (os error 299) ``` This was caused by incorrect handling of getting the gil_thread_id. * temporarily pause clippy linting * Migrate to upload-artifact@v4 * . * test on ubuntu 24.04 instead of 20.04 * 22.04 * Update python test versions
1 parent 1fa3a6d commit 8f2c994

8 files changed

Lines changed: 124 additions & 105 deletions

File tree

.github/workflows/build.yml

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,18 @@ jobs:
7676
run: |
7777
python3 -c "import shutil; import glob; wheels = glob.glob('dist/*.whl'); [shutil.move(wheel, wheel.replace('py3', 'py2.py3')) for wheel in wheels if 'py2' not in wheel]"
7878
if: runner.os != 'Linux'
79-
- name: Upload wheels
80-
uses: actions/upload-artifact@v3
79+
- name: Upload Windows Wheels
80+
uses: actions/upload-artifact@v4
8181
with:
82-
name: wheels
82+
name: wheels-windows
8383
path: dist
84-
if: runner.os != 'Linux'
84+
if: runner.os == 'Windows'
85+
- name: Upload Macos Wheels
86+
uses: actions/upload-artifact@v4
87+
with:
88+
name: wheels-macos
89+
path: dist
90+
if: runner.os == 'macOS'
8591

8692
build-linux-cross:
8793
runs-on: ubuntu-latest
@@ -120,9 +126,9 @@ jobs:
120126
run: |
121127
python3 -c "import shutil; import glob; wheels = glob.glob('dist/*.whl'); [shutil.move(wheel, wheel.replace('py3', 'py2.py3')) for wheel in wheels if 'py2' not in wheel]"
122128
- name: Upload wheels
123-
uses: actions/upload-artifact@v3
129+
uses: actions/upload-artifact@v4
124130
with:
125-
name: wheels
131+
name: wheels-${{ matrix.target }}
126132
path: dist
127133

128134
build-freebsd:
@@ -135,20 +141,6 @@ jobs:
135141
- freebsd-14
136142
steps:
137143
- uses: actions/checkout@v3
138-
- name: Cache Vagrant box
139-
uses: actions/cache@v3.0.4
140-
with:
141-
path: .vagrant.d
142-
key: ${{ matrix.box }}-vagrant-boxes-20231115-${{ hashFiles('ci/Vagrantfile') }}
143-
restore-keys: |
144-
${{ matrix.box }}-vagrant-boxes-20231115-
145-
- name: Cache Cargo and build artifacts
146-
uses: actions/cache@v3.0.4
147-
with:
148-
path: build-artifacts.tar
149-
key: ${{ matrix.box }}-cargo-20231115-${{ hashFiles('**/Cargo.lock') }}
150-
restore-keys: |
151-
${{ matrix.box }}-cargo-20231115-
152144
- name: Display CPU info
153145
run: lscpu
154146
- name: Install VM tools
@@ -177,7 +169,7 @@ jobs:
177169
tar xf build-artifacts.tar target/release/py-spy
178170
mv target/release/py-spy py-spy-x86_64-unknown-freebsd
179171
- name: Upload Binaries
180-
uses: actions/upload-artifact@v3
172+
uses: actions/upload-artifact@v4
181173
with:
182174
name: py-spy-x86_64-unknown-freebsd
183175
path: py-spy-x86_64-unknown-freebsd
@@ -199,11 +191,11 @@ jobs:
199191
3.8.0,
200192
3.8.18,
201193
3.9.0,
202-
3.9.20,
194+
3.9.23,
203195
3.10.0,
204-
3.10.15,
196+
3.10.18,
205197
3.11.0,
206-
3.11.10,
198+
3.11.13,
207199
3.12.0,
208200
3.12.1,
209201
3.12.2,
@@ -212,64 +204,79 @@ jobs:
212204
3.12.5,
213205
3.12.6,
214206
3.12.7,
207+
3.12.8,
208+
3.12.9,
209+
3.12.10,
210+
3.12.11,
215211
3.13.0,
212+
3.13.1,
213+
3.13.2,
214+
3.13.3,
215+
3.13.4,
216+
3.13.5,
216217
]
217-
# TODO: also test windows
218-
os: [ubuntu-20.04, macos-13, windows-latest]
218+
os: [ubuntu-22.04, macos-13, windows-latest]
219219
# some versions of python can't be tested on GHA with osx because of SIP:
220220
exclude:
221+
- os: ubuntu-22.04
222+
python-version: 3.6.7
221223
- os: windows-latest
222224
python-version: 3.6.15
225+
- os: ubuntu-22.04
226+
python-version: 3.6.15
227+
- os: ubuntu-22.04
228+
python-version: 3.7.1
223229
- os: windows-latest
224230
python-version: 3.7.17
231+
- os: ubuntu-22.04
232+
python-version: 3.8.0
225233
- os: windows-latest
226234
python-version: 3.8.18
235+
- os: ubuntu-22.04
236+
python-version: 3.9.0
227237
- os: windows-latest
228-
python-version: 3.9.20
238+
python-version: 3.9.23
239+
- os: ubuntu-22.04
240+
python-version: 3.10.0
229241
- os: windows-latest
230-
python-version: 3.10.15
242+
python-version: 3.10.18
231243
- os: macos-13
232-
python-version: 3.11.10
244+
python-version: 3.11.13
233245
- os: windows-latest
234-
python-version: 3.11.10
246+
python-version: 3.11.13
235247
- os: macos-13
236248
python-version: 3.12.0
237-
- os: windows-latest
238-
python-version: 3.12.0
239249
- os: macos-13
240250
python-version: 3.12.1
241-
- os: windows-latest
242-
python-version: 3.12.1
243251
- os: macos-13
244252
python-version: 3.12.2
245-
- os: windows-latest
246-
python-version: 3.12.2
247253
- os: macos-13
248254
python-version: 3.12.3
249-
- os: windows-latest
250-
python-version: 3.12.3
251255
- os: macos-13
252256
python-version: 3.12.4
253-
- os: windows-latest
254-
python-version: 3.12.4
255257
- os: macos-13
256258
python-version: 3.12.5
257-
- os: windows-latest
258-
python-version: 3.12.5
259259
- os: macos-13
260260
python-version: 3.12.6
261-
- os: windows-latest
262-
python-version: 3.12.6
263261
- os: macos-13
264262
python-version: 3.12.7
263+
- os: macos-13
264+
python-version: 3.12.8
265+
- os: macos-13
266+
python-version: 3.12.9
267+
- os: macos-13
268+
python-version: 3.12.10
269+
- os: macos-13
270+
python-version: 3.12.11
265271
- os: windows-latest
266-
python-version: 3.12.7
272+
python-version: 3.12.11
267273

268274
steps:
269275
- uses: actions/checkout@v2
270-
- uses: actions/download-artifact@v3
276+
- uses: actions/download-artifact@v4
271277
with:
272-
name: wheels
278+
pattern: wheels-*
279+
merge-multiple: true
273280
- uses: actions/setup-python@v4
274281
with:
275282
python-version: ${{ matrix.python-version }}
@@ -311,9 +318,10 @@ jobs:
311318
if: "startsWith(github.ref, 'refs/tags/')"
312319
needs: [test-wheels]
313320
steps:
314-
- uses: actions/download-artifact@v3
321+
- uses: actions/download-artifact@v4
315322
with:
316-
name: wheels
323+
pattern: wheels-*
324+
merge-multiple: true
317325
- name: Create GitHub Release
318326
uses: fnkr/github-action-ghr@v1.3
319327
env:

.pre-commit-config.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@ repos:
1111
hooks:
1212
- id: fmt
1313
- id: cargo-check
14-
- repo: local
15-
hooks:
16-
- id: cargo-clippy
17-
name: cargo clippy
18-
entry: cargo clippy -- -D warnings
19-
language: system
20-
files: \.rs$
21-
pass_filenames: false
2214
- repo: https://github.com/rbubley/mirrors-prettier
2315
rev: v3.3.3
2416
hooks:

ci/update_python_test_versions.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ def get_github_python_versions():
1919
# get a map of version: platform/arch so we can exclude here
2020
platforms = {}
2121
for v in versions_json:
22-
platforms[v["version"]] = set((f["platform"], f["arch"]) for f in v["files"])
22+
version_platforms = set()
23+
for f in v["files"]:
24+
platform, arch = f["platform"], f["arch"]
25+
if platform == "linux" and f.get("platform_version") != "22.04":
26+
continue
27+
version_platforms.add((platform, arch))
28+
platforms[v["version"]] = version_platforms
2329

2430
raw_versions = [v["version"] for v in versions_json]
2531
minor_versions = defaultdict(list)
@@ -63,7 +69,7 @@ def update_python_test_versions():
6369
pathlib.Path(__file__).parent.parent / ".github" / "workflows" / "build.yml"
6470
)
6571

66-
build_yml = yaml.safe_load(open(".github/workflows/build.yml"))
72+
build_yml = yaml.safe_load(open(build_yml_path))
6773
test_matrix = build_yml["jobs"]["test-wheels"]["strategy"]["matrix"]
6874
existing_python_versions = test_matrix["python-version"]
6975
if versions == existing_python_versions:
@@ -95,10 +101,14 @@ def update_python_test_versions():
95101
exclusions.append(" - os: macos-13\n")
96102
exclusions.append(f" python-version: {v}\n")
97103

98-
if ("win32", "x64") not in platforms[v] or v.startswith("3.12"):
104+
if ("win32", "x64") not in platforms[v]:
99105
exclusions.append(" - os: windows-latest\n")
100106
exclusions.append(f" python-version: {v}\n")
101107

108+
if ("linux", "x64") not in platforms[v]:
109+
exclusions.append(" - os: ubuntu-22.04\n")
110+
exclusions.append(f" python-version: {v}\n")
111+
102112
first_exclude_line = lines.index(" exclude:\n", first_line)
103113
last_exclude_line = lines.index("\n", first_exclude_line)
104114
lines = lines[: first_exclude_line + 1] + exclusions + lines[last_exclude_line:]

src/coredump.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ impl PythonCoreDump {
247247
// lets us figure out which thread has the GIL
248248
let config = Config::default();
249249
let threadstate_address =
250-
get_threadstate_address(interpreter_address, &python_info, &version, &config)?;
250+
get_threadstate_address(interpreter_address, &python_info, &core, &version, &config)?;
251251
info!("found threadstate at 0x{:016x}", threadstate_address);
252252

253253
Ok(PythonCoreDump {

src/python_interpreters.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ pub trait InterpreterState {
2222
type StringObject: StringObject;
2323
type ListObject: ListObject;
2424
type TupleObject: TupleObject;
25+
const HAS_GIL_RUNTIME_STATE: bool = false;
26+
2527
fn head(&self) -> *mut Self::ThreadState;
26-
fn gil_locked(&self) -> Option<bool>;
2728
fn modules(&self) -> *mut Self::Object;
2829
}
2930

@@ -117,9 +118,6 @@ macro_rules! PythonCommonImpl {
117118
fn head(&self) -> *mut Self::ThreadState {
118119
self.tstate_head
119120
}
120-
fn gil_locked(&self) -> Option<bool> {
121-
None
122-
}
123121
fn modules(&self) -> *mut Self::Object {
124122
self.modules
125123
}
@@ -416,12 +414,11 @@ impl InterpreterState for v3_13_0::PyInterpreterState {
416414
type StringObject = v3_13_0::PyUnicodeObject;
417415
type ListObject = v3_13_0::PyListObject;
418416
type TupleObject = v3_13_0::PyTupleObject;
417+
const HAS_GIL_RUNTIME_STATE: bool = true;
418+
419419
fn head(&self) -> *mut Self::ThreadState {
420420
self.threads.head
421421
}
422-
fn gil_locked(&self) -> Option<bool> {
423-
Some(self._gil.locked != 0)
424-
}
425422
fn modules(&self) -> *mut Self::Object {
426423
self.imports.modules
427424
}
@@ -500,14 +497,11 @@ impl InterpreterState for v3_12_0::PyInterpreterState {
500497
type StringObject = v3_12_0::PyUnicodeObject;
501498
type ListObject = v3_12_0::PyListObject;
502499
type TupleObject = v3_12_0::PyTupleObject;
500+
const HAS_GIL_RUNTIME_STATE: bool = true;
503501

504502
fn head(&self) -> *mut Self::ThreadState {
505503
self.threads.head
506504
}
507-
fn gil_locked(&self) -> Option<bool> {
508-
Some(self._gil.locked._value != 0)
509-
}
510-
511505
fn modules(&self) -> *mut Self::Object {
512506
self.imports.modules
513507
}
@@ -593,12 +587,10 @@ impl InterpreterState for v3_11_0::PyInterpreterState {
593587
type StringObject = v3_11_0::PyUnicodeObject;
594588
type ListObject = v3_11_0::PyListObject;
595589
type TupleObject = v3_11_0::PyTupleObject;
590+
596591
fn head(&self) -> *mut Self::ThreadState {
597592
self.threads.head
598593
}
599-
fn gil_locked(&self) -> Option<bool> {
600-
None
601-
}
602594
fn modules(&self) -> *mut Self::Object {
603595
self.modules
604596
}

src/python_process_info.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -588,30 +588,32 @@ where
588588
}
589589
}
590590

591-
pub fn get_threadstate_address(
591+
pub fn get_threadstate_address<P>(
592592
interpreter_address: usize,
593593
python_info: &PythonProcessInfo,
594+
process: &P,
594595
version: &Version,
595596
config: &Config,
596-
) -> Result<usize, Error> {
597+
) -> Result<usize, Error>
598+
where
599+
P: ProcessMemory,
600+
{
597601
let threadstate_address = match version {
598602
Version {
599603
major: 3,
600604
minor: 13,
601605
..
602606
} => {
603-
let interp: v3_13_0::_is = Default::default();
604-
let offset = crate::utils::offset_of(&interp, &interp._gil.last_holder);
605-
interpreter_address + offset
607+
let interp: v3_13_0::_is = process.copy_struct(interpreter_address)?;
608+
interp.ceval.gil as usize
606609
}
607610
Version {
608611
major: 3,
609612
minor: 12,
610613
..
611614
} => {
612-
let interp: v3_12_0::_is = Default::default();
613-
let offset = crate::utils::offset_of(&interp, &interp._gil.last_holder._value);
614-
interpreter_address + offset
615+
let interp: v3_12_0::_is = process.copy_struct(interpreter_address)?;
616+
interp.ceval.gil as usize
615617
}
616618
Version {
617619
major: 3,

src/python_spy.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ impl PythonSpy {
6262
info!("Found interpreter at 0x{:016x}", interpreter_address);
6363

6464
// lets us figure out which thread has the GIL
65-
let threadstate_address =
66-
get_threadstate_address(interpreter_address, &python_info, &version, config)?;
65+
let threadstate_address = get_threadstate_address(
66+
interpreter_address,
67+
&python_info,
68+
&process,
69+
&version,
70+
config,
71+
)?;
6772

6873
#[cfg(feature = "unwind")]
6974
let native = if config.native {
@@ -219,11 +224,8 @@ impl PythonSpy {
219224
.context("Failed to copy PyInterpreterState from process")?;
220225

221226
// get the threadid of the gil if appropriate
222-
let gil_thread_id = if interp.gil_locked().unwrap_or(true) {
223-
get_gil_threadid::<I, Process>(self.threadstate_address, &self.process)?
224-
} else {
225-
0
226-
};
227+
let gil_thread_id = get_gil_threadid::<I, Process>(self.threadstate_address, &self.process)
228+
.context("failed to get gil_thread_id")?;
227229

228230
let mut traces = Vec::new();
229231
let mut threads = interp.head();

0 commit comments

Comments
 (0)