Skip to content

Commit

Permalink
GH-122548: Implement branch taken and not taken events for sys.monito…
Browse files Browse the repository at this point in the history
…ring (GH-122564)
  • Loading branch information
markshannon authored Dec 19, 2024
1 parent 7b811d0 commit d2f1d91
Show file tree
Hide file tree
Showing 29 changed files with 998 additions and 583 deletions.
12 changes: 9 additions & 3 deletions Doc/c-api/monitoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,14 @@ See :mod:`sys.monitoring` for descriptions of the events.
Fire a ``JUMP`` event.
.. c:function:: int PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
.. c:function:: int PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
Fire a ``BRANCH`` event.
Fire a ``BRANCH_LEFT`` event.
.. c:function:: int PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
Fire a ``BRANCH_RIGHT`` event.
.. c:function:: int PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval)
Expand Down Expand Up @@ -168,7 +173,8 @@ would typically correspond to a python function.
================================================== =====================================
Macro Event
================================================== =====================================
.. c:macro:: PY_MONITORING_EVENT_BRANCH :monitoring-event:`BRANCH`
.. c:macro:: PY_MONITORING_EVENT_BRANCH_LEFT :monitoring-event:`BRANCH_LEFT`
.. c:macro:: PY_MONITORING_EVENT_BRANCH_RIGHT :monitoring-event:`BRANCH_RIGHT`
.. c:macro:: PY_MONITORING_EVENT_CALL :monitoring-event:`CALL`
.. c:macro:: PY_MONITORING_EVENT_C_RAISE :monitoring-event:`C_RAISE`
.. c:macro:: PY_MONITORING_EVENT_C_RETURN :monitoring-event:`C_RETURN`
Expand Down
29 changes: 23 additions & 6 deletions Doc/library/sys.monitoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,17 @@ Events

The following events are supported:

.. monitoring-event:: BRANCH
.. monitoring-event:: BRANCH_LEFT

A conditional branch is taken (or not).
A conditional branch goes left.

It is up to the tool to determine how to present "left" and "right" branches.
There is no guarantee which branch is "left" and which is "right", except
that it will be consistent for the duration of the program.

.. monitoring-event:: BRANCH_RIGHT

A conditional branch goes right.

.. monitoring-event:: CALL

Expand Down Expand Up @@ -180,9 +188,20 @@ The local events are:
* :monitoring-event:`LINE`
* :monitoring-event:`INSTRUCTION`
* :monitoring-event:`JUMP`
* :monitoring-event:`BRANCH`
* :monitoring-event:`BRANCH_LEFT`
* :monitoring-event:`BRANCH_RIGHT`
* :monitoring-event:`STOP_ITERATION`

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

* ``BRANCH``

The ``BRANCH`` event is deprecated in 3.14.
Using :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT`
events will give much better performance as they can be disabled
independently.

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

Expand Down Expand Up @@ -357,13 +376,11 @@ Different events will provide the callback function with different arguments, as

func(code: CodeType, line_number: int) -> DISABLE | Any

* :monitoring-event:`BRANCH` and :monitoring-event:`JUMP`::
* :monitoring-event:`BRANCH_LEFT`, :monitoring-event:`BRANCH_RIGHT` and :monitoring-event:`JUMP`::

func(code: CodeType, instruction_offset: int, destination_offset: int) -> DISABLE | Any

Note that the *destination_offset* is where the code will next execute.
For an untaken branch this will be the offset of the instruction following
the branch.

* :monitoring-event:`INSTRUCTION`::

Expand Down
2 changes: 1 addition & 1 deletion Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1971,7 +1971,7 @@ New Features
* :c:func:`PyMonitoring_FireCallEvent`
* :c:func:`PyMonitoring_FireLineEvent`
* :c:func:`PyMonitoring_FireJumpEvent`
* :c:func:`PyMonitoring_FireBranchEvent`
* ``PyMonitoring_FireBranchEvent``
* :c:func:`PyMonitoring_FireCReturnEvent`
* :c:func:`PyMonitoring_FirePyThrowEvent`
* :c:func:`PyMonitoring_FireRaiseEvent`
Expand Down
14 changes: 14 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,11 @@ sys
which only exists in specialized builds of Python, may now return objects
from other interpreters than the one it's called in.

sys.monitoring
--------------

Two new events are added: :monitoring-event:`BRANCH_LEFT` and
:monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated.

tkinter
-------
Expand Down Expand Up @@ -1144,6 +1149,11 @@ New features
a :exc:`UnicodeError` object.
(Contributed by Bénédikt Tran in :gh:`127691`.)

* Add :c:func:`PyMonitoring_FireBranchLeftEvent` and
:c:func:`PyMonitoring_FireBranchRightEvent` for generating
:monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT`
events, respectively.


Porting to Python 3.14
----------------------
Expand Down Expand Up @@ -1177,6 +1187,10 @@ Deprecated

.. include:: ../deprecations/c-api-pending-removal-in-future.rst

* The ``PyMonitoring_FireBranchEvent`` function is deprecated and should
be replaced with calls to :c:func:`PyMonitoring_FireBranchLeftEvent`
and :c:func:`PyMonitoring_FireBranchRightEvent`.

Removed
-------

Expand Down
6 changes: 3 additions & 3 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ extern "C" {
/* Total tool ids available */
#define _PY_MONITORING_TOOL_IDS 8
/* Count of all local monitoring events */
#define _PY_MONITORING_LOCAL_EVENTS 10
#define _PY_MONITORING_LOCAL_EVENTS 11
/* Count of all "real" monitoring events (not derived from other events) */
#define _PY_MONITORING_UNGROUPED_EVENTS 15
#define _PY_MONITORING_UNGROUPED_EVENTS 16
/* Count of all monitoring events */
#define _PY_MONITORING_EVENTS 17
#define _PY_MONITORING_EVENTS 19

/* Tables of which tools are active for each monitored event. */
typedef struct _Py_LocalMonitors {
Expand Down
43 changes: 31 additions & 12 deletions Include/cpython/monitoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,27 @@
#define PY_MONITORING_EVENT_LINE 5
#define PY_MONITORING_EVENT_INSTRUCTION 6
#define PY_MONITORING_EVENT_JUMP 7
#define PY_MONITORING_EVENT_BRANCH 8
#define PY_MONITORING_EVENT_STOP_ITERATION 9
#define PY_MONITORING_EVENT_BRANCH_LEFT 8
#define PY_MONITORING_EVENT_BRANCH_RIGHT 9
#define PY_MONITORING_EVENT_STOP_ITERATION 10

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

/* Other events, mainly exceptions */

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


/* Ancillary events */

#define PY_MONITORING_EVENT_C_RETURN 15
#define PY_MONITORING_EVENT_C_RAISE 16
#define PY_MONITORING_EVENT_C_RETURN 16
#define PY_MONITORING_EVENT_C_RAISE 17
#define PY_MONITORING_EVENT_BRANCH 18


typedef struct _PyMonitoringState {
Expand Down Expand Up @@ -74,10 +76,18 @@ PyAPI_FUNC(int)
_PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset);

PyAPI_FUNC(int)
Py_DEPRECATED(3.14) PyAPI_FUNC(int)
_PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset);

PyAPI_FUNC(int)
_PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset);

PyAPI_FUNC(int)
_PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset);

PyAPI_FUNC(int)
_PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *retval);
Expand Down Expand Up @@ -174,12 +184,21 @@ PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t
}

static inline int
PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{
_PYMONITORING_IF_ACTIVE(
state,
_PyMonitoring_FireBranchRightEvent(state, codelike, offset, target_offset));
}

static inline int
PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset,
PyObject *target_offset)
{
_PYMONITORING_IF_ACTIVE(
state,
_PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset));
_PyMonitoring_FireBranchLeftEvent(state, codelike, offset, target_offset));
}

static inline int
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ extern _Py_CODEUNIT _Py_GetBaseCodeUnit(PyCodeObject *code, int offset);

extern int _PyInstruction_GetLength(PyCodeObject *code, int offset);

extern PyObject *_PyInstrumentation_BranchesIterator(PyCodeObject *code);

struct _PyCode8 _PyCode_DEF(8);

PyAPI_DATA(const struct _PyCode8) _Py_InitCleanup;
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_magic_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ Known values:
Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE)
Python 3.14a1 3608 (Add support for slices)
Python 3.14a2 3609 (Add LOAD_SMALL_INT and LOAD_CONST_IMMORTAL instructions, remove RETURN_CONST)
Python 3.14a3 3610 (Add NOT_TAKEN instruction)
Python 3.15 will start with 3650
Expand All @@ -274,7 +275,7 @@ PC/launcher.c must also be updated.
*/

#define PYC_MAGIC_NUMBER 3609
#define PYC_MAGIC_NUMBER 3611
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
Expand Down
25 changes: 23 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Include/internal/pycore_opcode_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ extern "C" {
(opcode) == JUMP_BACKWARD || \
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)

#define IS_CONDITIONAL_JUMP_OPCODE(opcode) \
((opcode) == POP_JUMP_IF_FALSE || \
(opcode) == POP_JUMP_IF_TRUE || \
(opcode) == POP_JUMP_IF_NONE || \
(opcode) == POP_JUMP_IF_NOT_NONE)

#define IS_SCOPE_EXIT_OPCODE(opcode) \
((opcode) == RETURN_VALUE || \
(opcode) == RAISE_VARARGS || \
Expand Down
Loading

0 comments on commit d2f1d91

Please sign in to comment.