Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
keep local vs other separation
  • Loading branch information
P403n1x87 committed Apr 18, 2026
commit 7761135cbcc40abb879575a1ccc4f32a00a35276
75 changes: 36 additions & 39 deletions Doc/library/sys.monitoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,9 @@ events, use the expression ``PY_RETURN | PY_START``.
Local events
''''''''''''

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:
Local events are associated with normal execution of the program and happen
at clearly defined locations. All local events can be disabled
per location. The local events are:

* :monitoring-event:`PY_START`
* :monitoring-event:`PY_RESUME`
Expand All @@ -200,24 +195,6 @@ code location only, leaving all other locations unaffected:
* :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 Down Expand Up @@ -245,6 +222,28 @@ 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 per location.

The other events that can be monitored are:

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

.. versionchanged:: 3.15
Other events can now be turned on and disabled on a per code object
basis. Returning :data:`DISABLE` from a callback disables the event
for the entire code object (for the current tool).


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

Expand Down Expand Up @@ -315,23 +314,21 @@ Disabling events
.. data:: DISABLE

A special value that can be returned from a callback function to disable
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.
events for the current code location.

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.
: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.

For :ref:`non-instrumented local events <monitoring-event-local>`,
:data:`DISABLE` disables the event for the entire code object (for the current
:ref:`Other events <monitoring-event-global>` can be disabled on a per code
object basis by returning :data:`sys.monitoring.DISABLE` from a callback
function. This 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.
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.

.. function:: restart_events() -> None

Expand Down
7 changes: 5 additions & 2 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 (tracked per code object).
* Events 0-10 require bytecode instrumentation; 11-15 do not. */
/* Local events.
* These require bytecode instrumentation */

#define PY_MONITORING_EVENT_PY_START 0
#define PY_MONITORING_EVENT_PY_RESUME 1
Expand All @@ -26,6 +26,9 @@ extern "C" {
#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)

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

#define PY_MONITORING_EVENT_PY_UNWIND 11
#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 12
#define PY_MONITORING_EVENT_RAISE 13
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_instruments.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ PyAPI_DATA(PyObject) _PyInstrumentation_DISABLE;
/* Total tool ids available */
#define PY_MONITORING_TOOL_IDS 8
/* Count of all "real" monitoring events (not derived from other events).
* All ungrouped events are now local events. */
* "Other" events can now be turned on/disabled per code object. */
#define _PY_MONITORING_UNGROUPED_EVENTS 16
/* Count of all monitoring events */
#define _PY_MONITORING_EVENTS 19
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
Make all the low-impact monitoring API events local on a per-code object
basis. The sentinel ``DISABLE`` value can be returned to disable them for
the whole code object. The behavior of events that were already local (via
bytecode instrumentation) hsa not been changed.
Global :mod:`sys.monitoring` events can now be turned on and disabled on a
per code object basis. Returning ``DISABLE`` from a callback disables the
event for the entire code object (for the current tool).
7 changes: 3 additions & 4 deletions Python/instrumentation.c
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i,
}
}
else {
/* Non-instrumented local events are not tied to specific instructions;
/* Other (non-instrumented) events are not tied to specific instructions;
* use the code-object-level active_monitors bitmap instead. */
tools = code->_co_monitoring->active_monitors.tools[event];
Comment thread
P403n1x87 marked this conversation as resolved.
}
Expand Down Expand Up @@ -1143,7 +1143,7 @@ static const char *const event_names [] = {
[PY_MONITORING_EVENT_STOP_ITERATION] = "STOP_ITERATION",
};

/* Disable a local-but-not-instrumented event (e.g. PY_UNWIND) for a single
/* Disable an "other" (non-instrumented) event (e.g. PY_UNWIND) for a single
* tool on this code object. Must be called with the world stopped or the
* code lock held. */
static void
Expand Down Expand Up @@ -1212,8 +1212,7 @@ call_instrumentation_vector(
_PyEval_StartTheWorld(interp);
}
else if (_PY_MONITORING_IS_UNGROUPED_EVENT(event)) {
/* Non-instrumented local event: disable for this code object
* entirely. */
/* Other (non-instrumented) event: disable for this code object. */
_PyEval_StopTheWorld(interp);
remove_local_tool(code, interp, event, tool);
_PyEval_StartTheWorld(interp);
Expand Down
Loading