Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 46 additions & 37 deletions Doc/library/sys.monitoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,14 @@ events, use the expression ``PY_RETURN | PY_START``.
Local events
''''''''''''

Local events are associated with normal execution of the program and happen
at clearly defined locations. All local events can be disabled.
The local events are:
Local events are associated with execution of a particular code object and
can all be disabled via :data:`DISABLE`. There are two kinds, which differ
in how :data:`DISABLE` takes effect.

**Instrumented local events** require bytecode instrumentation and fire at
clearly defined instruction locations within a function. Returning
:data:`DISABLE` from a callback disables the event at that specific
code location only, leaving all other locations unaffected:

* :monitoring-event:`PY_START`
* :monitoring-event:`PY_RESUME`
Expand All @@ -195,6 +200,24 @@ The local events are:
* :monitoring-event:`BRANCH_RIGHT`
* :monitoring-event:`STOP_ITERATION`

**Non-instrumented local events** do not require bytecode instrumentation and
are not tied to a specific instruction. They can fire at any point within
a function where the triggering condition occurs. Returning :data:`DISABLE`
from a callback disables the event for the entire code object (for the
current tool):

* :monitoring-event:`PY_UNWIND`
* :monitoring-event:`EXCEPTION_HANDLED`
* :monitoring-event:`RAISE`
* :monitoring-event:`PY_THROW`
* :monitoring-event:`RERAISE`

.. versionchanged:: 3.15
:monitoring-event:`PY_UNWIND`, :monitoring-event:`EXCEPTION_HANDLED`,
:monitoring-event:`RAISE`, :monitoring-event:`PY_THROW`, and
:monitoring-event:`RERAISE` are now local events and can be disabled
via :data:`DISABLE`.

Deprecated event
''''''''''''''''

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

.. _monitoring-ancillary-events:

Ancillary events
''''''''''''''''

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


.. _monitoring-event-global:

Other events
''''''''''''

Other events are not necessarily tied to a specific location in the
program and cannot be individually disabled via :data:`DISABLE`.

The other events that can be monitored are:

* :monitoring-event:`PY_THROW`
* :monitoring-event:`PY_UNWIND`
* :monitoring-event:`RAISE`
* :monitoring-event:`EXCEPTION_HANDLED`


The STOP_ITERATION event
''''''''''''''''''''''''

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

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

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

A special value that can be returned from a callback function to disable
events for the current code location.

:ref:`Local events <monitoring-event-local>` can be disabled for a specific code
location by returning :data:`sys.monitoring.DISABLE` from a callback function.
This does not change which events are set, or any other code locations for the
same event.

Disabling events for specific locations is very important for high
performance monitoring. For example, a program can be run under a
debugger with no overhead if the debugger disables all monitoring
except for a few breakpoints.

If :data:`DISABLE` is returned by a callback for a
:ref:`global event <monitoring-event-global>`, :exc:`ValueError` will be raised
by the interpreter in a non-specific location (that is, no traceback will be
provided).
the event.

All :ref:`local events <monitoring-event-local>` can be disabled by returning
:data:`sys.monitoring.DISABLE` from a callback function. This does not change
which events are set globally.

For :ref:`instrumented local events <monitoring-event-local>`, :data:`DISABLE`
disables the event at the specific code location where it fired only, leaving
all other locations for the same event unaffected.

For :ref:`non-instrumented local events <monitoring-event-local>`,
:data:`DISABLE` disables the event for the entire code object (for the current
tool).

Disabling events is very important for high performance monitoring. For
example, a program can be run under a debugger with no overhead if the
debugger disables all monitoring except for a few breakpoints.

.. function:: restart_events() -> None

Expand Down
15 changes: 8 additions & 7 deletions Include/cpython/monitoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ extern "C" {
// There is currently no limited API for monitoring


/* Local events.
* These require bytecode instrumentation */
/* Local events (tracked per code object).
* Events 0-10 require bytecode instrumentation; 11-15 do not. */

#define PY_MONITORING_EVENT_PY_START 0
#define PY_MONITORING_EVENT_PY_RESUME 1
Expand All @@ -24,16 +24,17 @@ extern "C" {
#define PY_MONITORING_EVENT_STOP_ITERATION 10

#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
((ev) < _PY_MONITORING_LOCAL_EVENTS)
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)

/* Other events, mainly exceptions */

#define PY_MONITORING_EVENT_RAISE 11
#define PY_MONITORING_EVENT_PY_UNWIND 11
#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 12
#define PY_MONITORING_EVENT_PY_UNWIND 13
#define PY_MONITORING_EVENT_RAISE 13
#define PY_MONITORING_EVENT_PY_THROW 14
#define PY_MONITORING_EVENT_RERAISE 15

#define _PY_MONITORING_IS_UNGROUPED_EVENT(ev) \
((ev) < _PY_MONITORING_UNGROUPED_EVENTS)


/* Ancillary events */

Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ PyObject * _PyEval_ImportNameWithImport(
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate);
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame);
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);
Expand Down
7 changes: 3 additions & 4 deletions Include/internal/pycore_instruments.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,15 @@ PyAPI_DATA(PyObject) _PyInstrumentation_DISABLE;

/* Total tool ids available */
#define PY_MONITORING_TOOL_IDS 8
/* Count of all local monitoring events */
#define _PY_MONITORING_LOCAL_EVENTS 11
/* Count of all "real" monitoring events (not derived from other events) */
/* Count of all "real" monitoring events (not derived from other events).
* All ungrouped events are now local events. */
#define _PY_MONITORING_UNGROUPED_EVENTS 16
/* Count of all monitoring events */
#define _PY_MONITORING_EVENTS 19

/* Tables of which tools are active for each monitored event. */
typedef struct _Py_LocalMonitors {
uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
} _Py_LocalMonitors;

typedef struct _Py_GlobalMonitors {
Expand Down
Loading
Loading