2021-12-07 10:09:44 -05:00
|
|
|
PEP: 669
|
|
|
|
Title: Low Impact Monitoring for CPython
|
|
|
|
Author: Mark Shannon <mark@hotpy.org>
|
|
|
|
Status: Draft
|
|
|
|
Type: Standards Track
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
Created: 18-Aug-2021
|
2022-03-09 11:04:44 -05:00
|
|
|
Post-History: 07-Dec-2021
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
========
|
|
|
|
|
|
|
|
Using a profiler or debugger in CPython can have a severe impact on
|
|
|
|
performance. Slowdowns by an order of magnitude are common.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
This PEP proposes an API for monitoring Python programs running
|
2021-12-07 10:09:44 -05:00
|
|
|
on CPython that will enable monitoring at low cost.
|
|
|
|
|
|
|
|
Although this PEP does not specify an implementation, it is expected that
|
2022-01-21 06:03:51 -05:00
|
|
|
it will be implemented using the quickening step of
|
2022-08-04 10:04:26 -04:00
|
|
|
:pep:`659`.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2021-12-10 08:17:58 -05:00
|
|
|
A ``sys.monitoring`` namespace will be added, which will contain
|
|
|
|
the relevant functions and enum.
|
|
|
|
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Motivation
|
|
|
|
==========
|
|
|
|
|
|
|
|
Developers should not have to pay an unreasonable cost to use debuggers,
|
|
|
|
profilers and other similar tools.
|
|
|
|
|
|
|
|
C++ and Java developers expect to be able to run a program at full speed
|
|
|
|
(or very close to it) under a debugger.
|
|
|
|
Python developers should expect that too.
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
=========
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
The quickening mechanism provided by :pep:`659` provides a way to dynamically
|
2021-12-07 10:09:44 -05:00
|
|
|
modify executing Python bytecode. These modifications have little cost beyond
|
|
|
|
the parts of the code that are modified and a relatively low cost to those
|
|
|
|
parts that are modified. We can leverage this to provide an efficient
|
|
|
|
mechanism for monitoring that was not possible in 3.10 or earlier.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
By using quickening, we expect that code run under a debugger on 3.12
|
|
|
|
should outperform code run without a debugger on 3.11.
|
|
|
|
Profiling will still slow down execution, but by much less than in 3.11.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
=============
|
|
|
|
|
|
|
|
Monitoring of Python programs is done by registering callback functions
|
|
|
|
for events and by activating a set of events.
|
|
|
|
|
|
|
|
Activating events and registering callback functions are independent of each other.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
Both registering callbacks and activating events are done on a per-tool basis.
|
|
|
|
It is possible to have multiple tools that respond to different sets of events.
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Events
|
|
|
|
------
|
|
|
|
|
|
|
|
As a code object executes various events occur that might be of interest
|
|
|
|
to tools. By activating events and by registering callback functions
|
|
|
|
tools can respond to these events in any way that suits them.
|
|
|
|
Events can be set globally, or for individual code objects.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
For 3.12, CPython will support the following events:
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* PY_START: Start of a Python function (occurs immediately after the call, the callee's frame will be on the stack)
|
2021-12-07 10:09:44 -05:00
|
|
|
* PY_RESUME: Resumption of a Python function (for generator and coroutine functions), except for throw() calls.
|
|
|
|
* PY_THROW: A Python function is resumed by a throw() call.
|
|
|
|
* PY_RETURN: Return from a Python function (occurs immediately before the return, the callee's frame will be on the stack).
|
|
|
|
* PY_YIELD: Yield from a Python function (occurs immediately before the yield, the callee's frame will be on the stack).
|
|
|
|
* PY_UNWIND: Exit from a Python function during exception unwinding.
|
2022-08-04 10:04:26 -04:00
|
|
|
* C_CALL: Call to any callable, except Python functions (before the call in this case).
|
|
|
|
* C_RETURN: Return from any callable, except Python functions (after the return in this case).
|
2021-12-07 10:09:44 -05:00
|
|
|
* RAISE: An exception is raised.
|
|
|
|
* EXCEPTION_HANDLED: An exception is handled.
|
|
|
|
* LINE: An instruction is about to be executed that has a different line number from the preceding instruction.
|
|
|
|
* INSTRUCTION -- A VM instruction is about to be executed.
|
|
|
|
* JUMP -- An unconditional jump in the control flow graph is reached.
|
|
|
|
* BRANCH -- A conditional branch is about to be taken (or not).
|
|
|
|
* MARKER -- A marker is hit
|
|
|
|
|
|
|
|
More events may be added in the future.
|
|
|
|
|
2021-12-10 08:17:58 -05:00
|
|
|
All events will be attributes of the ``Event`` enum in ``sys.monitoring``::
|
|
|
|
|
|
|
|
class Event(enum.IntFlag):
|
|
|
|
PY_CALL = ...
|
|
|
|
|
|
|
|
Note that ``Event`` is an ``IntFlag`` which means that the events can be or-ed
|
|
|
|
together to form a set of events.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
Tool identifiers
|
|
|
|
----------------
|
|
|
|
|
|
|
|
The VM can support up to 6 tools at once.
|
|
|
|
Before registering or activating events, a tool should choose an identifier.
|
|
|
|
Identifiers are integers in the range 0 to 5.
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
sys.monitoring.use_tool_id(id)->None
|
|
|
|
sys.monitoring.free_tool_id(id)->None
|
|
|
|
|
|
|
|
``sys.monitoring.use_tool_id`` raises a ``ValueError`` if ``id`` is in use.
|
|
|
|
|
|
|
|
All IDs are treated the same by the VM with regard to events, but the following
|
|
|
|
IDs are pre-defined to make co-operation of tools easier::
|
|
|
|
|
|
|
|
sys.monitoring.DEBUGGER_ID = 0
|
|
|
|
sys.monitoring.COVERAGE_ID = 1
|
|
|
|
sys.monitoring.PROFILER_ID = 2
|
|
|
|
sys.monitoring.OPTIMIZER_ID = 3
|
|
|
|
|
|
|
|
There is no obligation to set an ID, nor is there anything preventing a tool from
|
|
|
|
using an ID even it is already in use.
|
|
|
|
However, tool are encouraged to use a unique ID and respect other tools.
|
|
|
|
|
|
|
|
For example, if a debugger were attached and ``DEBUGGER_ID`` were in use, it should
|
|
|
|
report an error, rather than carrying on regardless.
|
|
|
|
|
|
|
|
The ``OPTIMIZER_ID`` is provided for tools like Cinder or PyTorch
|
|
|
|
that want to optimize Python code, but need to decide what to
|
|
|
|
optimize in a way that depends on some wider context.
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Setting events globally
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
Events can be controlled globally by modifying the set of events being monitored:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``sys.monitoring.get_events(tool_id:int)->Event``
|
2021-12-10 08:17:58 -05:00
|
|
|
Returns the ``Event`` set for all the active events.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``sys.monitoring.set_events(tool_id:int, event_set: Event)``
|
2021-12-07 10:09:44 -05:00
|
|
|
Activates all events which are set in ``event_set``.
|
|
|
|
|
|
|
|
No events are active by default.
|
|
|
|
|
|
|
|
Per code object events
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
Events can also be controlled on a per code object basis:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``sys.monitoring.get_local_events(tool_id:int, code: CodeType)->Event``
|
2021-12-10 08:17:58 -05:00
|
|
|
Returns the ``Event`` set for all the local events for ``code``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``sys.monitoring.set_local_events(tool_id:int, code: CodeType, event_set: Event)``
|
2021-12-10 08:17:58 -05:00
|
|
|
Activates all the local events for ``code`` which are set in ``event_set``.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Local events add to global events, but do not mask them.
|
|
|
|
In other words, all global events will trigger for a code object, regardless of the local events.
|
|
|
|
|
|
|
|
|
|
|
|
Register callback functions
|
|
|
|
---------------------------
|
|
|
|
|
|
|
|
To register a callable for events call::
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
sys.monitoring.register_callback(tool_id:int, event: Event, func: Callable | None)
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-01-17 11:22:49 -05:00
|
|
|
``register_callback`` returns the previously registered callback, or ``None``.
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Functions can be unregistered by calling
|
2022-08-04 10:04:26 -04:00
|
|
|
``sys.monitoring.register_callback(tool_id, event, None)``.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Callback functions can be registered and unregistered at any time.
|
|
|
|
|
|
|
|
Registering a callback function will generate a ``sys.audit`` event.
|
|
|
|
|
|
|
|
Callback function arguments
|
|
|
|
'''''''''''''''''''''''''''
|
|
|
|
|
|
|
|
When an active event occurs, the registered callback function is called.
|
|
|
|
Different events will provide the callback function with different arguments, as follows:
|
|
|
|
|
|
|
|
* All events starting with ``PY_``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
* ``C_CALL`` and ``C_RETURN``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int, callable: object) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
* ``RAISE`` and ``EXCEPTION_HANDLED``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int, exception: BaseException) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
* ``LINE``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, line_number: int) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``BRANCH``:
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int, destination_offset: int) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
* ``INSTRUCTION``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int) -> DISABLE | Any``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
* ``MARKER``:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``func(code: CodeType, instruction_offset: int) -> DISABLE | Any``
|
|
|
|
|
|
|
|
If a callback returns ``sys.monitoring.DISABLE`` then that tool will not
|
|
|
|
recieve any more events for that ``(code, instruction_offset)``.
|
|
|
|
|
|
|
|
This feature is provided for coverage and other tools that are only interested
|
|
|
|
seeing an event once.
|
|
|
|
|
|
|
|
Tools may see events after returning ``DISABLE``, in which case, they will not see
|
|
|
|
those events until ``sys.monitoring.restart_events()`` is called.
|
|
|
|
Note that ``sys.monitoring.restart_events()`` is not specific to one tool,
|
|
|
|
so tools must be prepared to recieve events that they have chosen to DISABLE.
|
|
|
|
|
|
|
|
Events in callback functions
|
|
|
|
----------------------------
|
|
|
|
|
|
|
|
Events are suspended in callback functions and their callees for the tool
|
|
|
|
that registered that callback.
|
|
|
|
|
|
|
|
That means that other tools will see events in the callback functions for other
|
|
|
|
tools. This could be useful for debugging a profiling tool, but would produce
|
|
|
|
misleading profiles, as the debugger tool would show up in the profile.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Inserting and removing markers
|
2022-08-04 10:04:26 -04:00
|
|
|
------------------------------
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Two new functions are added to the ``sys`` module to support markers.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``sys.monitoring.insert_marker(tool_id: int, code: CodeType, offset: int)``
|
|
|
|
* ``sys.monitoring.remove_marker(tool_id: int, code: CodeType, offset: int)``
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
A single code object may not have more than 255 markers at once.
|
|
|
|
``sys.monitoring.insert_marker`` raises a ``ValueError`` if this limit
|
|
|
|
is exceeded.
|
|
|
|
|
|
|
|
Order of events
|
|
|
|
---------------
|
|
|
|
|
|
|
|
If an instructions triggers several events the occur in the following order:
|
|
|
|
|
|
|
|
* MARKER
|
|
|
|
* INSTRUCTION
|
|
|
|
* LINE
|
|
|
|
* All other events (only one of these event can occur per instruction)
|
|
|
|
|
|
|
|
Each event is delivered to tools in ascending order of ID.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2021-12-10 08:17:58 -05:00
|
|
|
Attributes of the ``sys.monitoring`` namespace
|
2022-08-04 10:04:26 -04:00
|
|
|
----------------------------------------------
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2021-12-10 08:17:58 -05:00
|
|
|
* ``class Event(enum.IntFlag)``
|
2022-08-04 10:04:26 -04:00
|
|
|
* ``def use_tool_id(id)->None``
|
|
|
|
* ``def free_tool_id(id)->None``
|
|
|
|
* ``def get_events(tool_id: int)->Event``
|
|
|
|
* ``def set_events(tool_id: int, event_set: Event)->None``
|
|
|
|
* ``def get_local_events(tool_id: int, code: CodeType)->Event``
|
|
|
|
* ``def set_local_events(tool_id: int, code: CodeType, event_set: Event)->None``
|
|
|
|
* ``def register_callback(tool_id: int, event: Event, func: Callable)->Optional[Callable]``
|
|
|
|
* ``def insert_marker(tool_id: int, code: CodeType, offset: Event)->None``
|
|
|
|
* ``def remove_marker(tool_id: int, code: CodeType, offset: Event)->None``
|
|
|
|
* ``def restart_events()->None``
|
|
|
|
* ``DISABLE: object``
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Backwards Compatibility
|
|
|
|
=======================
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
This PEP is mostly backwards compatible.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
This PEP is incompatible with :pep:`523` as the behavior would be undefined,
|
|
|
|
as we have no control over the behavior of :pep:`523` plugins.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
Thus, if :pep:`523` is in use, then calling ``sys.monitoring.set_events()`` or
|
2021-12-10 08:17:58 -05:00
|
|
|
``sys.monitoring.set_local_events()`` will raise an exception.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2021-12-10 08:17:58 -05:00
|
|
|
Likewise, if ``sys.monitoring.set_events()`` or
|
2022-01-21 06:03:51 -05:00
|
|
|
``sys.monitoring.set_local_events()`` has been called, then using :pep:`523`
|
2022-08-04 10:04:26 -04:00
|
|
|
will raise an exception.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
``sys.settrace`` and ``sys.setprofile`` will act as if they were tools 6 and 7
|
|
|
|
respectively, so can be used along side this PEP.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
This makes ``sys.settrace`` and ``sys.setprofile`` incompatible with :pep:`523`.
|
|
|
|
Arguably, they already were as the author know of any PEP 523 plugin that support
|
|
|
|
``sys.settrace`` or ``sys.setprofile`` correctly. This PEP merely formalizes that.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Performance
|
|
|
|
-----------
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
If no events are active, this PEP should have a small positive impact on
|
|
|
|
performance. Experiments show between 1 and 2% speedup from not supporting
|
|
|
|
``sys.settrace()`` directly.
|
|
|
|
|
|
|
|
The performance of ``sys.settrace()`` will be worse.
|
|
|
|
The performance of ``sys.setprofile()`` should be better.
|
|
|
|
However, by the tools relying on ``sys.settrace()`` and ``sys.setprofile()``
|
|
|
|
can be made a lot faster by using the API provided by this PEP.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
If a small set of events are active, e.g. for a debugger, then the overhead
|
|
|
|
of callbacks will be orders of magnitudes less than for ``sys.settrace`` and
|
2022-01-21 06:03:51 -05:00
|
|
|
much cheaper than using :pep:`523`.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
Coverage tools can be implemented at very low cost,
|
|
|
|
by returning ``DISABLE`` in all callbacks.
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
For heavily instrumented code, e.g. using ``LINE``, performance should be
|
|
|
|
better than ``sys.settrace``, but not by that much as performance will be
|
|
|
|
dominated by the time spent in callbacks.
|
|
|
|
|
|
|
|
For optimizing virtual machines, such as future versions of CPython
|
2022-08-04 10:04:26 -04:00
|
|
|
(and ``PyPy`` should they choose to support this API), changes to the set
|
|
|
|
active events in the midst of a long running program could be quite
|
2021-12-07 10:09:44 -05:00
|
|
|
expensive, possibly taking hundreds of milliseconds as it triggers
|
|
|
|
de-optimizations. Once such de-optimization has occurred, performance should
|
|
|
|
recover as the VM can re-optimize the instrumented code.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
In general these operations can be considered to be fast:
|
|
|
|
|
|
|
|
* ``def get_events(tool_id: int)->Event``
|
|
|
|
* ``def get_local_events(tool_id: int, code: CodeType)->Event``
|
|
|
|
* ``def register_callback(tool_id: int, event: Event, func: Callable)->Optional[Callable]``
|
|
|
|
|
|
|
|
These operations are slower, but not especially so:
|
|
|
|
|
|
|
|
* ``def set_local_events(tool_id: int, code: CodeType, event_set: Event)->None``
|
|
|
|
* ``def insert_marker(tool_id: int, code: CodeType, offset: Event)->None``
|
|
|
|
* ``def remove_marker(tool_id: int, code: CodeType, offset: Event)->None``
|
|
|
|
|
|
|
|
And these operations should be regarded as slow:
|
|
|
|
|
|
|
|
* ``def use_tool_id(id)->None``
|
|
|
|
* ``def free_tool_id(id)->None``
|
|
|
|
* ``def set_events(tool_id: int, event_set: Event)->None``
|
|
|
|
* ``def restart_events()->None``
|
|
|
|
|
|
|
|
How slow, the slow operation the operations are, depends on when then happen.
|
|
|
|
If done early in the program, before modules are loaded,
|
|
|
|
they should be fairly inexpensive.
|
|
|
|
|
|
|
|
Memory Consumption
|
|
|
|
''''''''''''''''''
|
|
|
|
|
|
|
|
When not in use, this PEP will have a neglible change on memory consumption.
|
|
|
|
|
|
|
|
How memory is used is very much an implementation detail.
|
|
|
|
However, we expect that for 3.12 the additional memory consumption per
|
|
|
|
code object will be **roughly** as follows:
|
|
|
|
|
|
|
|
+-------------+--------+--------+-------------+
|
|
|
|
| | Events |
|
|
|
|
+-------------+--------+--------+-------------+
|
|
|
|
| Tools | Others | LINE | INSTRUCTION |
|
|
|
|
+=============+========+========+=============+
|
|
|
|
| One | None | ≈40% | ≈80% |
|
|
|
|
+-------------+--------+--------+-------------+
|
|
|
|
+ Two or more | ≈40% | ≈120% | ≈200% |
|
|
|
|
+-------------+--------+--------+-------------+
|
|
|
|
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Security Implications
|
|
|
|
=====================
|
|
|
|
|
|
|
|
Allowing modification of running code has some security implications,
|
|
|
|
but no more than the ability to generate and call new code.
|
|
|
|
|
|
|
|
All the new functions listed above will trigger audit hooks.
|
|
|
|
|
|
|
|
Implementation
|
|
|
|
==============
|
|
|
|
|
2022-08-05 08:09:54 -04:00
|
|
|
This outlines the proposed implementation for CPython 3.12. The actual
|
2021-12-07 10:09:44 -05:00
|
|
|
implementation for later versions of CPython and other Python implementations
|
|
|
|
may differ considerably.
|
|
|
|
|
|
|
|
The proposed implementation of this PEP will be built on top of the quickening
|
2022-01-21 06:03:51 -05:00
|
|
|
step of :pep:`PEP 659 <659#quickening>`.
|
|
|
|
Activating some events will cause all code objects to
|
2021-12-07 10:09:44 -05:00
|
|
|
be quickened before they are executed.
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
For example, if the ``C_CALL`` event is turned on,
|
|
|
|
then all call instructions will be
|
|
|
|
replaced with a ``INSTRUMENTED_CALL`` instruction.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
Note that this will interfere with specialization, which will result in some
|
|
|
|
performance degradation in addition to the overhead of calling the
|
|
|
|
registered callable.
|
|
|
|
|
|
|
|
When the set of active events changes, the VM will immediately update
|
|
|
|
all code objects present on the call stack of any thread. It will also set in
|
|
|
|
place traps to ensure that all code objects are correctly instrumented when
|
|
|
|
called. Consequently changing the set of active events should be done as
|
|
|
|
infrequently as possible, as it could be quite an expensive operation.
|
|
|
|
|
|
|
|
Other events, such as ``RAISE`` can be turned on or off cheaply,
|
|
|
|
as they do not rely on code instrumentation, but runtime checks when the
|
|
|
|
underlying event occurs.
|
|
|
|
|
|
|
|
The exact set of events that require instrumentation is an implementation detail,
|
|
|
|
but for the current design, the following events will require instrumentation:
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
* PY_START
|
2021-12-07 10:09:44 -05:00
|
|
|
* PY_RESUME
|
|
|
|
* PY_RETURN
|
|
|
|
* PY_YIELD
|
|
|
|
* C_CALL
|
|
|
|
* C_RETURN
|
|
|
|
* LINE
|
|
|
|
* INSTRUCTION
|
|
|
|
* JUMP
|
|
|
|
* BRANCH
|
|
|
|
|
2022-08-04 10:04:26 -04:00
|
|
|
Each instrumented bytecode will require an additional 8 bits of information to
|
|
|
|
note which tool the instrumentation applies to.
|
|
|
|
``LINE`` and ``INSTRUCTION`` events require additional information, as they
|
|
|
|
need to store the original instruction, or even the instrumented instruction
|
|
|
|
if they overlap other instrumentation.
|
|
|
|
|
|
|
|
|
2021-12-07 10:09:44 -05:00
|
|
|
Implementing tools
|
|
|
|
==================
|
|
|
|
|
|
|
|
It is the philosophy of this PEP that it should be possible for third-party monitoring
|
|
|
|
tools to achieve high-performance, not that it should be easy for them to do so.
|
|
|
|
|
|
|
|
Converting events into data that is meaningful to the users is
|
|
|
|
the responsibility of the tool.
|
|
|
|
|
|
|
|
All events have a cost, and tools should attempt to the use set of events
|
|
|
|
that trigger the least often and still provide the necessary information.
|
|
|
|
|
|
|
|
Debuggers
|
|
|
|
---------
|
|
|
|
|
|
|
|
Inserting breakpoints
|
|
|
|
'''''''''''''''''''''
|
|
|
|
|
|
|
|
Breakpoints can be inserted by using markers. For example::
|
|
|
|
|
|
|
|
sys.insert_marker(code, offset)
|
|
|
|
|
|
|
|
Which will insert a marker at ``offset`` in ``code``,
|
|
|
|
which can be used as a breakpoint.
|
|
|
|
|
|
|
|
To insert a breakpoint at a given line, the matching instruction offsets
|
|
|
|
should be found from ``code.co_lines()``.
|
|
|
|
|
|
|
|
Breakpoints can be removed by removing the marker::
|
|
|
|
|
|
|
|
sys.remove_marker(code, offset)
|
|
|
|
|
|
|
|
Stepping
|
|
|
|
''''''''
|
|
|
|
|
|
|
|
Debuggers usually offer the ability to step execution by a
|
|
|
|
single instruction or line.
|
|
|
|
|
|
|
|
This can be implemented by inserting a new marker at the required
|
|
|
|
offset(s) of the code to be stepped to,
|
|
|
|
and by removing the current marker.
|
|
|
|
|
|
|
|
It is the job of the debugger to compute the relevant offset(s).
|
|
|
|
|
|
|
|
Attaching
|
|
|
|
'''''''''
|
|
|
|
|
|
|
|
Debuggers can use the ``PY_CALL``, etc. events to be informed when
|
|
|
|
a code object is first encountered, so that any necessary breakpoints
|
|
|
|
can be inserted.
|
|
|
|
|
|
|
|
Coverage Tools
|
|
|
|
--------------
|
|
|
|
|
|
|
|
Coverage tools need to track which parts of the control graph have been
|
|
|
|
executed. To do this, they need to register for the ``PY_`` events,
|
|
|
|
plus ``JUMP`` and ``BRANCH``.
|
|
|
|
|
|
|
|
This information can be then be converted back into a line based report
|
|
|
|
after execution has completed.
|
|
|
|
|
|
|
|
Profilers
|
|
|
|
---------
|
|
|
|
|
|
|
|
Simple profilers need to gather information about calls.
|
|
|
|
To do this profilers should register for the following events:
|
|
|
|
|
|
|
|
* PY_CALL
|
|
|
|
* PY_RESUME
|
|
|
|
* PY_THROW
|
|
|
|
* PY_RETURN
|
|
|
|
* PY_YIELD
|
|
|
|
* PY_UNWIND
|
|
|
|
* C_CALL
|
|
|
|
* C_RETURN
|
|
|
|
|
|
|
|
|
|
|
|
Line based profilers
|
|
|
|
''''''''''''''''''''
|
|
|
|
|
|
|
|
Line based profilers can use the ``LINE`` and ``JUMP`` events.
|
|
|
|
Implementers of profilers should be aware that instrumenting ``LINE``
|
2022-08-04 10:04:26 -04:00
|
|
|
events will have a large impact on performance.
|
2021-12-07 10:09:44 -05:00
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
Instrumenting profilers have significant overhead and will distort
|
|
|
|
the results of profiling. Unless you need exact call counts,
|
|
|
|
consider using a statistical profiler.
|
|
|
|
|
|
|
|
|
|
|
|
Rejected ideas
|
|
|
|
==============
|
|
|
|
|
|
|
|
A draft version of this PEP proposed making the user responsible
|
|
|
|
for inserting the monitoring instructions, rather than have VM do it.
|
|
|
|
However, that puts too much of a burden on the tools, and would make
|
|
|
|
attaching a debugger nearly impossible.
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
=========
|
|
|
|
|
|
|
|
This document is placed in the public domain or under the
|
|
|
|
CC0-1.0-Universal license, whichever is more permissive.
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
Local Variables:
|
|
|
|
mode: indented-text
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
sentence-end-double-space: t
|
|
|
|
fill-column: 70
|
|
|
|
coding: utf-8
|
|
|
|
End:
|