Skip to content

bpo-42823: Update frame.f_lineno before any call to the (C) tracing function. #24150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 54 additions & 33 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ _Py_IDENTIFIER(__name__);

/* Forward declarations */
Py_LOCAL_INLINE(PyObject *) call_function(
PyThreadState *tstate, PyObject ***pp_stack,
PyThreadState *tstate, PyCodeAddressRange *, PyObject ***pp_stack,
Py_ssize_t oparg, PyObject *kwnames);
static PyObject * do_call_core(
PyThreadState *tstate, PyObject *func,
PyThreadState *tstate, PyCodeAddressRange *, PyObject *func,
PyObject *callargs, PyObject *kwdict);

#ifdef LLTRACE
Expand All @@ -58,12 +58,15 @@ static int prtrace(PyThreadState *, PyObject *, const char *);
#endif
static int call_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
PyCodeAddressRange *,
int, PyObject *);
static int call_trace_protected(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
PyCodeAddressRange *,
int, PyObject *);
static void call_exc_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *);
PyThreadState *, PyFrameObject *,
PyCodeAddressRange *);
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
PyCodeAddressRange *, int *);
Expand Down Expand Up @@ -1380,6 +1383,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
}

tstate->frame = f;
co = f->f_code;
PyCodeAddressRange bounds;
_PyCode_InitAddressRange(co, &bounds);

if (tstate->use_tracing) {
if (tstate->c_tracefunc != NULL) {
Expand All @@ -1398,7 +1404,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
whenever an exception is detected. */
if (call_trace_protected(tstate->c_tracefunc,
tstate->c_traceobj,
tstate, f, PyTrace_CALL, Py_None)) {
tstate, f, &bounds,
PyTrace_CALL, Py_None)) {
/* Trace function raised an error */
goto exit_eval_frame;
}
Expand All @@ -1408,7 +1415,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
return itself and isn't called for "line" events */
if (call_trace_protected(tstate->c_profilefunc,
tstate->c_profileobj,
tstate, f, PyTrace_CALL, Py_None)) {
tstate, f, &bounds,
PyTrace_CALL, Py_None)) {
/* Profile function raised an error */
goto exit_eval_frame;
}
Expand All @@ -1418,9 +1426,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (PyDTrace_FUNCTION_ENTRY_ENABLED())
dtrace_function_entry(f);

co = f->f_code;
PyCodeAddressRange bounds;
_PyCode_InitAddressRange(co, &bounds);
int instr_prev = -1;

names = co->co_names;
Expand Down Expand Up @@ -2348,7 +2353,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (retval == NULL) {
if (tstate->c_tracefunc != NULL
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &bounds);
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
gen_status = PYGEN_RETURN;
}
Expand Down Expand Up @@ -3596,7 +3601,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
goto error;
}
else if (tstate->c_tracefunc != NULL) {
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &bounds);
}
_PyErr_Clear(tstate);
}
Expand Down Expand Up @@ -3764,7 +3769,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
`callable` will be POPed by call_function.
NULL will will be POPed manually later.
*/
res = call_function(tstate, &sp, oparg, NULL);
res = call_function(tstate, &bounds, &sp, oparg, NULL);
stack_pointer = sp;
(void)POP(); /* POP the NULL. */
}
Expand All @@ -3781,7 +3786,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
We'll be passing `oparg + 1` to call_function, to
make it accept the `self` as a first argument.
*/
res = call_function(tstate, &sp, oparg + 1, NULL);
res = call_function(tstate, &bounds, &sp, oparg + 1, NULL);
stack_pointer = sp;
}

Expand All @@ -3795,7 +3800,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
PREDICTED(CALL_FUNCTION);
PyObject **sp, *res;
sp = stack_pointer;
res = call_function(tstate, &sp, oparg, NULL);
res = call_function(tstate, &bounds, &sp, oparg, NULL);
stack_pointer = sp;
PUSH(res);
if (res == NULL) {
Expand All @@ -3812,7 +3817,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
assert(PyTuple_GET_SIZE(names) <= oparg);
/* We assume without checking that names contains only strings */
sp = stack_pointer;
res = call_function(tstate, &sp, oparg, names);
res = call_function(tstate, &bounds, &sp, oparg, names);
stack_pointer = sp;
PUSH(res);
Py_DECREF(names);
Expand Down Expand Up @@ -3857,7 +3862,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
}
assert(PyTuple_CheckExact(callargs));

result = do_call_core(tstate, func, callargs, kwargs);
result = do_call_core(tstate, &bounds, func, callargs, kwargs);
Py_DECREF(func);
Py_DECREF(callargs);
Py_XDECREF(kwargs);
Expand Down Expand Up @@ -4024,7 +4029,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
assert(f->f_state == FRAME_EXECUTING);
f->f_state = FRAME_UNWINDING;
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f);
tstate, f, &bounds);
}
exception_unwind:
f->f_state = FRAME_UNWINDING;
Expand Down Expand Up @@ -4102,13 +4107,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (tstate->use_tracing) {
if (tstate->c_tracefunc) {
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f, PyTrace_RETURN, retval)) {
tstate, f, &bounds, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
}
}
if (tstate->c_profilefunc) {
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
tstate, f, PyTrace_RETURN, retval)) {
tstate, f, &bounds, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
}
}
Expand Down Expand Up @@ -4902,7 +4907,9 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str)

static void
call_exc_trace(Py_tracefunc func, PyObject *self,
PyThreadState *tstate, PyFrameObject *f)
PyThreadState *tstate,
PyFrameObject *f,
PyCodeAddressRange *bounds)
{
PyObject *type, *value, *traceback, *orig_traceback, *arg;
int err;
Expand All @@ -4918,7 +4925,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
_PyErr_Restore(tstate, type, value, orig_traceback);
return;
}
err = call_trace(func, self, tstate, f, PyTrace_EXCEPTION, arg);
err = call_trace(func, self, tstate, f, bounds, PyTrace_EXCEPTION, arg);
Py_DECREF(arg);
if (err == 0) {
_PyErr_Restore(tstate, type, value, orig_traceback);
Expand All @@ -4933,12 +4940,13 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
static int
call_trace_protected(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
PyCodeAddressRange *bounds,
int what, PyObject *arg)
{
PyObject *type, *value, *traceback;
int err;
_PyErr_Fetch(tstate, &type, &value, &traceback);
err = call_trace(func, obj, tstate, frame, what, arg);
err = call_trace(func, obj, tstate, frame, bounds, what, arg);
if (err == 0)
{
_PyErr_Restore(tstate, type, value, traceback);
Expand All @@ -4955,14 +4963,22 @@ call_trace_protected(Py_tracefunc func, PyObject *obj,
static int
call_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
PyCodeAddressRange *bounds,
int what, PyObject *arg)
{
int result;
if (tstate->tracing)
return 0;
tstate->tracing++;
tstate->use_tracing = 0;
if (frame->f_lasti < 0) {
frame->f_lineno = frame->f_code->co_firstlineno;
}
else {
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
}
result = func(obj, frame, what, arg);
frame->f_lineno = 0;
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
Expand Down Expand Up @@ -5005,16 +5021,12 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
if (frame->f_lasti < *instr_prev ||
(line != lastline && frame->f_lasti == bounds->ar_start))
{
frame->f_lineno = line;
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
frame->f_lineno = 0;
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_LINE, Py_None);
}
}
/* Always emit an opcode event if we're tracing all opcodes. */
if (frame->f_trace_opcodes) {
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None);
frame->f_lineno = 0;
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_OPCODE, Py_None);
}
*instr_prev = frame->f_lasti;
return result;
Expand Down Expand Up @@ -5281,7 +5293,7 @@ PyEval_GetFuncDesc(PyObject *func)
#define C_TRACE(x, call) \
if (tstate->use_tracing && tstate->c_profilefunc) { \
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
tstate, tstate->frame, \
tstate, tstate->frame, bounds, \
PyTrace_C_CALL, func)) { \
x = NULL; \
} \
Expand All @@ -5291,13 +5303,13 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
if (x == NULL) { \
call_trace_protected(tstate->c_profilefunc, \
tstate->c_profileobj, \
tstate, tstate->frame, \
tstate, tstate->frame, bounds, \
PyTrace_C_EXCEPTION, func); \
/* XXX should pass (type, value, tb) */ \
} else { \
if (call_trace(tstate->c_profilefunc, \
tstate->c_profileobj, \
tstate, tstate->frame, \
tstate, tstate->frame, bounds, \
PyTrace_C_RETURN, func)) { \
Py_DECREF(x); \
x = NULL; \
Expand All @@ -5312,6 +5324,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \

static PyObject *
trace_call_function(PyThreadState *tstate,
PyCodeAddressRange *bounds,
PyObject *func,
PyObject **args, Py_ssize_t nargs,
PyObject *kwnames)
Expand Down Expand Up @@ -5346,7 +5359,11 @@ trace_call_function(PyThreadState *tstate,
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
to reduce the stack consumption. */
Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
call_function(PyThreadState *tstate,
PyCodeAddressRange *bounds,
PyObject ***pp_stack,
Py_ssize_t oparg,
PyObject *kwnames)
{
PyObject **pfunc = (*pp_stack) - oparg - 1;
PyObject *func = *pfunc;
Expand All @@ -5356,7 +5373,7 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
PyObject **stack = (*pp_stack) - nargs - nkwargs;

if (tstate->use_tracing) {
x = trace_call_function(tstate, func, stack, nargs, kwnames);
x = trace_call_function(tstate, bounds, func, stack, nargs, kwnames);
}
else {
x = PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
Expand All @@ -5374,7 +5391,11 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
}

static PyObject *
do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject *kwdict)
do_call_core(PyThreadState *tstate,
PyCodeAddressRange *bounds,
PyObject *func,
PyObject *callargs,
PyObject *kwdict)
{
PyObject *result;

Expand Down