mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
Update frame.f_lineno before any call to the (C) tracing function. (GH-24150)
* Minimizes breakage of C extensions and ensures PyFrame_GetLineNumber is efficient.
This commit is contained in:
parent
5c30145afb
commit
8643345bdb
1 changed files with 54 additions and 33 deletions
|
@ -46,10 +46,10 @@ _Py_IDENTIFIER(__name__);
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
Py_LOCAL_INLINE(PyObject *) call_function(
|
Py_LOCAL_INLINE(PyObject *) call_function(
|
||||||
PyThreadState *tstate, PyObject ***pp_stack,
|
PyThreadState *tstate, PyCodeAddressRange *, PyObject ***pp_stack,
|
||||||
Py_ssize_t oparg, PyObject *kwnames);
|
Py_ssize_t oparg, PyObject *kwnames);
|
||||||
static PyObject * do_call_core(
|
static PyObject * do_call_core(
|
||||||
PyThreadState *tstate, PyObject *func,
|
PyThreadState *tstate, PyCodeAddressRange *, PyObject *func,
|
||||||
PyObject *callargs, PyObject *kwdict);
|
PyObject *callargs, PyObject *kwdict);
|
||||||
|
|
||||||
#ifdef LLTRACE
|
#ifdef LLTRACE
|
||||||
|
@ -58,12 +58,15 @@ static int prtrace(PyThreadState *, PyObject *, const char *);
|
||||||
#endif
|
#endif
|
||||||
static int call_trace(Py_tracefunc, PyObject *,
|
static int call_trace(Py_tracefunc, PyObject *,
|
||||||
PyThreadState *, PyFrameObject *,
|
PyThreadState *, PyFrameObject *,
|
||||||
|
PyCodeAddressRange *,
|
||||||
int, PyObject *);
|
int, PyObject *);
|
||||||
static int call_trace_protected(Py_tracefunc, PyObject *,
|
static int call_trace_protected(Py_tracefunc, PyObject *,
|
||||||
PyThreadState *, PyFrameObject *,
|
PyThreadState *, PyFrameObject *,
|
||||||
|
PyCodeAddressRange *,
|
||||||
int, PyObject *);
|
int, PyObject *);
|
||||||
static void call_exc_trace(Py_tracefunc, PyObject *,
|
static void call_exc_trace(Py_tracefunc, PyObject *,
|
||||||
PyThreadState *, PyFrameObject *);
|
PyThreadState *, PyFrameObject *,
|
||||||
|
PyCodeAddressRange *);
|
||||||
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
|
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
|
||||||
PyThreadState *, PyFrameObject *,
|
PyThreadState *, PyFrameObject *,
|
||||||
PyCodeAddressRange *, int *);
|
PyCodeAddressRange *, int *);
|
||||||
|
@ -1380,6 +1383,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
}
|
}
|
||||||
|
|
||||||
tstate->frame = f;
|
tstate->frame = f;
|
||||||
|
co = f->f_code;
|
||||||
|
PyCodeAddressRange bounds;
|
||||||
|
_PyCode_InitAddressRange(co, &bounds);
|
||||||
|
|
||||||
if (tstate->use_tracing) {
|
if (tstate->use_tracing) {
|
||||||
if (tstate->c_tracefunc != NULL) {
|
if (tstate->c_tracefunc != NULL) {
|
||||||
|
@ -1398,7 +1404,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
whenever an exception is detected. */
|
whenever an exception is detected. */
|
||||||
if (call_trace_protected(tstate->c_tracefunc,
|
if (call_trace_protected(tstate->c_tracefunc,
|
||||||
tstate->c_traceobj,
|
tstate->c_traceobj,
|
||||||
tstate, f, PyTrace_CALL, Py_None)) {
|
tstate, f, &bounds,
|
||||||
|
PyTrace_CALL, Py_None)) {
|
||||||
/* Trace function raised an error */
|
/* Trace function raised an error */
|
||||||
goto exit_eval_frame;
|
goto exit_eval_frame;
|
||||||
}
|
}
|
||||||
|
@ -1408,7 +1415,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
return itself and isn't called for "line" events */
|
return itself and isn't called for "line" events */
|
||||||
if (call_trace_protected(tstate->c_profilefunc,
|
if (call_trace_protected(tstate->c_profilefunc,
|
||||||
tstate->c_profileobj,
|
tstate->c_profileobj,
|
||||||
tstate, f, PyTrace_CALL, Py_None)) {
|
tstate, f, &bounds,
|
||||||
|
PyTrace_CALL, Py_None)) {
|
||||||
/* Profile function raised an error */
|
/* Profile function raised an error */
|
||||||
goto exit_eval_frame;
|
goto exit_eval_frame;
|
||||||
}
|
}
|
||||||
|
@ -1418,9 +1426,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
if (PyDTrace_FUNCTION_ENTRY_ENABLED())
|
if (PyDTrace_FUNCTION_ENTRY_ENABLED())
|
||||||
dtrace_function_entry(f);
|
dtrace_function_entry(f);
|
||||||
|
|
||||||
co = f->f_code;
|
|
||||||
PyCodeAddressRange bounds;
|
|
||||||
_PyCode_InitAddressRange(co, &bounds);
|
|
||||||
int instr_prev = -1;
|
int instr_prev = -1;
|
||||||
|
|
||||||
names = co->co_names;
|
names = co->co_names;
|
||||||
|
@ -2348,7 +2353,7 @@ main_loop:
|
||||||
if (retval == NULL) {
|
if (retval == NULL) {
|
||||||
if (tstate->c_tracefunc != NULL
|
if (tstate->c_tracefunc != NULL
|
||||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
&& _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) {
|
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||||
gen_status = PYGEN_RETURN;
|
gen_status = PYGEN_RETURN;
|
||||||
}
|
}
|
||||||
|
@ -3596,7 +3601,7 @@ main_loop:
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
else if (tstate->c_tracefunc != NULL) {
|
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);
|
_PyErr_Clear(tstate);
|
||||||
}
|
}
|
||||||
|
@ -3764,7 +3769,7 @@ main_loop:
|
||||||
`callable` will be POPed by call_function.
|
`callable` will be POPed by call_function.
|
||||||
NULL will will be POPed manually later.
|
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;
|
stack_pointer = sp;
|
||||||
(void)POP(); /* POP the NULL. */
|
(void)POP(); /* POP the NULL. */
|
||||||
}
|
}
|
||||||
|
@ -3781,7 +3786,7 @@ main_loop:
|
||||||
We'll be passing `oparg + 1` to call_function, to
|
We'll be passing `oparg + 1` to call_function, to
|
||||||
make it accept the `self` as a first argument.
|
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;
|
stack_pointer = sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3795,7 +3800,7 @@ main_loop:
|
||||||
PREDICTED(CALL_FUNCTION);
|
PREDICTED(CALL_FUNCTION);
|
||||||
PyObject **sp, *res;
|
PyObject **sp, *res;
|
||||||
sp = stack_pointer;
|
sp = stack_pointer;
|
||||||
res = call_function(tstate, &sp, oparg, NULL);
|
res = call_function(tstate, &bounds, &sp, oparg, NULL);
|
||||||
stack_pointer = sp;
|
stack_pointer = sp;
|
||||||
PUSH(res);
|
PUSH(res);
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
|
@ -3812,7 +3817,7 @@ main_loop:
|
||||||
assert(PyTuple_GET_SIZE(names) <= oparg);
|
assert(PyTuple_GET_SIZE(names) <= oparg);
|
||||||
/* We assume without checking that names contains only strings */
|
/* We assume without checking that names contains only strings */
|
||||||
sp = stack_pointer;
|
sp = stack_pointer;
|
||||||
res = call_function(tstate, &sp, oparg, names);
|
res = call_function(tstate, &bounds, &sp, oparg, names);
|
||||||
stack_pointer = sp;
|
stack_pointer = sp;
|
||||||
PUSH(res);
|
PUSH(res);
|
||||||
Py_DECREF(names);
|
Py_DECREF(names);
|
||||||
|
@ -3857,7 +3862,7 @@ main_loop:
|
||||||
}
|
}
|
||||||
assert(PyTuple_CheckExact(callargs));
|
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(func);
|
||||||
Py_DECREF(callargs);
|
Py_DECREF(callargs);
|
||||||
Py_XDECREF(kwargs);
|
Py_XDECREF(kwargs);
|
||||||
|
@ -4024,7 +4029,7 @@ error:
|
||||||
assert(f->f_state == FRAME_EXECUTING);
|
assert(f->f_state == FRAME_EXECUTING);
|
||||||
f->f_state = FRAME_UNWINDING;
|
f->f_state = FRAME_UNWINDING;
|
||||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
||||||
tstate, f);
|
tstate, f, &bounds);
|
||||||
}
|
}
|
||||||
exception_unwind:
|
exception_unwind:
|
||||||
f->f_state = FRAME_UNWINDING;
|
f->f_state = FRAME_UNWINDING;
|
||||||
|
@ -4102,13 +4107,13 @@ exiting:
|
||||||
if (tstate->use_tracing) {
|
if (tstate->use_tracing) {
|
||||||
if (tstate->c_tracefunc) {
|
if (tstate->c_tracefunc) {
|
||||||
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
|
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
|
||||||
tstate, f, PyTrace_RETURN, retval)) {
|
tstate, f, &bounds, PyTrace_RETURN, retval)) {
|
||||||
Py_CLEAR(retval);
|
Py_CLEAR(retval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tstate->c_profilefunc) {
|
if (tstate->c_profilefunc) {
|
||||||
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
|
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
|
||||||
tstate, f, PyTrace_RETURN, retval)) {
|
tstate, f, &bounds, PyTrace_RETURN, retval)) {
|
||||||
Py_CLEAR(retval);
|
Py_CLEAR(retval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4902,7 +4907,9 @@ prtrace(PyThreadState *tstate, PyObject *v, const char *str)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
call_exc_trace(Py_tracefunc func, PyObject *self,
|
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;
|
PyObject *type, *value, *traceback, *orig_traceback, *arg;
|
||||||
int err;
|
int err;
|
||||||
|
@ -4918,7 +4925,7 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
|
||||||
_PyErr_Restore(tstate, type, value, orig_traceback);
|
_PyErr_Restore(tstate, type, value, orig_traceback);
|
||||||
return;
|
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);
|
Py_DECREF(arg);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
_PyErr_Restore(tstate, type, value, orig_traceback);
|
_PyErr_Restore(tstate, type, value, orig_traceback);
|
||||||
|
@ -4933,12 +4940,13 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
|
||||||
static int
|
static int
|
||||||
call_trace_protected(Py_tracefunc func, PyObject *obj,
|
call_trace_protected(Py_tracefunc func, PyObject *obj,
|
||||||
PyThreadState *tstate, PyFrameObject *frame,
|
PyThreadState *tstate, PyFrameObject *frame,
|
||||||
|
PyCodeAddressRange *bounds,
|
||||||
int what, PyObject *arg)
|
int what, PyObject *arg)
|
||||||
{
|
{
|
||||||
PyObject *type, *value, *traceback;
|
PyObject *type, *value, *traceback;
|
||||||
int err;
|
int err;
|
||||||
_PyErr_Fetch(tstate, &type, &value, &traceback);
|
_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)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
_PyErr_Restore(tstate, type, value, traceback);
|
_PyErr_Restore(tstate, type, value, traceback);
|
||||||
|
@ -4955,6 +4963,7 @@ call_trace_protected(Py_tracefunc func, PyObject *obj,
|
||||||
static int
|
static int
|
||||||
call_trace(Py_tracefunc func, PyObject *obj,
|
call_trace(Py_tracefunc func, PyObject *obj,
|
||||||
PyThreadState *tstate, PyFrameObject *frame,
|
PyThreadState *tstate, PyFrameObject *frame,
|
||||||
|
PyCodeAddressRange *bounds,
|
||||||
int what, PyObject *arg)
|
int what, PyObject *arg)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
@ -4962,7 +4971,14 @@ call_trace(Py_tracefunc func, PyObject *obj,
|
||||||
return 0;
|
return 0;
|
||||||
tstate->tracing++;
|
tstate->tracing++;
|
||||||
tstate->use_tracing = 0;
|
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);
|
result = func(obj, frame, what, arg);
|
||||||
|
frame->f_lineno = 0;
|
||||||
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|
||||||
|| (tstate->c_profilefunc != NULL));
|
|| (tstate->c_profilefunc != NULL));
|
||||||
tstate->tracing--;
|
tstate->tracing--;
|
||||||
|
@ -5005,16 +5021,12 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
|
||||||
if (frame->f_lasti < *instr_prev ||
|
if (frame->f_lasti < *instr_prev ||
|
||||||
(line != lastline && frame->f_lasti == bounds->ar_start))
|
(line != lastline && frame->f_lasti == bounds->ar_start))
|
||||||
{
|
{
|
||||||
frame->f_lineno = line;
|
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_LINE, Py_None);
|
||||||
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
|
|
||||||
frame->f_lineno = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Always emit an opcode event if we're tracing all opcodes. */
|
/* Always emit an opcode event if we're tracing all opcodes. */
|
||||||
if (frame->f_trace_opcodes) {
|
if (frame->f_trace_opcodes) {
|
||||||
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
|
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_OPCODE, Py_None);
|
||||||
result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None);
|
|
||||||
frame->f_lineno = 0;
|
|
||||||
}
|
}
|
||||||
*instr_prev = frame->f_lasti;
|
*instr_prev = frame->f_lasti;
|
||||||
return result;
|
return result;
|
||||||
|
@ -5281,7 +5293,7 @@ PyEval_GetFuncDesc(PyObject *func)
|
||||||
#define C_TRACE(x, call) \
|
#define C_TRACE(x, call) \
|
||||||
if (tstate->use_tracing && tstate->c_profilefunc) { \
|
if (tstate->use_tracing && tstate->c_profilefunc) { \
|
||||||
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
|
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
|
||||||
tstate, tstate->frame, \
|
tstate, tstate->frame, bounds, \
|
||||||
PyTrace_C_CALL, func)) { \
|
PyTrace_C_CALL, func)) { \
|
||||||
x = NULL; \
|
x = NULL; \
|
||||||
} \
|
} \
|
||||||
|
@ -5291,13 +5303,13 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
||||||
if (x == NULL) { \
|
if (x == NULL) { \
|
||||||
call_trace_protected(tstate->c_profilefunc, \
|
call_trace_protected(tstate->c_profilefunc, \
|
||||||
tstate->c_profileobj, \
|
tstate->c_profileobj, \
|
||||||
tstate, tstate->frame, \
|
tstate, tstate->frame, bounds, \
|
||||||
PyTrace_C_EXCEPTION, func); \
|
PyTrace_C_EXCEPTION, func); \
|
||||||
/* XXX should pass (type, value, tb) */ \
|
/* XXX should pass (type, value, tb) */ \
|
||||||
} else { \
|
} else { \
|
||||||
if (call_trace(tstate->c_profilefunc, \
|
if (call_trace(tstate->c_profilefunc, \
|
||||||
tstate->c_profileobj, \
|
tstate->c_profileobj, \
|
||||||
tstate, tstate->frame, \
|
tstate, tstate->frame, bounds, \
|
||||||
PyTrace_C_RETURN, func)) { \
|
PyTrace_C_RETURN, func)) { \
|
||||||
Py_DECREF(x); \
|
Py_DECREF(x); \
|
||||||
x = NULL; \
|
x = NULL; \
|
||||||
|
@ -5312,6 +5324,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
trace_call_function(PyThreadState *tstate,
|
trace_call_function(PyThreadState *tstate,
|
||||||
|
PyCodeAddressRange *bounds,
|
||||||
PyObject *func,
|
PyObject *func,
|
||||||
PyObject **args, Py_ssize_t nargs,
|
PyObject **args, Py_ssize_t nargs,
|
||||||
PyObject *kwnames)
|
PyObject *kwnames)
|
||||||
|
@ -5346,7 +5359,11 @@ trace_call_function(PyThreadState *tstate,
|
||||||
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
|
/* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault()
|
||||||
to reduce the stack consumption. */
|
to reduce the stack consumption. */
|
||||||
Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION
|
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 **pfunc = (*pp_stack) - oparg - 1;
|
||||||
PyObject *func = *pfunc;
|
PyObject *func = *pfunc;
|
||||||
|
@ -5356,7 +5373,7 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
|
||||||
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
PyObject **stack = (*pp_stack) - nargs - nkwargs;
|
||||||
|
|
||||||
if (tstate->use_tracing) {
|
if (tstate->use_tracing) {
|
||||||
x = trace_call_function(tstate, func, stack, nargs, kwnames);
|
x = trace_call_function(tstate, bounds, func, stack, nargs, kwnames);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
x = PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
|
x = PyObject_Vectorcall(func, stack, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
|
||||||
|
@ -5374,7 +5391,11 @@ call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyO
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
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;
|
PyObject *result;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue