mirror of https://github.com/python/cpython
gh-107674: Lazy load line number to improve performance of tracing (GH-118127)
This commit is contained in:
parent
c7e7bfc4ca
commit
375c94c75d
|
@ -0,0 +1 @@
|
||||||
|
Lazy load frame line number to improve performance of tracing
|
|
@ -41,12 +41,20 @@ int
|
||||||
PyFrame_GetLineNumber(PyFrameObject *f)
|
PyFrame_GetLineNumber(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
assert(f != NULL);
|
assert(f != NULL);
|
||||||
if (f->f_lineno != 0) {
|
if (f->f_lineno == -1) {
|
||||||
|
// We should calculate it once. If we can't get the line number,
|
||||||
|
// set f->f_lineno to 0.
|
||||||
|
f->f_lineno = PyUnstable_InterpreterFrame_GetLine(f->f_frame);
|
||||||
|
if (f->f_lineno < 0) {
|
||||||
|
f->f_lineno = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->f_lineno > 0) {
|
||||||
return f->f_lineno;
|
return f->f_lineno;
|
||||||
}
|
}
|
||||||
else {
|
return PyUnstable_InterpreterFrame_GetLine(f->f_frame);
|
||||||
return PyUnstable_InterpreterFrame_GetLine(f->f_frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -268,14 +268,15 @@ get_events(_Py_GlobalMonitors *m, int tool_id)
|
||||||
* 8 bit value.
|
* 8 bit value.
|
||||||
* if line_delta == -128:
|
* if line_delta == -128:
|
||||||
* line = None # represented as -1
|
* line = None # represented as -1
|
||||||
* elif line_delta == -127:
|
* elif line_delta == -127 or line_delta == -126:
|
||||||
* line = PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT));
|
* line = PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT));
|
||||||
* else:
|
* else:
|
||||||
* line = first_line + (offset >> OFFSET_SHIFT) + line_delta;
|
* line = first_line + (offset >> OFFSET_SHIFT) + line_delta;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define NO_LINE -128
|
#define NO_LINE -128
|
||||||
#define COMPUTED_LINE -127
|
#define COMPUTED_LINE_LINENO_CHANGE -127
|
||||||
|
#define COMPUTED_LINE -126
|
||||||
|
|
||||||
#define OFFSET_SHIFT 4
|
#define OFFSET_SHIFT 4
|
||||||
|
|
||||||
|
@ -302,7 +303,7 @@ compute_line(PyCodeObject *code, int offset, int8_t line_delta)
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
assert(line_delta == COMPUTED_LINE);
|
assert(line_delta == COMPUTED_LINE || line_delta == COMPUTED_LINE_LINENO_CHANGE);
|
||||||
/* Look it up */
|
/* Look it up */
|
||||||
return PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT));
|
return PyCode_Addr2Line(code, offset * sizeof(_Py_CODEUNIT));
|
||||||
}
|
}
|
||||||
|
@ -1224,18 +1225,26 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
|
||||||
}
|
}
|
||||||
PyInterpreterState *interp = tstate->interp;
|
PyInterpreterState *interp = tstate->interp;
|
||||||
int8_t line_delta = line_data->line_delta;
|
int8_t line_delta = line_data->line_delta;
|
||||||
int line = compute_line(code, i, line_delta);
|
int line = 0;
|
||||||
assert(line >= 0);
|
|
||||||
assert(prev != NULL);
|
if (line_delta == COMPUTED_LINE_LINENO_CHANGE) {
|
||||||
int prev_index = (int)(prev - _PyCode_CODE(code));
|
// We know the line number must have changed, don't need to calculate
|
||||||
int prev_line = _Py_Instrumentation_GetLine(code, prev_index);
|
// the line number for now because we might not need it.
|
||||||
if (prev_line == line) {
|
line = -1;
|
||||||
int prev_opcode = _PyCode_CODE(code)[prev_index].op.code;
|
} else {
|
||||||
/* RESUME and INSTRUMENTED_RESUME are needed for the operation of
|
line = compute_line(code, i, line_delta);
|
||||||
* instrumentation, so must never be hidden by an INSTRUMENTED_LINE.
|
assert(line >= 0);
|
||||||
*/
|
assert(prev != NULL);
|
||||||
if (prev_opcode != RESUME && prev_opcode != INSTRUMENTED_RESUME) {
|
int prev_index = (int)(prev - _PyCode_CODE(code));
|
||||||
goto done;
|
int prev_line = _Py_Instrumentation_GetLine(code, prev_index);
|
||||||
|
if (prev_line == line) {
|
||||||
|
int prev_opcode = _PyCode_CODE(code)[prev_index].op.code;
|
||||||
|
/* RESUME and INSTRUMENTED_RESUME are needed for the operation of
|
||||||
|
* instrumentation, so must never be hidden by an INSTRUMENTED_LINE.
|
||||||
|
*/
|
||||||
|
if (prev_opcode != RESUME && prev_opcode != INSTRUMENTED_RESUME) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1260,6 +1269,12 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
|
||||||
tstate->tracing++;
|
tstate->tracing++;
|
||||||
/* Call c_tracefunc directly, having set the line number. */
|
/* Call c_tracefunc directly, having set the line number. */
|
||||||
Py_INCREF(frame_obj);
|
Py_INCREF(frame_obj);
|
||||||
|
if (line == -1 && line_delta > COMPUTED_LINE) {
|
||||||
|
/* Only assign f_lineno if it's easy to calculate, otherwise
|
||||||
|
* do lazy calculation by setting the f_lineno to 0.
|
||||||
|
*/
|
||||||
|
line = compute_line(code, i, line_delta);
|
||||||
|
}
|
||||||
frame_obj->f_lineno = line;
|
frame_obj->f_lineno = line;
|
||||||
int err = tstate->c_tracefunc(tstate->c_traceobj, frame_obj, PyTrace_LINE, Py_None);
|
int err = tstate->c_tracefunc(tstate->c_traceobj, frame_obj, PyTrace_LINE, Py_None);
|
||||||
frame_obj->f_lineno = 0;
|
frame_obj->f_lineno = 0;
|
||||||
|
@ -1276,6 +1291,11 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame,
|
||||||
if (tools == 0) {
|
if (tools == 0) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line == -1) {
|
||||||
|
/* Need to calculate the line number now for monitoring events */
|
||||||
|
line = compute_line(code, i, line_delta);
|
||||||
|
}
|
||||||
PyObject *line_obj = PyLong_FromLong(line);
|
PyObject *line_obj = PyLong_FromLong(line);
|
||||||
if (line_obj == NULL) {
|
if (line_obj == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1477,6 +1497,13 @@ initialize_lines(PyCodeObject *code)
|
||||||
*/
|
*/
|
||||||
if (line != current_line && line >= 0) {
|
if (line != current_line && line >= 0) {
|
||||||
line_data[i].original_opcode = opcode;
|
line_data[i].original_opcode = opcode;
|
||||||
|
if (line_data[i].line_delta == COMPUTED_LINE) {
|
||||||
|
/* Label this line as a line with a line number change
|
||||||
|
* which could help the monitoring callback to quickly
|
||||||
|
* identify the line number change.
|
||||||
|
*/
|
||||||
|
line_data[i].line_delta = COMPUTED_LINE_LINENO_CHANGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
line_data[i].original_opcode = 0;
|
line_data[i].original_opcode = 0;
|
||||||
|
@ -1529,6 +1556,11 @@ initialize_lines(PyCodeObject *code)
|
||||||
assert(target >= 0);
|
assert(target >= 0);
|
||||||
if (line_data[target].line_delta != NO_LINE) {
|
if (line_data[target].line_delta != NO_LINE) {
|
||||||
line_data[target].original_opcode = _Py_GetBaseOpcode(code, target);
|
line_data[target].original_opcode = _Py_GetBaseOpcode(code, target);
|
||||||
|
if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) {
|
||||||
|
// If the line is a jump target, we are not sure if the line
|
||||||
|
// number changes, so we set it to COMPUTED_LINE.
|
||||||
|
line_data[target].line_delta = COMPUTED_LINE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Scan exception table */
|
/* Scan exception table */
|
||||||
|
|
|
@ -174,6 +174,7 @@ call_trace_func(_PyLegacyEventHandler *self, PyObject *arg)
|
||||||
|
|
||||||
Py_INCREF(frame);
|
Py_INCREF(frame);
|
||||||
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg);
|
int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg);
|
||||||
|
frame->f_lineno = 0;
|
||||||
Py_DECREF(frame);
|
Py_DECREF(frame);
|
||||||
if (err) {
|
if (err) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in New Issue