Skip to content

Commit b5758b3

Browse files
committed
Make all PEP-669 events local
We make all the events of the low-impact monitoring API local, shifting the distinction to whether they are instrumented or non-instrumented.
1 parent 6d743f9 commit b5758b3

File tree

9 files changed

+340
-90
lines changed

9 files changed

+340
-90
lines changed

Doc/library/sys.monitoring.rst

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,14 @@ events, use the expression ``PY_RETURN | PY_START``.
179179
Local events
180180
''''''''''''
181181

182-
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:
182+
Local events are associated with execution of a particular code object and
183+
can all be disabled via :data:`DISABLE`. There are two kinds, which differ
184+
in how :data:`DISABLE` takes effect.
185+
186+
**Instrumented local events** require bytecode instrumentation and fire at
187+
clearly defined instruction locations within a function. Returning
188+
:data:`DISABLE` from a callback disables the event at that specific
189+
code location only, leaving all other locations unaffected:
185190

186191
* :monitoring-event:`PY_START`
187192
* :monitoring-event:`PY_RESUME`
@@ -195,6 +200,24 @@ The local events are:
195200
* :monitoring-event:`BRANCH_RIGHT`
196201
* :monitoring-event:`STOP_ITERATION`
197202

203+
**Non-instrumented local events** do not require bytecode instrumentation and
204+
are not tied to a specific instruction. They can fire at any point within
205+
a function where the triggering condition occurs. Returning :data:`DISABLE`
206+
from a callback disables the event for the entire code object (for the
207+
current tool):
208+
209+
* :monitoring-event:`PY_UNWIND`
210+
* :monitoring-event:`EXCEPTION_HANDLED`
211+
* :monitoring-event:`RAISE`
212+
* :monitoring-event:`PY_THROW`
213+
* :monitoring-event:`RERAISE`
214+
215+
.. versionchanged:: 3.15
216+
:monitoring-event:`PY_UNWIND`, :monitoring-event:`EXCEPTION_HANDLED`,
217+
:monitoring-event:`RAISE`, :monitoring-event:`PY_THROW`, and
218+
:monitoring-event:`RERAISE` are now local events and can be disabled
219+
via :data:`DISABLE`.
220+
198221
Deprecated event
199222
''''''''''''''''
200223

@@ -205,6 +228,8 @@ Using :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT`
205228
events will give much better performance as they can be disabled
206229
independently.
207230

231+
.. _monitoring-ancillary-events:
232+
208233
Ancillary events
209234
''''''''''''''''
210235

@@ -220,22 +245,6 @@ are controlled by the :monitoring-event:`CALL` event.
220245
seen if the corresponding :monitoring-event:`CALL` event is being monitored.
221246

222247

223-
.. _monitoring-event-global:
224-
225-
Other events
226-
''''''''''''
227-
228-
Other events are not necessarily tied to a specific location in the
229-
program and cannot be individually disabled via :data:`DISABLE`.
230-
231-
The other events that can be monitored are:
232-
233-
* :monitoring-event:`PY_THROW`
234-
* :monitoring-event:`PY_UNWIND`
235-
* :monitoring-event:`RAISE`
236-
* :monitoring-event:`EXCEPTION_HANDLED`
237-
238-
239248
The STOP_ITERATION event
240249
''''''''''''''''''''''''
241250

@@ -247,8 +256,7 @@ raise an exception unless it would be visible to other code.
247256

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

253261
Note that the :monitoring-event:`STOP_ITERATION` event and the
254262
:monitoring-event:`RAISE` event for a :exc:`StopIteration` exception are
@@ -307,22 +315,23 @@ Disabling events
307315
.. data:: DISABLE
308316

309317
A special value that can be returned from a callback function to disable
310-
events for the current code location.
311-
312-
:ref:`Local events <monitoring-event-local>` can be disabled for a specific code
313-
location by returning :data:`sys.monitoring.DISABLE` from a callback function.
314-
This does not change which events are set, or any other code locations for the
315-
same event.
316-
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.
321-
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).
318+
the event.
319+
320+
All :ref:`local events <monitoring-event-local>` can be disabled by returning
321+
:data:`sys.monitoring.DISABLE` from a callback function. This does not change
322+
which events are set globally.
323+
324+
For :ref:`instrumented local events <monitoring-event-local>`, :data:`DISABLE`
325+
disables the event at the specific code location where it fired only, leaving
326+
all other locations for the same event unaffected.
327+
328+
For :ref:`non-instrumented local events <monitoring-event-local>`,
329+
:data:`DISABLE` disables the event for the entire code object (for the current
330+
tool).
331+
332+
Disabling events is very important for high performance monitoring. For
333+
example, a program can be run under a debugger with no overhead if the
334+
debugger disables all monitoring except for a few breakpoints.
326335

327336
.. function:: restart_events() -> None
328337

Include/cpython/monitoring.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ extern "C" {
88
// There is currently no limited API for monitoring
99

1010

11-
/* Local events.
12-
* Some of these require bytecode instrumentation */
11+
/* Local events (tracked per code object).
12+
* Events 0-10 require bytecode instrumentation; 11-15 do not. */
1313

1414
#define PY_MONITORING_EVENT_PY_START 0
1515
#define PY_MONITORING_EVENT_PY_RESUME 1
@@ -27,18 +27,14 @@ extern "C" {
2727
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)
2828

2929
#define PY_MONITORING_EVENT_PY_UNWIND 11
30-
31-
#define PY_MONITORING_IS_LOCAL_EVENT(ev) \
32-
((ev) < _PY_MONITORING_LOCAL_EVENTS)
33-
34-
35-
/* Other events, mainly exceptions */
36-
3730
#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 12
3831
#define PY_MONITORING_EVENT_RAISE 13
3932
#define PY_MONITORING_EVENT_PY_THROW 14
4033
#define PY_MONITORING_EVENT_RERAISE 15
4134

35+
#define PY_MONITORING_IS_LOCAL_EVENT(ev) \
36+
((ev) < _PY_MONITORING_UNGROUPED_EVENTS)
37+
4238

4339
/* Ancillary events */
4440

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ PyObject * _PyEval_ImportNameWithImport(
329329
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
330330
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
331331
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
332-
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate);
332+
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame);
333333
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
334334
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
335335
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
@@ -73,16 +73,15 @@ PyAPI_DATA(PyObject) _PyInstrumentation_DISABLE;
7373

7474
/* Total tool ids available */
7575
#define PY_MONITORING_TOOL_IDS 8
76-
/* Count of all local monitoring events */
77-
#define _PY_MONITORING_LOCAL_EVENTS 12
78-
/* Count of all "real" monitoring events (not derived from other events) */
76+
/* Count of all "real" monitoring events (not derived from other events).
77+
* All ungrouped events are now local events. */
7978
#define _PY_MONITORING_UNGROUPED_EVENTS 16
8079
/* Count of all monitoring events */
8180
#define _PY_MONITORING_EVENTS 19
8281

8382
/* Tables of which tools are active for each monitored event. */
8483
typedef struct _Py_LocalMonitors {
85-
uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
84+
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
8685
} _Py_LocalMonitors;
8786

8887
typedef struct _Py_GlobalMonitors {

0 commit comments

Comments
 (0)