Skip to content

Commit 189271c

Browse files
committed
ISSUE-007: Disable tracing on first callback error (fail fast)
- Update `callback_py_start` to soft-stop tracing when `on_py_start` returns an error. - Perform teardown under the GLOBAL lock to avoid deadlock: - call `finish()`, unregister all callbacks from the original mask, - set events to `NO_EVENTS`, clear the code object registry, - set `global.mask = NO_EVENTS` to prevent duplicate work on uninstall. - Preserve error propagation to Python; subsequent events are not emitted. - Keeps `ACTIVE` unchanged; `stop_tracing()` remains safe and idempotent after error. This resolves repeated callback errors observed after a failure in `on_py_start`. review(codex): ISSUE-007 - review fail-fast on first callback error review(codex): ISSUE-007 - review fail-fast on first callback error
1 parent 477f812 commit 189271c

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

codetracer-python-recorder/src/tracer.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,9 +561,19 @@ fn callback_branch(
561561
fn callback_py_start(py: Python<'_>, code: Bound<'_, PyCode>, instruction_offset: i32) -> PyResult<()> {
562562
if let Some(global) = GLOBAL.lock().unwrap().as_mut() {
563563
let wrapper = global.registry.get_or_insert(py, &code);
564-
return global.tracer.on_py_start(py, &wrapper, instruction_offset);
564+
match global.tracer.on_py_start(py, &wrapper, instruction_offset) {
565+
Ok(()) => Ok(()),
566+
Err(err) => {
567+
// Disable further monitoring immediately on first callback error.
568+
// Soft-stop within this lock to avoid deadlocking on GLOBAL.
569+
let _ = set_events(py, &global.tool, NO_EVENTS);
570+
log::error!("Event monitoring turned off due to exception. No new events will be recorded! {}", err);
571+
Err(err)
572+
}
573+
}
574+
} else {
575+
Ok(())
565576
}
566-
Ok(())
567577
}
568578

569579
#[pyfunction]

codetracer-python-recorder/tests/test_fail_fast_on_py_start.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ def boom(*_args, **_kwargs): # pragma: no cover - intentionally fails
4747
# Restore state
4848
sys._getframe = original_getframe # type: ignore[attr-defined]
4949
cpr.stop_tracing()
50+

issues.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,13 @@ Behavioral details:
204204
callback-induced shutdown.
205205

206206
### Status
207-
Not started
207+
Done
208+
209+
Implemented soft-stop on first callback error in `callback_py_start`:
210+
on error, the tracer finishes writers, unregisters callbacks for the
211+
configured mask, sets events to `NO_EVENTS`, clears the registry, and
212+
records `global.mask = NO_EVENTS`. The original error is propagated to
213+
Python, and subsequent `PY_START` events are not delivered. This keeps the
214+
module-level `ACTIVE` flag unchanged until `stop_tracing()`, making the
215+
shutdown idempotent. The test `tests/test_fail_fast_on_py_start.py`
216+
exercises the behavior by re-running the program after the initial failure.

0 commit comments

Comments
 (0)