Skip to content

Commit cc90ef8

Browse files
markshannonjunkmd
authored andcommitted
pythonGH-107674: Avoid allocating boxed ints for sys.settrace line events (pythonGH-107780)
1 parent 042b492 commit cc90ef8

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed performance regression in ``sys.settrace``.

Python/instrumentation.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ call_instrumentation_vector(
962962
/* Offset visible to user should be the offset in bytes, as that is the
963963
* convention for APIs involving code offsets. */
964964
int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
965-
PyObject *offset_obj = PyLong_FromSsize_t(bytes_offset);
965+
PyObject *offset_obj = PyLong_FromLong(bytes_offset);
966966
if (offset_obj == NULL) {
967967
return -1;
968968
}
@@ -1141,14 +1141,46 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
11411141
(interp->monitors.tools[PY_MONITORING_EVENT_LINE] |
11421142
code->_co_monitoring->local_monitors.tools[PY_MONITORING_EVENT_LINE]
11431143
);
1144-
PyObject *line_obj = PyLong_FromSsize_t(line);
1144+
/* Special case sys.settrace to avoid boxing the line number,
1145+
* only to immediately unbox it. */
1146+
if (tools & (1 << PY_MONITORING_SYS_TRACE_ID)) {
1147+
if (tstate->c_tracefunc != NULL && line >= 0) {
1148+
PyFrameObject *frame_obj = _PyFrame_GetFrameObject(frame);
1149+
if (frame_obj == NULL) {
1150+
return -1;
1151+
}
1152+
if (frame_obj->f_trace_lines) {
1153+
/* Need to set tracing and what_event as if using
1154+
* the instrumentation call. */
1155+
int old_what = tstate->what_event;
1156+
tstate->what_event = PY_MONITORING_EVENT_LINE;
1157+
tstate->tracing++;
1158+
/* Call c_tracefunc directly, having set the line number. */
1159+
Py_INCREF(frame_obj);
1160+
frame_obj->f_lineno = line;
1161+
int err = tstate->c_tracefunc(tstate->c_traceobj, frame_obj, PyTrace_LINE, Py_None);
1162+
frame_obj->f_lineno = 0;
1163+
tstate->tracing--;
1164+
tstate->what_event = old_what;
1165+
Py_DECREF(frame_obj);
1166+
if (err) {
1167+
return -1;
1168+
}
1169+
}
1170+
}
1171+
tools &= (255 - (1 << PY_MONITORING_SYS_TRACE_ID));
1172+
}
1173+
if (tools == 0) {
1174+
goto done;
1175+
}
1176+
PyObject *line_obj = PyLong_FromLong(line);
11451177
if (line_obj == NULL) {
11461178
return -1;
11471179
}
11481180
PyObject *args[3] = { NULL, (PyObject *)code, line_obj };
1149-
while (tools) {
1181+
do {
11501182
int tool = most_significant_bit(tools);
1151-
assert(tool >= 0 && tool < 8);
1183+
assert(tool >= 0 && tool < PY_MONITORING_SYS_PROFILE_ID);
11521184
assert(tools & (1 << tool));
11531185
tools &= ~(1 << tool);
11541186
int res = call_one_instrument(interp, tstate, &args[1],
@@ -1166,7 +1198,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
11661198
/* DISABLE */
11671199
remove_line_tools(code, i, 1 << tool);
11681200
}
1169-
}
1201+
} while (tools);
11701202
Py_DECREF(line_obj);
11711203
uint8_t original_opcode;
11721204
done:
@@ -1197,7 +1229,7 @@ _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame*
11971229
code->_co_monitoring->local_monitors.tools[PY_MONITORING_EVENT_INSTRUCTION]
11981230
);
11991231
int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT);
1200-
PyObject *offset_obj = PyLong_FromSsize_t(bytes_offset);
1232+
PyObject *offset_obj = PyLong_FromLong(bytes_offset);
12011233
if (offset_obj == NULL) {
12021234
return -1;
12031235
}

0 commit comments

Comments
 (0)