Let tools know when tracing/profiling is enabled/disabled (original) (raw)
February 12, 2026, 7:19pm 1
While audit hooks notify us when sys.setprofile/sys.settrace is called, they do not report the argument passed to these functions. This means we cannot distinguish between tracing/profiling being enabled (a function is set) versus disabled (cleared with None).
This forces tools that want different behavior based on tracing/profiling enablement (e.g. JIT compilers) to poll on every function call, adding significant overhead. PEP-523 established frame evaluation hooks as an officially supported extension point. However, tools using this API have no efficient way to know when tracing/profiling is enabled/disabled.
For example, it’s difficult for a JIT to support debugging/profiling workflows. The simplest solution would be for the JIT may just want to fall back to the interpreter when tracing/profiling is enabled.
I propose adding a small C API enhancement allowing callbacks to be registered when trace/profile functions are set or cleared (PR).
Alternatively, if we modify audit hooks to report the argument in addition to the sys.setprofile/sys.settrace event then that would be sufficient as well. Since audit hooks run before the function completes, just knowing if sys.setprofile/sys.settrace was called isn’t enough to know if tracing/profiling is enabled.
markshannon (Mark Shannon) March 11, 2026, 4:49pm 2
Why only sys.settrace and sys.setprofile?
coverage.py and pdb now use sys.monitoring directly, so wouldn’t be supported.
Subbarao (Subbarao Garlapati) March 12, 2026, 1:52pm 3
I was thinking only sys.settrace and sys.setprofile because those can be invoked at the C level (e.g. PyEval_SetProfile() ) — there’s no Python-level function to intercept. Whereas sys.monitoring.register_callback can be patched externally and doesn’t have a public C API.
markshannon (Mark Shannon) March 26, 2026, 6:40pm 4
If you can detect when sys.setprofile/sys.settrace are called, then you should only need to poll once after a sys.setprofile/sys.settrace call, not on every call.
If you do need a hook, then I think the best way to do it would be to add an “instrumented” event for code watchers.
Subbarao (Subbarao Garlapati) March 28, 2026, 12:11am 5
Okay thanks! We can detect when sys.setprofile/sys.settrace are called, but we can’t detect PyEval_SetProfile()/PyEval_SetTrace(), which are part of the C API. I’ll look into the “instrumented” event!
Subbarao (Subbarao Garlapati) March 30, 2026, 3:58pm 6
Okay did some investigation - here’s what I’ve come up with:
Why audit hooks wouldn’t work:
- A tool like the JIT would like to deopt frames currently on the stack when tracing/profiling is enabled. With an audit hook approach, since audit hooks fire before
_PyEval_SetTrace/_PyEval_SetProfilecompletes, the JIT would have to wait for the next function call to check. This would be too late for the frames on the stack to be deopted (so the behavior would differ from debugging/profiling in the interpreter).
Why an “instrumented” event for code watchers wouldn’t work:
sys.settrace()andsys.setprofile()would instrument every code object so there would be an event for each code object, which would give far more granularity than needed as well as being noisy and slowing down the JIT.
Why the current approach doesn’t have these issues:
- It fires at the end of the tracing/profiling call so it’s when they are fully active (allowing deopting of frames on the stack)
- It’s a single notification per call so it’s not overly noisy
I’m thinking it would make sense to keep with the current approach. Thoughts?