Skip to content

Commit 858e69e

Browse files
authored
gh-142186: Allow all PEP-669 events to be per-code object and disableable (GH-146182)
* Make the `PY_UNWIND` monitoring event available as a code-local event to allow trapping on function exit events when an exception bubbles up. This complements the PY_RETURN event by allowing to catch any function exit event. * Allow `PY_UNWIND` to be `DISABLE`d; disabling it disables the event for the whole code object. * Do the above for `PY_THROW`, `RAISE`, `EXCEPTION_HANDLED`, and `RERAISE` events.
1 parent 09233bd commit 858e69e

11 files changed

Lines changed: 436 additions & 71 deletions

File tree

Doc/library/sys.monitoring.rst

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ Local events
180180
''''''''''''
181181

182182
Local events are associated with normal execution of the program and happen
183-
at clearly defined locations. All local events can be disabled.
184-
The local events are:
183+
at clearly defined locations. All local events can be disabled
184+
per location. The local events are:
185185

186186
* :monitoring-event:`PY_START`
187187
* :monitoring-event:`PY_RESUME`
@@ -205,6 +205,8 @@ Using :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT`
205205
events will give much better performance as they can be disabled
206206
independently.
207207

208+
.. _monitoring-ancillary-events:
209+
208210
Ancillary events
209211
''''''''''''''''
210212

@@ -226,14 +228,20 @@ Other events
226228
''''''''''''
227229

228230
Other events are not necessarily tied to a specific location in the
229-
program and cannot be individually disabled via :data:`DISABLE`.
231+
program and cannot be individually disabled per location.
230232

231233
The other events that can be monitored are:
232234

233235
* :monitoring-event:`PY_THROW`
234236
* :monitoring-event:`PY_UNWIND`
235237
* :monitoring-event:`RAISE`
236238
* :monitoring-event:`EXCEPTION_HANDLED`
239+
* :monitoring-event:`RERAISE`
240+
241+
.. versionchanged:: 3.15
242+
Other events can now be turned on and disabled on a per code object
243+
basis. Returning :data:`DISABLE` from a callback disables the event
244+
for the entire code object (for the current tool).
237245

238246

239247
The STOP_ITERATION event
@@ -247,8 +255,7 @@ raise an exception unless it would be visible to other code.
247255

248256
To allow tools to monitor for real exceptions without slowing down generators
249257
and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided.
250-
:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike
251-
:monitoring-event:`RAISE`.
258+
:monitoring-event:`STOP_ITERATION` can be locally disabled.
252259

253260
Note that the :monitoring-event:`STOP_ITERATION` event and the
254261
:monitoring-event:`RAISE` event for a :exc:`StopIteration` exception are
@@ -314,15 +321,14 @@ location by returning :data:`sys.monitoring.DISABLE` from a callback function.
314321
This does not change which events are set, or any other code locations for the
315322
same event.
316323

317-
Disabling events for specific locations is very important for high
318-
performance monitoring. For example, a program can be run under a
319-
debugger with no overhead if the debugger disables all monitoring
320-
except for a few breakpoints.
324+
:ref:`Other events <monitoring-event-global>` can be disabled on a per code
325+
object basis by returning :data:`sys.monitoring.DISABLE` from a callback
326+
function. This disables the event for the entire code object (for the current
327+
tool).
321328

322-
If :data:`DISABLE` is returned by a callback for a
323-
:ref:`global event <monitoring-event-global>`, :exc:`ValueError` will be raised
324-
by the interpreter in a non-specific location (that is, no traceback will be
325-
provided).
329+
Disabling events for specific locations is very important for high performance
330+
monitoring. For example, a program can be run under a debugger with no overhead
331+
if the debugger disables all monitoring except for a few breakpoints.
326332

327333
.. function:: restart_events() -> None
328334

Doc/whatsnew/3.15.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,19 @@ sys
11191119
(Contributed by Klaus Zimmermann in :gh:`137476`.)
11201120

11211121

1122+
sys.monitoring
1123+
--------------
1124+
1125+
* The :ref:`other events <monitoring-event-global>`
1126+
(:monitoring-event:`PY_THROW`, :monitoring-event:`PY_UNWIND`,
1127+
:monitoring-event:`RAISE`, :monitoring-event:`EXCEPTION_HANDLED`, and
1128+
:monitoring-event:`RERAISE`) can now be turned on and disabled on a per code
1129+
object basis. Returning :data:`~sys.monitoring.DISABLE` from a callback for
1130+
one of these events disables the event for the entire code object (for the
1131+
current tool), rather than raising :exc:`ValueError` as in prior versions.
1132+
(Contributed by Gabriele N. Tornetta in :gh:`146182`.)
1133+
1134+
11221135
tarfile
11231136
-------
11241137

Include/cpython/monitoring.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@ extern "C" {
2424
#define PY_MONITORING_EVENT_STOP_ITERATION 10
2525

2626
#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
27-
((ev) < _PY_MONITORING_LOCAL_EVENTS)
27+
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)
2828

29-
/* Other events, mainly exceptions */
29+
/* Other events, mainly exceptions.
30+
* These can now be turned on and disabled on a per code object basis. */
3031

31-
#define PY_MONITORING_EVENT_RAISE 11
32+
#define PY_MONITORING_EVENT_PY_UNWIND 11
3233
#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 12
33-
#define PY_MONITORING_EVENT_PY_UNWIND 13
34+
#define PY_MONITORING_EVENT_RAISE 13
3435
#define PY_MONITORING_EVENT_PY_THROW 14
3536
#define PY_MONITORING_EVENT_RERAISE 15
3637

38+
#define _PY_MONITORING_IS_UNGROUPED_EVENT(ev) \
39+
((ev) < _PY_MONITORING_UNGROUPED_EVENTS)
40+
3741

3842
/* Ancillary events */
3943

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ PyObject * _PyEval_ImportNameWithImport(
320320
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
321321
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
322322
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
323-
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate);
323+
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame);
324324
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
325325
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
326326
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);

Include/internal/pycore_instruments.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,15 @@ PyAPI_DATA(PyObject) _PyInstrumentation_DISABLE;
7070

7171
/* Total tool ids available */
7272
#define PY_MONITORING_TOOL_IDS 8
73-
/* Count of all local monitoring events */
74-
#define _PY_MONITORING_LOCAL_EVENTS 11
75-
/* Count of all "real" monitoring events (not derived from other events) */
73+
/* Count of all "real" monitoring events (not derived from other events).
74+
* "Other" events can now be turned on/disabled per code object. */
7675
#define _PY_MONITORING_UNGROUPED_EVENTS 16
7776
/* Count of all monitoring events */
7877
#define _PY_MONITORING_EVENTS 19
7978

8079
/* Tables of which tools are active for each monitored event. */
8180
typedef struct _Py_LocalMonitors {
82-
uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
81+
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
8382
} _Py_LocalMonitors;
8483

8584
typedef struct _Py_GlobalMonitors {

0 commit comments

Comments
 (0)