]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] GH-107674: Avoid allocating boxed ints for `sys.settrace` line events (GH...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sun, 6 Apr 2025 19:48:53 +0000 (21:48 +0200)
committerGitHub <noreply@github.com>
Sun, 6 Apr 2025 19:48:53 +0000 (19:48 +0000)
GH-107674: Avoid allocating boxed ints for `sys.settrace` line events (GH-107780)
(cherry picked from commit 37d8b904f8b5b660f556597b21c0933b841d18de)

Co-authored-by: Mark Shannon <mark@hotpy.org>
Misc/NEWS.d/next/Core and Builtins/2023-08-05-04-47-18.gh-issue-107674.0sYhR2.rst [new file with mode: 0644]
Python/instrumentation.c

diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-05-04-47-18.gh-issue-107674.0sYhR2.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-05-04-47-18.gh-issue-107674.0sYhR2.rst
new file mode 100644 (file)
index 0000000..acfbf1f
--- /dev/null
@@ -0,0 +1 @@
+Fixed performance regression in ``sys.settrace``.
index a6ff7a8a98506c5dcf32841bbad6ffaa37879c00..2f94f103e431868df960b4fc2a4e9302f243552d 100644 (file)
@@ -962,7 +962,7 @@ call_instrumentation_vector(
     /* Offset visible to user should be the offset in bytes, as that is the
      * convention for APIs involving code offsets. */
     int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
-    PyObject *offset_obj = PyLong_FromSsize_t(bytes_offset);
+    PyObject *offset_obj = PyLong_FromLong(bytes_offset);
     if (offset_obj == NULL) {
         return -1;
     }
@@ -1141,14 +1141,46 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
         (interp->monitors.tools[PY_MONITORING_EVENT_LINE] |
          code->_co_monitoring->local_monitors.tools[PY_MONITORING_EVENT_LINE]
         );
-    PyObject *line_obj = PyLong_FromSsize_t(line);
+    /* Special case sys.settrace to avoid boxing the line number,
+     * only to immediately unbox it. */
+    if (tools & (1 << PY_MONITORING_SYS_TRACE_ID)) {
+        if (tstate->c_tracefunc != NULL && line >= 0) {
+            PyFrameObject *frame_obj = _PyFrame_GetFrameObject(frame);
+            if (frame_obj == NULL) {
+                return -1;
+            }
+            if (frame_obj->f_trace_lines) {
+                /* Need to set tracing and what_event as if using
+                 * the instrumentation call. */
+                int old_what = tstate->what_event;
+                tstate->what_event = PY_MONITORING_EVENT_LINE;
+                tstate->tracing++;
+                /* Call c_tracefunc directly, having set the line number. */
+                Py_INCREF(frame_obj);
+                frame_obj->f_lineno = line;
+                int err = tstate->c_tracefunc(tstate->c_traceobj, frame_obj, PyTrace_LINE, Py_None);
+                frame_obj->f_lineno = 0;
+                tstate->tracing--;
+                tstate->what_event = old_what;
+                Py_DECREF(frame_obj);
+                if (err) {
+                    return -1;
+                }
+            }
+        }
+        tools &= (255 - (1 << PY_MONITORING_SYS_TRACE_ID));
+    }
+    if (tools == 0) {
+        goto done;
+    }
+    PyObject *line_obj = PyLong_FromLong(line);
     if (line_obj == NULL) {
         return -1;
     }
     PyObject *args[3] = { NULL, (PyObject *)code, line_obj };
-    while (tools) {
+    do {
         int tool = most_significant_bit(tools);
-        assert(tool >= 0 && tool < 8);
+        assert(tool >= 0 && tool < PY_MONITORING_SYS_PROFILE_ID);
         assert(tools & (1 << tool));
         tools &= ~(1 << tool);
         int res = call_one_instrument(interp, tstate, &args[1],
@@ -1166,7 +1198,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
             /* DISABLE  */
             remove_line_tools(code, i, 1 << tool);
         }
-    }
+    } while (tools);
     Py_DECREF(line_obj);
     uint8_t original_opcode;
 done:
@@ -1197,7 +1229,7 @@ _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame*
          code->_co_monitoring->local_monitors.tools[PY_MONITORING_EVENT_INSTRUCTION]
         );
     int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
-    PyObject *offset_obj = PyLong_FromSsize_t(bytes_offset);
+    PyObject *offset_obj = PyLong_FromLong(bytes_offset);
     if (offset_obj == NULL) {
         return -1;
     }