742 lines
57 KiB
HTML
742 lines
57 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 669 – Low Impact Monitoring for CPython | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0669/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 669 – Low Impact Monitoring for CPython | peps.python.org'>
|
||
<meta property="og:description" content="Using a profiler or debugger in CPython can have a severe impact on performance. Slowdowns by an order of magnitude are common.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0669/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="Using a profiler or debugger in CPython can have a severe impact on performance. Slowdowns by an order of magnitude are common.">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 669</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 669 – Low Impact Monitoring for CPython</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Mark Shannon <mark at hotpy.org></dd>
|
||
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-669-low-impact-monitoring-for-cpython/13018/">Discourse thread</a></dd>
|
||
<dt class="field-odd">Status<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-even">Type<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-odd">Created<span class="colon">:</span></dt>
|
||
<dd class="field-odd">18-Aug-2021</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.12</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/thread/VNSD4TSAM2BM64FJNIQPAOPNEGNX4MDX/" title="Python-Dev thread">07-Dec-2021</a>,
|
||
<a class="reference external" href="https://discuss.python.org/t/pep-669-low-impact-monitoring-for-cpython/13018" title="Discourse thread">10-Jan-2022</a></dd>
|
||
<dt class="field-even">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-669-low-impact-monitoring-for-cpython/13018/42">Discourse message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#events">Events</a><ul>
|
||
<li><a class="reference internal" href="#local-events">Local events</a></li>
|
||
<li><a class="reference internal" href="#ancilliary-events">Ancilliary events</a></li>
|
||
<li><a class="reference internal" href="#other-events">Other events</a></li>
|
||
<li><a class="reference internal" href="#the-stop-iteration-event">The STOP_ITERATION event</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#tool-identifiers">Tool identifiers</a></li>
|
||
<li><a class="reference internal" href="#setting-events-globally">Setting events globally</a></li>
|
||
<li><a class="reference internal" href="#per-code-object-events">Per code object events</a></li>
|
||
<li><a class="reference internal" href="#register-callback-functions">Register callback functions</a><ul>
|
||
<li><a class="reference internal" href="#callback-function-arguments">Callback function arguments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#events-in-callback-functions">Events in callback functions</a></li>
|
||
<li><a class="reference internal" href="#order-of-events">Order of events</a></li>
|
||
<li><a class="reference internal" href="#the-call-event-group">The “call” event group</a></li>
|
||
<li><a class="reference internal" href="#attributes-of-the-sys-monitoring-namespace">Attributes of the <code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code> namespace</a></li>
|
||
<li><a class="reference internal" href="#access-to-debug-only-features">Access to “debug only” features</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#performance">Performance</a><ul>
|
||
<li><a class="reference internal" href="#memory-consumption">Memory Consumption</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#implementing-tools">Implementing tools</a><ul>
|
||
<li><a class="reference internal" href="#debuggers">Debuggers</a><ul>
|
||
<li><a class="reference internal" href="#inserting-breakpoints">Inserting breakpoints</a></li>
|
||
<li><a class="reference internal" href="#stepping">Stepping</a></li>
|
||
<li><a class="reference internal" href="#attaching">Attaching</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#coverage-tools">Coverage Tools</a></li>
|
||
<li><a class="reference internal" href="#profilers">Profilers</a><ul>
|
||
<li><a class="reference internal" href="#line-based-profilers">Line based profilers</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<div class="pep-banner canonical-doc sticky-banner admonition important">
|
||
<p class="admonition-title">Important</p>
|
||
<p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3/library/sys.monitoring.html#module-sys.monitoring" title="(in Python v3.13)"><code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code></a>.</p>
|
||
<p class="close-button">×</p>
|
||
<p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 – PEP Purpose and Guidelines">PEP 1</a> for how to propose changes.</p>
|
||
</div>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>Using a profiler or debugger in CPython can have a severe impact on
|
||
performance. Slowdowns by an order of magnitude are common.</p>
|
||
<p>This PEP proposes an API for monitoring Python programs running
|
||
on CPython that will enable monitoring at low cost.</p>
|
||
<p>Although this PEP does not specify an implementation, it is expected that
|
||
it will be implemented using the quickening step of
|
||
<a class="pep reference internal" href="../pep-0659/" title="PEP 659 – Specializing Adaptive Interpreter">PEP 659</a>.</p>
|
||
<p>A <code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code> namespace will be added, which will contain
|
||
the relevant functions and constants.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>Developers should not have to pay an unreasonable cost to use debuggers,
|
||
profilers and other similar tools.</p>
|
||
<p>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.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>The quickening mechanism provided by <a class="pep reference internal" href="../pep-0659/" title="PEP 659 – Specializing Adaptive Interpreter">PEP 659</a> provides a way to dynamically
|
||
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.</p>
|
||
<p>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.</p>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>Monitoring of Python programs is done by registering callback functions
|
||
for events and by activating a set of events.</p>
|
||
<p>Activating events and registering callback functions are independent of each other.</p>
|
||
<p>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.</p>
|
||
<p>Note that, unlike <code class="docutils literal notranslate"><span class="pre">sys.settrace()</span></code>, events and callbacks are per interpreter, not per thread.</p>
|
||
<section id="events">
|
||
<h3><a class="toc-backref" href="#events" role="doc-backlink">Events</a></h3>
|
||
<p>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.</p>
|
||
<p>For 3.12, CPython will support the following events:</p>
|
||
<ul class="simple">
|
||
<li>PY_START: Start of a Python function (occurs immediately after the call, the callee’s frame will be on the stack)</li>
|
||
<li>PY_RESUME: Resumption of a Python function (for generator and coroutine functions), except for throw() calls.</li>
|
||
<li>PY_THROW: A Python function is resumed by a throw() call.</li>
|
||
<li>PY_RETURN: Return from a Python function (occurs immediately before the return, the callee’s frame will be on the stack).</li>
|
||
<li>PY_YIELD: Yield from a Python function (occurs immediately before the yield, the callee’s frame will be on the stack).</li>
|
||
<li>PY_UNWIND: Exit from a Python function during exception unwinding.</li>
|
||
<li>CALL: A call in Python code (event occurs before the call).</li>
|
||
<li>C_RETURN: Return from any callable, except Python functions (event occurs after the return).</li>
|
||
<li>C_RAISE: Exception raised from any callable, except Python functions (event occurs after the exit).</li>
|
||
<li>RAISE: An exception is raised, except those that cause a <code class="docutils literal notranslate"><span class="pre">STOP_ITERATION</span></code> event.</li>
|
||
<li>EXCEPTION_HANDLED: An exception is handled.</li>
|
||
<li>LINE: An instruction is about to be executed that has a different line number from the preceding instruction.</li>
|
||
<li>INSTRUCTION – A VM instruction is about to be executed.</li>
|
||
<li>JUMP – An unconditional jump in the control flow graph is made.</li>
|
||
<li>BRANCH – A conditional branch is taken (or not).</li>
|
||
<li>STOP_ITERATION – An artificial <code class="docutils literal notranslate"><span class="pre">StopIteration</span></code> is raised;
|
||
see <a class="reference internal" href="#the-stop-iteration-event">the STOP_ITERATION event</a>.</li>
|
||
</ul>
|
||
<p>More events may be added in the future.</p>
|
||
<p>All events will be attributes of the <code class="docutils literal notranslate"><span class="pre">events</span></code> namespace in <code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code>.
|
||
All events will represented by a power of two integer, so that they can be combined
|
||
with the <code class="docutils literal notranslate"><span class="pre">|</span></code> operator.</p>
|
||
<p>Events are divided into three groups:</p>
|
||
<section id="local-events">
|
||
<h4><a class="toc-backref" href="#local-events" role="doc-backlink">Local events</a></h4>
|
||
<p>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:</p>
|
||
<ul class="simple">
|
||
<li>PY_START</li>
|
||
<li>PY_RESUME</li>
|
||
<li>PY_RETURN</li>
|
||
<li>PY_YIELD</li>
|
||
<li>CALL</li>
|
||
<li>LINE</li>
|
||
<li>INSTRUCTION</li>
|
||
<li>JUMP</li>
|
||
<li>BRANCH</li>
|
||
<li>STOP_ITERATION</li>
|
||
</ul>
|
||
</section>
|
||
<section id="ancilliary-events">
|
||
<h4><a class="toc-backref" href="#ancilliary-events" role="doc-backlink">Ancilliary events</a></h4>
|
||
<p>Ancillary events can be monitored like other events, but are controlled
|
||
by another event:</p>
|
||
<ul class="simple">
|
||
<li>C_RAISE</li>
|
||
<li>C_RETURN</li>
|
||
</ul>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">C_RETURN</span></code> and <code class="docutils literal notranslate"><span class="pre">C_RAISE</span></code> events are are controlled by the <code class="docutils literal notranslate"><span class="pre">CALL</span></code>
|
||
event. <code class="docutils literal notranslate"><span class="pre">C_RETURN</span></code> and <code class="docutils literal notranslate"><span class="pre">C_RAISE</span></code> events will only be seen if the
|
||
corresponding <code class="docutils literal notranslate"><span class="pre">CALL</span></code> event is being monitored.</p>
|
||
</section>
|
||
<section id="other-events">
|
||
<h4><a class="toc-backref" href="#other-events" role="doc-backlink">Other events</a></h4>
|
||
<p>Other events are not necessarily tied to a specific location in the
|
||
program and cannot be individually disabled.</p>
|
||
<p>The other events that can be monitored are:</p>
|
||
<ul class="simple">
|
||
<li>PY_THROW</li>
|
||
<li>PY_UNWIND</li>
|
||
<li>RAISE</li>
|
||
<li>EXCEPTION_HANDLED</li>
|
||
</ul>
|
||
</section>
|
||
<section id="the-stop-iteration-event">
|
||
<h4><a class="toc-backref" href="#the-stop-iteration-event" role="doc-backlink">The STOP_ITERATION event</a></h4>
|
||
<p><a class="pep reference internal" href="../pep-0380/#use-of-stopiteration-to-return-values" title="PEP 380 – Syntax for Delegating to a Subgenerator § Use of StopIteration to return values">PEP 380</a>
|
||
specifies that a <code class="docutils literal notranslate"><span class="pre">StopIteration</span></code> exception is raised when returning a value
|
||
from a generator or coroutine. However, this is a very inefficient way to
|
||
return a value, so some Python implementations, notably CPython 3.12+, do not
|
||
raise an exception unless it would be visible to other code.</p>
|
||
<p>To allow tools to monitor for real exceptions without slowing down generators
|
||
and coroutines, the <code class="docutils literal notranslate"><span class="pre">STOP_ITERATION</span></code> event is provided.
|
||
<code class="docutils literal notranslate"><span class="pre">STOP_ITERATION</span></code> can be locally disabled, unlike <code class="docutils literal notranslate"><span class="pre">RAISE</span></code>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="tool-identifiers">
|
||
<h3><a class="toc-backref" href="#tool-identifiers" role="doc-backlink">Tool identifiers</a></h3>
|
||
<p>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.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">use_tool_id</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">free_tool_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">get_tool</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">sys.monitoring.use_tool_id</span></code> raises a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if <code class="docutils literal notranslate"><span class="pre">id</span></code> is in use.
|
||
<code class="docutils literal notranslate"><span class="pre">sys.monitoring.get_tool</span></code> returns the name of the tool if <code class="docutils literal notranslate"><span class="pre">id</span></code> is in use,
|
||
otherwise it returns <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||
<p>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:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">DEBUGGER_ID</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">COVERAGE_ID</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">PROFILER_ID</span> <span class="o">=</span> <span class="mi">2</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">OPTIMIZER_ID</span> <span class="o">=</span> <span class="mi">5</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>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, tools are encouraged to use a unique ID and respect other tools.</p>
|
||
<p>For example, if a debugger were attached and <code class="docutils literal notranslate"><span class="pre">DEBUGGER_ID</span></code> were in use, it
|
||
should report an error, rather than carrying on regardless.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">OPTIMIZER_ID</span></code> 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.</p>
|
||
</section>
|
||
<section id="setting-events-globally">
|
||
<h3><a class="toc-backref" href="#setting-events-globally" role="doc-backlink">Setting events globally</a></h3>
|
||
<p>Events can be controlled globally by modifying the set of events being monitored:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.monitoring.get_events(tool_id:int)->int</span></code>
|
||
Returns the <code class="docutils literal notranslate"><span class="pre">int</span></code> representing all the active events.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.monitoring.set_events(tool_id:int,</span> <span class="pre">event_set:</span> <span class="pre">int)</span></code>
|
||
Activates all events which are set in <code class="docutils literal notranslate"><span class="pre">event_set</span></code>.
|
||
Raises a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if <code class="docutils literal notranslate"><span class="pre">tool_id</span></code> is not in use.</li>
|
||
</ul>
|
||
<p>No events are active by default.</p>
|
||
</section>
|
||
<section id="per-code-object-events">
|
||
<h3><a class="toc-backref" href="#per-code-object-events" role="doc-backlink">Per code object events</a></h3>
|
||
<p>Events can also be controlled on a per code object basis:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.monitoring.get_local_events(tool_id:int,</span> <span class="pre">code:</span> <span class="pre">CodeType)->int</span></code>
|
||
Returns all the local events for <code class="docutils literal notranslate"><span class="pre">code</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.monitoring.set_local_events(tool_id:int,</span> <span class="pre">code:</span> <span class="pre">CodeType,</span> <span class="pre">event_set:</span> <span class="pre">int)</span></code>
|
||
Activates all the local events for <code class="docutils literal notranslate"><span class="pre">code</span></code> which are set in <code class="docutils literal notranslate"><span class="pre">event_set</span></code>.
|
||
Raises a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if <code class="docutils literal notranslate"><span class="pre">tool_id</span></code> is not in use.</li>
|
||
</ul>
|
||
<p>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.</p>
|
||
</section>
|
||
<section id="register-callback-functions">
|
||
<h3><a class="toc-backref" href="#register-callback-functions" role="doc-backlink">Register callback functions</a></h3>
|
||
<p>To register a callable for events call:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sys</span><span class="o">.</span><span class="n">monitoring</span><span class="o">.</span><span class="n">register_callback</span><span class="p">(</span><span class="n">tool_id</span><span class="p">:</span><span class="nb">int</span><span class="p">,</span> <span class="n">event</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">func</span><span class="p">:</span> <span class="n">Callable</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">Callable</span> <span class="o">|</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If another callback was registered for the given <code class="docutils literal notranslate"><span class="pre">tool_id</span></code> and <code class="docutils literal notranslate"><span class="pre">event</span></code>,
|
||
it is unregistered and returned.
|
||
Otherwise <code class="docutils literal notranslate"><span class="pre">register_callback</span></code> returns <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||
<p>Functions can be unregistered by calling
|
||
<code class="docutils literal notranslate"><span class="pre">sys.monitoring.register_callback(tool_id,</span> <span class="pre">event,</span> <span class="pre">None)</span></code>.</p>
|
||
<p>Callback functions can be registered and unregistered at any time.</p>
|
||
<p>Registering or unregistering a callback function will generate a <code class="docutils literal notranslate"><span class="pre">sys.audit</span></code> event.</p>
|
||
<section id="callback-function-arguments">
|
||
<h4><a class="toc-backref" href="#callback-function-arguments" role="doc-backlink">Callback function arguments</a></h4>
|
||
<p>When an active event occurs, the registered callback function is called.
|
||
Different events will provide the callback function with different arguments, as follows:</p>
|
||
<ul>
|
||
<li><code class="docutils literal notranslate"><span class="pre">PY_START</span></code> and <code class="docutils literal notranslate"><span class="pre">PY_RESUME</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">func</span><span class="p">(</span><span class="n">code</span><span class="p">:</span> <span class="n">CodeType</span><span class="p">,</span> <span class="n">instruction_offset</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">DISABLE</span> <span class="o">|</span> <span class="n">Any</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">PY_RETURN</span></code> and <code class="docutils literal notranslate"><span class="pre">PY_YIELD</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">instruction_offset:</span> <span class="pre">int,</span> <span class="pre">retval:</span> <span class="pre">object)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code></div></blockquote>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">CALL</span></code>, <code class="docutils literal notranslate"><span class="pre">C_RAISE</span></code> and <code class="docutils literal notranslate"><span class="pre">C_RETURN</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">instruction_offset:</span> <span class="pre">int,</span> <span class="pre">callable:</span> <span class="pre">object,</span> <span class="pre">arg0:</span> <span class="pre">object</span> <span class="pre">|</span> <span class="pre">MISSING)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code><p>If there are no arguments, <code class="docutils literal notranslate"><span class="pre">arg0</span></code> is set to <code class="docutils literal notranslate"><span class="pre">MISSING</span></code>.</p>
|
||
</div></blockquote>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">RAISE</span></code> and <code class="docutils literal notranslate"><span class="pre">EXCEPTION_HANDLED</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">instruction_offset:</span> <span class="pre">int,</span> <span class="pre">exception:</span> <span class="pre">BaseException)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code></div></blockquote>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">LINE</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">line_number:</span> <span class="pre">int)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code></div></blockquote>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">BRANCH</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">instruction_offset:</span> <span class="pre">int,</span> <span class="pre">destination_offset:</span> <span class="pre">int)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code></div></blockquote>
|
||
<p>Note that the <code class="docutils literal notranslate"><span class="pre">destination_offset</span></code> is where the code will next execute.
|
||
For an untaken branch this will be the offset of the instruction following
|
||
the branch.</p>
|
||
</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">INSTRUCTION</span></code>:<blockquote>
|
||
<div><code class="docutils literal notranslate"><span class="pre">func(code:</span> <span class="pre">CodeType,</span> <span class="pre">instruction_offset:</span> <span class="pre">int)</span> <span class="pre">-></span> <span class="pre">DISABLE</span> <span class="pre">|</span> <span class="pre">Any</span></code></div></blockquote>
|
||
</li>
|
||
</ul>
|
||
<p>If a callback function returns <code class="docutils literal notranslate"><span class="pre">DISABLE</span></code>, then that function will no longer
|
||
be called for that <code class="docutils literal notranslate"><span class="pre">(code,</span> <span class="pre">instruction_offset)</span></code> until
|
||
<code class="docutils literal notranslate"><span class="pre">sys.monitoring.restart_events()</span></code> is called.
|
||
This feature is provided for coverage and other tools that are only interested
|
||
seeing an event once.</p>
|
||
<p>Note that <code class="docutils literal notranslate"><span class="pre">sys.monitoring.restart_events()</span></code> is not specific to one tool,
|
||
so tools must be prepared to receive events that they have chosen to DISABLE.</p>
|
||
</section>
|
||
</section>
|
||
<section id="events-in-callback-functions">
|
||
<h3><a class="toc-backref" href="#events-in-callback-functions" role="doc-backlink">Events in callback functions</a></h3>
|
||
<p>Events are suspended in callback functions and their callees for the tool
|
||
that registered that callback.</p>
|
||
<p>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.</p>
|
||
</section>
|
||
<section id="order-of-events">
|
||
<h3><a class="toc-backref" href="#order-of-events" role="doc-backlink">Order of events</a></h3>
|
||
<p>If an instructions triggers several events they occur in the following order:</p>
|
||
<ul class="simple">
|
||
<li>LINE</li>
|
||
<li>INSTRUCTION</li>
|
||
<li>All other events (only one of these events can occur per instruction)</li>
|
||
</ul>
|
||
<p>Each event is delivered to tools in ascending order of ID.</p>
|
||
</section>
|
||
<section id="the-call-event-group">
|
||
<h3><a class="toc-backref" href="#the-call-event-group" role="doc-backlink">The “call” event group</a></h3>
|
||
<p>Most events are independent; setting or disabling one event has no effect on the others.
|
||
However, the <code class="docutils literal notranslate"><span class="pre">CALL</span></code>, <code class="docutils literal notranslate"><span class="pre">C_RAISE</span></code> and <code class="docutils literal notranslate"><span class="pre">C_RETURN</span></code> events form a group.
|
||
If any of those events are set or disabled, then all events in the group are.
|
||
Disabling a <code class="docutils literal notranslate"><span class="pre">CALL</span></code> event will not disable the matching <code class="docutils literal notranslate"><span class="pre">C_RAISE</span></code> or <code class="docutils literal notranslate"><span class="pre">C_RETURN</span></code>,
|
||
but will disable all subsequent events.</p>
|
||
</section>
|
||
<section id="attributes-of-the-sys-monitoring-namespace">
|
||
<h3><a class="toc-backref" href="#attributes-of-the-sys-monitoring-namespace" role="doc-backlink">Attributes of the <code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code> namespace</a></h3>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">use_tool_id(id)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">free_tool_id(id)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">get_events(tool_id:</span> <span class="pre">int)->int</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">set_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">event_set:</span> <span class="pre">int)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">get_local_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">code:</span> <span class="pre">CodeType)->int</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">set_local_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">code:</span> <span class="pre">CodeType,</span> <span class="pre">event_set:</span> <span class="pre">int)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">register_callback(tool_id:</span> <span class="pre">int,</span> <span class="pre">event:</span> <span class="pre">int,</span> <span class="pre">func:</span> <span class="pre">Callable)->Optional[Callable]</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">restart_events()->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">DISABLE:</span> <span class="pre">object</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">MISSING:</span> <span class="pre">object</span></code></li>
|
||
</ul>
|
||
</section>
|
||
<section id="access-to-debug-only-features">
|
||
<h3><a class="toc-backref" href="#access-to-debug-only-features" role="doc-backlink">Access to “debug only” features</a></h3>
|
||
<p>Some features of the standard library are not accessible to normal code,
|
||
but are accessible to debuggers. For example, setting local variables, or
|
||
the line number.</p>
|
||
<p>These features will be available to callback functions.</p>
|
||
</section>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>This PEP is mostly backwards compatible.</p>
|
||
<p>There are some compatibility issues with <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a>, as the behavior
|
||
of <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a> plugins is outside of the VM’s control.
|
||
It is up to <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a> plugins to ensure that they respect the semantics
|
||
of this PEP. Simple plugins that do not change the state of the VM, and
|
||
defer execution to <code class="docutils literal notranslate"><span class="pre">_PyEval_EvalFrameDefault()</span></code> should continue to work.</p>
|
||
<p><a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a> and <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.setprofile" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.setprofile()</span></code></a> will act as if they were tools
|
||
6 and 7 respectively, so can be used alongside this PEP.</p>
|
||
<p>This means that <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a> and <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.setprofile" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.setprofile()</span></code></a> may not work
|
||
correctly with all <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a> plugins. Although, simple <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a>
|
||
plugins, as described above, should be fine.</p>
|
||
<section id="performance">
|
||
<h3><a class="toc-backref" href="#performance" role="doc-backlink">Performance</a></h3>
|
||
<p>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
|
||
<a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a> directly.</p>
|
||
<p>The performance of <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a> will be about the same.
|
||
The performance of <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.setprofile" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.setprofile()</span></code></a> should be better.
|
||
However, tools relying on <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a> and
|
||
<a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.setprofile" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.setprofile()</span></code></a> can be made a lot faster by using the
|
||
API provided by this PEP.</p>
|
||
<p>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 <a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">sys.settrace()</span></code></a>
|
||
and much cheaper than using <a class="pep reference internal" href="../pep-0523/" title="PEP 523 – Adding a frame evaluation API to CPython">PEP 523</a>.</p>
|
||
<p>Coverage tools can be implemented at very low cost,
|
||
by returning <code class="docutils literal notranslate"><span class="pre">DISABLE</span></code> in all callbacks.</p>
|
||
<p>For heavily instrumented code, e.g. using <code class="docutils literal notranslate"><span class="pre">LINE</span></code>, performance should be
|
||
better than <code class="docutils literal notranslate"><span class="pre">sys.settrace</span></code>, but not by that much as performance will be
|
||
dominated by the time spent in callbacks.</p>
|
||
<p>For optimizing virtual machines, such as future versions of CPython
|
||
(and <code class="docutils literal notranslate"><span class="pre">PyPy</span></code> should they choose to support this API), changes to the set
|
||
active events in the midst of a long running program could be quite
|
||
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.</p>
|
||
<p>In general these operations can be considered to be fast:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">get_events(tool_id:</span> <span class="pre">int)->int</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">get_local_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">code:</span> <span class="pre">CodeType)->int</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">register_callback(tool_id:</span> <span class="pre">int,</span> <span class="pre">event:</span> <span class="pre">int,</span> <span class="pre">func:</span> <span class="pre">Callable)->Optional[Callable]</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">get_tool(tool_id)</span> <span class="pre">-></span> <span class="pre">str</span> <span class="pre">|</span> <span class="pre">None</span></code></li>
|
||
</ul>
|
||
<p>These operations are slower, but not especially so:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">set_local_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">code:</span> <span class="pre">CodeType,</span> <span class="pre">event_set:</span> <span class="pre">int)->None</span></code></li>
|
||
</ul>
|
||
<p>And these operations should be regarded as slow:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">use_tool_id(id,</span> <span class="pre">name:str)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">free_tool_id(id)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">set_events(tool_id:</span> <span class="pre">int,</span> <span class="pre">event_set:</span> <span class="pre">int)->None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">restart_events()->None</span></code></li>
|
||
</ul>
|
||
<p>How slow the slow operations are depends on when they happen.
|
||
If done early in the program, before modules are loaded,
|
||
they should be fairly inexpensive.</p>
|
||
<section id="memory-consumption">
|
||
<h4><a class="toc-backref" href="#memory-consumption" role="doc-backlink">Memory Consumption</a></h4>
|
||
<p>When not in use, this PEP will have a negligible change on memory consumption.</p>
|
||
<p>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 <strong>roughly</strong> as follows:</p>
|
||
<table class="docutils align-default">
|
||
<thead>
|
||
<tr class="row-odd"><th class="head" colspan="2"></th>
|
||
<th class="head" colspan="2">Events</th>
|
||
</tr>
|
||
<tr class="row-even"><th class="head">Tools</th>
|
||
<th class="head">Others</th>
|
||
<th class="head">LINE</th>
|
||
<th class="head">INSTRUCTION</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-odd"><td>One</td>
|
||
<td>None</td>
|
||
<td>≈40%</td>
|
||
<td>≈80%</td>
|
||
</tr>
|
||
<tr class="row-even"><td rowspan="2">Two or more</td>
|
||
<td rowspan="2">≈40%</td>
|
||
<td rowspan="2">≈120%</td>
|
||
<td rowspan="2">≈200%</td>
|
||
</tr>
|
||
<tr class="row-odd"></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="security-implications">
|
||
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
|
||
<p>Allowing modification of running code has some security implications,
|
||
but no more than the ability to generate and call new code.</p>
|
||
<p>All the new functions listed above will trigger audit hooks.</p>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>This outlines the proposed implementation for CPython 3.12. The actual
|
||
implementation for later versions of CPython and other Python implementations
|
||
may differ considerably.</p>
|
||
<p>The proposed implementation of this PEP will be built on top of the quickening
|
||
step of CPython 3.11, as described in <a class="pep reference internal" href="../pep-0659/#quickening" title="PEP 659 – Specializing Adaptive Interpreter § Quickening">PEP 659</a>.
|
||
Instrumentation works in much the same way as quickening, bytecodes are
|
||
replaced with instrumented ones as needed.</p>
|
||
<p>For example, if the <code class="docutils literal notranslate"><span class="pre">CALL</span></code> event is turned on,
|
||
then all call instructions will be
|
||
replaced with a <code class="docutils literal notranslate"><span class="pre">INSTRUMENTED_CALL</span></code> instruction.</p>
|
||
<p>Note that this will interfere with specialization, which will result in some
|
||
performance degradation in addition to the overhead of calling the
|
||
registered callable.</p>
|
||
<p>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.</p>
|
||
<p>Other events, such as <code class="docutils literal notranslate"><span class="pre">RAISE</span></code> can be turned on or off cheaply,
|
||
as they do not rely on code instrumentation, but runtime checks when the
|
||
underlying event occurs.</p>
|
||
<p>The exact set of events that require instrumentation is an implementation detail,
|
||
but for the current design, the following events will require instrumentation:</p>
|
||
<ul class="simple">
|
||
<li>PY_START</li>
|
||
<li>PY_RESUME</li>
|
||
<li>PY_RETURN</li>
|
||
<li>PY_YIELD</li>
|
||
<li>CALL</li>
|
||
<li>LINE</li>
|
||
<li>INSTRUCTION</li>
|
||
<li>JUMP</li>
|
||
<li>BRANCH</li>
|
||
</ul>
|
||
<p>Each instrumented bytecode will require an additional 8 bits of information to
|
||
note which tool the instrumentation applies to.
|
||
<code class="docutils literal notranslate"><span class="pre">LINE</span></code> and <code class="docutils literal notranslate"><span class="pre">INSTRUCTION</span></code> events require additional information, as they
|
||
need to store the original instruction, or even the instrumented instruction
|
||
if they overlap other instrumentation.</p>
|
||
</section>
|
||
<section id="implementing-tools">
|
||
<h2><a class="toc-backref" href="#implementing-tools" role="doc-backlink">Implementing tools</a></h2>
|
||
<p>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.</p>
|
||
<p>Converting events into data that is meaningful to the users is
|
||
the responsibility of the tool.</p>
|
||
<p>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.</p>
|
||
<section id="debuggers">
|
||
<h3><a class="toc-backref" href="#debuggers" role="doc-backlink">Debuggers</a></h3>
|
||
<section id="inserting-breakpoints">
|
||
<h4><a class="toc-backref" href="#inserting-breakpoints" role="doc-backlink">Inserting breakpoints</a></h4>
|
||
<p>Breakpoints can be inserted setting per code object events, either <code class="docutils literal notranslate"><span class="pre">LINE</span></code> or <code class="docutils literal notranslate"><span class="pre">INSTRUCTION</span></code>,
|
||
and returning <code class="docutils literal notranslate"><span class="pre">DISABLE</span></code> for any events not matching a breakpoint.</p>
|
||
</section>
|
||
<section id="stepping">
|
||
<h4><a class="toc-backref" href="#stepping" role="doc-backlink">Stepping</a></h4>
|
||
<p>Debuggers usually offer the ability to step execution by a
|
||
single instruction or line.</p>
|
||
<p>Like breakpoints, stepping can be implemented by setting per code object events.
|
||
As soon as normal execution is to be resumed, the local events can be unset.</p>
|
||
</section>
|
||
<section id="attaching">
|
||
<h4><a class="toc-backref" href="#attaching" role="doc-backlink">Attaching</a></h4>
|
||
<p>Debuggers can use the <code class="docutils literal notranslate"><span class="pre">PY_START</span></code> and <code class="docutils literal notranslate"><span class="pre">PY_RESUME</span></code> events to be informed
|
||
when a code object is first encountered, so that any necessary breakpoints
|
||
can be inserted.</p>
|
||
</section>
|
||
</section>
|
||
<section id="coverage-tools">
|
||
<h3><a class="toc-backref" href="#coverage-tools" role="doc-backlink">Coverage Tools</a></h3>
|
||
<p>Coverage tools need to track which parts of the control graph have been
|
||
executed. To do this, they need to register for the <code class="docutils literal notranslate"><span class="pre">PY_</span></code> events,
|
||
plus <code class="docutils literal notranslate"><span class="pre">JUMP</span></code> and <code class="docutils literal notranslate"><span class="pre">BRANCH</span></code>.</p>
|
||
<p>This information can be then be converted back into a line based report
|
||
after execution has completed.</p>
|
||
</section>
|
||
<section id="profilers">
|
||
<h3><a class="toc-backref" href="#profilers" role="doc-backlink">Profilers</a></h3>
|
||
<p>Simple profilers need to gather information about calls.
|
||
To do this profilers should register for the following events:</p>
|
||
<ul class="simple">
|
||
<li>PY_START</li>
|
||
<li>PY_RESUME</li>
|
||
<li>PY_THROW</li>
|
||
<li>PY_RETURN</li>
|
||
<li>PY_YIELD</li>
|
||
<li>PY_UNWIND</li>
|
||
<li>CALL</li>
|
||
<li>C_RAISE</li>
|
||
<li>C_RETURN</li>
|
||
</ul>
|
||
<section id="line-based-profilers">
|
||
<h4><a class="toc-backref" href="#line-based-profilers" role="doc-backlink">Line based profilers</a></h4>
|
||
<p>Line based profilers can use the <code class="docutils literal notranslate"><span class="pre">LINE</span></code> and <code class="docutils literal notranslate"><span class="pre">JUMP</span></code> events.
|
||
Implementers of profilers should be aware that instrumenting <code class="docutils literal notranslate"><span class="pre">LINE</span></code>
|
||
events will have a large impact on performance.</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>Instrumenting profilers have significant overhead and will distort
|
||
the results of profiling. Unless you need exact call counts,
|
||
consider using a statistical profiler.</p>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected ideas</a></h2>
|
||
<p>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.</p>
|
||
<p>An earlier version of this PEP, proposed storing events as <code class="docutils literal notranslate"><span class="pre">enums</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Event</span><span class="p">(</span><span class="n">enum</span><span class="o">.</span><span class="n">IntFlag</span><span class="p">):</span>
|
||
<span class="n">PY_START</span> <span class="o">=</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>However, that would prevent monitoring of code before the <code class="docutils literal notranslate"><span class="pre">enum</span></code> module was
|
||
loaded and could cause unnecessary overhead.</p>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document is placed in the public domain or under the
|
||
CC0-1.0-Universal license, whichever is more permissive.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0669.rst">https://github.com/python/peps/blob/main/peps/pep-0669.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0669.rst">2024-02-07 11:51:52 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#events">Events</a><ul>
|
||
<li><a class="reference internal" href="#local-events">Local events</a></li>
|
||
<li><a class="reference internal" href="#ancilliary-events">Ancilliary events</a></li>
|
||
<li><a class="reference internal" href="#other-events">Other events</a></li>
|
||
<li><a class="reference internal" href="#the-stop-iteration-event">The STOP_ITERATION event</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#tool-identifiers">Tool identifiers</a></li>
|
||
<li><a class="reference internal" href="#setting-events-globally">Setting events globally</a></li>
|
||
<li><a class="reference internal" href="#per-code-object-events">Per code object events</a></li>
|
||
<li><a class="reference internal" href="#register-callback-functions">Register callback functions</a><ul>
|
||
<li><a class="reference internal" href="#callback-function-arguments">Callback function arguments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#events-in-callback-functions">Events in callback functions</a></li>
|
||
<li><a class="reference internal" href="#order-of-events">Order of events</a></li>
|
||
<li><a class="reference internal" href="#the-call-event-group">The “call” event group</a></li>
|
||
<li><a class="reference internal" href="#attributes-of-the-sys-monitoring-namespace">Attributes of the <code class="docutils literal notranslate"><span class="pre">sys.monitoring</span></code> namespace</a></li>
|
||
<li><a class="reference internal" href="#access-to-debug-only-features">Access to “debug only” features</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#performance">Performance</a><ul>
|
||
<li><a class="reference internal" href="#memory-consumption">Memory Consumption</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#implementing-tools">Implementing tools</a><ul>
|
||
<li><a class="reference internal" href="#debuggers">Debuggers</a><ul>
|
||
<li><a class="reference internal" href="#inserting-breakpoints">Inserting breakpoints</a></li>
|
||
<li><a class="reference internal" href="#stepping">Stepping</a></li>
|
||
<li><a class="reference internal" href="#attaching">Attaching</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#coverage-tools">Coverage Tools</a></li>
|
||
<li><a class="reference internal" href="#profilers">Profilers</a><ul>
|
||
<li><a class="reference internal" href="#line-based-profilers">Line based profilers</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0669.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |