Skip to content

Commit 2b30cd4

Browse files
chore: minimal repro for crash
1 parent 62a894e commit 2b30cd4

4 files changed

Lines changed: 64 additions & 2 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ compat = [
4747
[tool.uv]
4848
# Python builds change with uv versions, and we are quite susceptible to that.
4949
# We pin uv to to make sure reproducibility is maintained for any contributor.
50-
required-version = "0.9.5"
50+
required-version = ">=0.9.5"
5151

5252
[tool.uv.sources]
5353
pytest-codspeed = { workspace = true }

report.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Segfault in `instrument_hooks_deinit` during Python shutdown
2+
3+
## Issue
4+
5+
CI job `tests (valgrind, 3.13, >=8.1.1)` crashes with exit code 139 (SIGSEGV) after all tests pass.
6+
The segfault occurs during Python's shutdown/GC phase, not during test execution.
7+
8+
Failing CI run: https://github.com/CodSpeedHQ/pytest-codspeed/actions/runs/23649460934/job/68890119732
9+
10+
## Root cause (suspected)
11+
12+
`InstrumentHooks.__del__` calls `instrument_hooks_deinit()` via cffi during Python shutdown.
13+
When multiple `InstrumentHooks` instances are created (e.g. the xdist test loops 5 times, each creating a new instance), their destructors fire during GC at exit and crash — likely because the native library or cffi runtime is already partially torn down.
14+
15+
## Stack trace (from CI)
16+
17+
```
18+
Fatal Python error: Segmentation fault
19+
File "plugin.py", line 148 in pytest_configure
20+
...
21+
File "test_pytest_plugin_cpu_instrumentation.py", line 116 in test_pytest_xdist_concurrency_compatibility
22+
```
23+
24+
## Minimal repro
25+
26+
```python
27+
# repro.py
28+
import os
29+
os.environ["CODSPEED_ENV"] = "1"
30+
31+
from pytest_codspeed.instruments.hooks import InstrumentHooks
32+
33+
for i in range(5):
34+
h = InstrumentHooks()
35+
print(f"Created instance {i}: {h.instance}")
36+
37+
del h
38+
print("Exiting...")
39+
```
40+
41+
```
42+
uv run python repro.py
43+
# Segfaults at exit with code 139
44+
```
45+
46+
## Key files
47+
48+
- `src/pytest_codspeed/instruments/hooks/__init__.py``InstrumentHooks.__del__` calls `instrument_hooks_deinit`
49+
- `src/pytest_codspeed/instruments/hooks/build.py` — cffi bindings for the native library
50+
- `src/pytest_codspeed/instruments/analysis.py` — creates `InstrumentHooks` in `__init__`
51+
- `tests/test_pytest_plugin_cpu_instrumentation.py::test_pytest_xdist_concurrency_compatibility` — triggers the bug by creating 5 successive instances

repro.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import os
2+
os.environ["CODSPEED_ENV"] = "1"
3+
4+
from pytest_codspeed.instruments.hooks import InstrumentHooks
5+
6+
for i in range(5):
7+
h = InstrumentHooks()
8+
print(f"Created instance {i}: {h.instance}")
9+
10+
del h
11+
print("Exiting...")

tests/test_pytest_plugin_cpu_instrumentation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def test_my_stuff(benchmark, i):
110110
"""
111111
)
112112
# Run the test multiple times to reduce the chance of a false positive
113-
ITERATIONS = 5
113+
ITERATIONS = 2
114114
for i in range(ITERATIONS):
115115
with codspeed_env():
116116
result = pytester.runpytest("--codspeed", "-n", "128")

0 commit comments

Comments
 (0)