Make tracing info (bounds, and previous instruction offset) a bit more robust. (GH-24726)

This commit is contained in:
Mark Shannon 2021-03-05 14:45:50 +00:00 committed by GitHub
parent 68245b7a10
commit 8e1b406275
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -34,6 +34,13 @@
#include <ctype.h> #include <ctype.h>
typedef struct {
PyCodeObject *code; // The code object for the bounds. May be NULL.
int instr_prev; // Only valid if code != NULL.
PyCodeAddressRange bounds; // Only valid if code != NULL.
} PyTraceInfo;
#ifdef Py_DEBUG #ifdef Py_DEBUG
/* For debugging the interpreter: */ /* For debugging the interpreter: */
#define LLTRACE 1 /* Low-level trace feature */ #define LLTRACE 1 /* Low-level trace feature */
@ -48,10 +55,10 @@ _Py_IDENTIFIER(__name__);
/* Forward declarations */ /* Forward declarations */
Py_LOCAL_INLINE(PyObject *) call_function( Py_LOCAL_INLINE(PyObject *) call_function(
PyThreadState *tstate, PyCodeAddressRange *, PyObject ***pp_stack, PyThreadState *tstate, PyTraceInfo *, 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, PyCodeAddressRange *, PyObject *func, PyThreadState *tstate, PyTraceInfo *, PyObject *func,
PyObject *callargs, PyObject *kwdict); PyObject *callargs, PyObject *kwdict);
#ifdef LLTRACE #ifdef LLTRACE
@ -60,19 +67,19 @@ 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 *, PyTraceInfo *,
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 *, PyTraceInfo *,
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 *); PyTraceInfo *trace_info);
static int maybe_call_line_trace(Py_tracefunc, PyObject *, static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *, PyThreadState *, PyFrameObject *,
PyCodeAddressRange *, int *); PyTraceInfo *);
static void maybe_dtrace_line(PyFrameObject *, PyCodeAddressRange *, int *); static void maybe_dtrace_line(PyFrameObject *, PyTraceInfo *);
static void dtrace_function_entry(PyFrameObject *); static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *); static void dtrace_function_return(PyFrameObject *);
@ -1617,15 +1624,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
/* Start of code */ /* Start of code */
/* push frame */
if (_Py_EnterRecursiveCall(tstate, "")) { if (_Py_EnterRecursiveCall(tstate, "")) {
return NULL; return NULL;
} }
PyTraceInfo trace_info;
/* Mark trace_info as initialized */
trace_info.code = NULL;
/* push frame */
tstate->frame = f; tstate->frame = f;
co = f->f_code; 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) {
@ -1644,7 +1653,7 @@ _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, &bounds, tstate, f, &trace_info,
PyTrace_CALL, Py_None)) { PyTrace_CALL, Py_None)) {
/* Trace function raised an error */ /* Trace function raised an error */
goto exit_eval_frame; goto exit_eval_frame;
@ -1655,7 +1664,7 @@ _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, &bounds, tstate, f, &trace_info,
PyTrace_CALL, Py_None)) { PyTrace_CALL, Py_None)) {
/* Profile function raised an error */ /* Profile function raised an error */
goto exit_eval_frame; goto exit_eval_frame;
@ -1666,8 +1675,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);
int instr_prev = -1;
names = co->co_names; names = co->co_names;
consts = co->co_consts; consts = co->co_consts;
fastlocals = f->f_localsplus; fastlocals = f->f_localsplus;
@ -1792,7 +1799,7 @@ main_loop:
f->f_lasti = INSTR_OFFSET(); f->f_lasti = INSTR_OFFSET();
if (PyDTrace_LINE_ENABLED()) if (PyDTrace_LINE_ENABLED())
maybe_dtrace_line(f, &bounds, &instr_prev); maybe_dtrace_line(f, &trace_info);
/* line-by-line tracing support */ /* line-by-line tracing support */
@ -1806,7 +1813,7 @@ main_loop:
err = maybe_call_line_trace(tstate->c_tracefunc, err = maybe_call_line_trace(tstate->c_tracefunc,
tstate->c_traceobj, tstate->c_traceobj,
tstate, f, tstate, f,
&bounds, &instr_prev); &trace_info);
/* Reload possibly changed frame fields */ /* Reload possibly changed frame fields */
JUMPTO(f->f_lasti); JUMPTO(f->f_lasti);
stack_pointer = f->f_valuestack+f->f_stackdepth; stack_pointer = f->f_valuestack+f->f_stackdepth;
@ -2593,7 +2600,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, &bounds); call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &trace_info);
if (_PyGen_FetchStopIterationValue(&retval) == 0) { if (_PyGen_FetchStopIterationValue(&retval) == 0) {
gen_status = PYGEN_RETURN; gen_status = PYGEN_RETURN;
} }
@ -4061,7 +4068,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, &bounds); call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f, &trace_info);
} }
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }
@ -4229,7 +4236,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, &bounds, &sp, oparg, NULL); res = call_function(tstate, &trace_info, &sp, oparg, NULL);
stack_pointer = sp; stack_pointer = sp;
(void)POP(); /* POP the NULL. */ (void)POP(); /* POP the NULL. */
} }
@ -4246,7 +4253,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, &bounds, &sp, oparg + 1, NULL); res = call_function(tstate, &trace_info, &sp, oparg + 1, NULL);
stack_pointer = sp; stack_pointer = sp;
} }
@ -4260,7 +4267,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, &bounds, &sp, oparg, NULL); res = call_function(tstate, &trace_info, &sp, oparg, NULL);
stack_pointer = sp; stack_pointer = sp;
PUSH(res); PUSH(res);
if (res == NULL) { if (res == NULL) {
@ -4277,7 +4284,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, &bounds, &sp, oparg, names); res = call_function(tstate, &trace_info, &sp, oparg, names);
stack_pointer = sp; stack_pointer = sp;
PUSH(res); PUSH(res);
Py_DECREF(names); Py_DECREF(names);
@ -4322,7 +4329,7 @@ main_loop:
} }
assert(PyTuple_CheckExact(callargs)); assert(PyTuple_CheckExact(callargs));
result = do_call_core(tstate, &bounds, func, callargs, kwargs); result = do_call_core(tstate, &trace_info, func, callargs, kwargs);
Py_DECREF(func); Py_DECREF(func);
Py_DECREF(callargs); Py_DECREF(callargs);
Py_XDECREF(kwargs); Py_XDECREF(kwargs);
@ -4489,7 +4496,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, &bounds); tstate, f, &trace_info);
} }
exception_unwind: exception_unwind:
f->f_state = FRAME_UNWINDING; f->f_state = FRAME_UNWINDING;
@ -4541,7 +4548,7 @@ exception_unwind:
PUSH(exc); PUSH(exc);
JUMPTO(handler); JUMPTO(handler);
if (_Py_TracingPossible(ceval2)) { if (_Py_TracingPossible(ceval2)) {
instr_prev = INT_MAX; trace_info.instr_prev = INT_MAX;
} }
/* Resume normal execution */ /* Resume normal execution */
f->f_state = FRAME_EXECUTING; f->f_state = FRAME_EXECUTING;
@ -4567,13 +4574,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, &bounds, PyTrace_RETURN, retval)) { tstate, f, &trace_info, 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, &bounds, PyTrace_RETURN, retval)) { tstate, f, &trace_info, PyTrace_RETURN, retval)) {
Py_CLEAR(retval); Py_CLEAR(retval);
} }
} }
@ -5436,7 +5443,7 @@ static void
call_exc_trace(Py_tracefunc func, PyObject *self, call_exc_trace(Py_tracefunc func, PyObject *self,
PyThreadState *tstate, PyThreadState *tstate,
PyFrameObject *f, PyFrameObject *f,
PyCodeAddressRange *bounds) PyTraceInfo *trace_info)
{ {
PyObject *type, *value, *traceback, *orig_traceback, *arg; PyObject *type, *value, *traceback, *orig_traceback, *arg;
int err; int err;
@ -5452,7 +5459,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, bounds, PyTrace_EXCEPTION, arg); err = call_trace(func, self, tstate, f, trace_info, 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);
@ -5467,13 +5474,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, PyTraceInfo *trace_info,
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, bounds, what, arg); err = call_trace(func, obj, tstate, frame, trace_info, what, arg);
if (err == 0) if (err == 0)
{ {
_PyErr_Restore(tstate, type, value, traceback); _PyErr_Restore(tstate, type, value, traceback);
@ -5487,10 +5494,20 @@ call_trace_protected(Py_tracefunc func, PyObject *obj,
} }
} }
static void
initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame)
{
if (trace_info->code != frame->f_code) {
trace_info->code = frame->f_code;
trace_info->instr_prev = -1;
_PyCode_InitAddressRange(frame->f_code, &trace_info->bounds);
}
}
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, PyTraceInfo *trace_info,
int what, PyObject *arg) int what, PyObject *arg)
{ {
int result; int result;
@ -5502,7 +5519,8 @@ call_trace(Py_tracefunc func, PyObject *obj,
frame->f_lineno = frame->f_code->co_firstlineno; frame->f_lineno = frame->f_code->co_firstlineno;
} }
else { else {
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds); initialize_trace_info(trace_info, frame);
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, &trace_info->bounds);
} }
result = func(obj, frame, what, arg); result = func(obj, frame, what, arg);
frame->f_lineno = 0; frame->f_lineno = 0;
@ -5533,7 +5551,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
static int static int
maybe_call_line_trace(Py_tracefunc func, PyObject *obj, maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame, PyThreadState *tstate, PyFrameObject *frame,
PyCodeAddressRange *bounds, int *instr_prev) PyTraceInfo *trace_info)
{ {
int result = 0; int result = 0;
@ -5541,21 +5559,22 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
represents a jump backwards, update the frame's line number and represents a jump backwards, update the frame's line number and
then call the trace function if we're tracing source lines. then call the trace function if we're tracing source lines.
*/ */
int lastline = bounds->ar_line; initialize_trace_info(trace_info, frame);
int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds); int lastline = trace_info->bounds.ar_line;
int line = _PyCode_CheckLineNumber(frame->f_lasti, &trace_info->bounds);
if (line != -1 && frame->f_trace_lines) { if (line != -1 && frame->f_trace_lines) {
/* Trace backward edges or first instruction of a new line */ /* Trace backward edges or first instruction of a new line */
if (frame->f_lasti < *instr_prev || if (frame->f_lasti < trace_info->instr_prev ||
(line != lastline && frame->f_lasti == bounds->ar_start)) (line != lastline && frame->f_lasti == trace_info->bounds.ar_start))
{ {
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_LINE, Py_None); result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_LINE, Py_None);
} }
} }
/* 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) {
result = call_trace(func, obj, tstate, frame, bounds, PyTrace_OPCODE, Py_None); result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_OPCODE, Py_None);
} }
*instr_prev = frame->f_lasti; trace_info->instr_prev = frame->f_lasti;
return result; return result;
} }
@ -5826,7 +5845,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, bounds, \ tstate, tstate->frame, trace_info, \
PyTrace_C_CALL, func)) { \ PyTrace_C_CALL, func)) { \
x = NULL; \ x = NULL; \
} \ } \
@ -5836,13 +5855,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, bounds, \ tstate, tstate->frame, trace_info, \
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, bounds, \ tstate, tstate->frame, trace_info, \
PyTrace_C_RETURN, func)) { \ PyTrace_C_RETURN, func)) { \
Py_DECREF(x); \ Py_DECREF(x); \
x = NULL; \ x = NULL; \
@ -5857,7 +5876,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
static PyObject * static PyObject *
trace_call_function(PyThreadState *tstate, trace_call_function(PyThreadState *tstate,
PyCodeAddressRange *bounds, PyTraceInfo *trace_info,
PyObject *func, PyObject *func,
PyObject **args, Py_ssize_t nargs, PyObject **args, Py_ssize_t nargs,
PyObject *kwnames) PyObject *kwnames)
@ -5893,7 +5912,7 @@ trace_call_function(PyThreadState *tstate,
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, call_function(PyThreadState *tstate,
PyCodeAddressRange *bounds, PyTraceInfo *trace_info,
PyObject ***pp_stack, PyObject ***pp_stack,
Py_ssize_t oparg, Py_ssize_t oparg,
PyObject *kwnames) PyObject *kwnames)
@ -5906,7 +5925,7 @@ call_function(PyThreadState *tstate,
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, bounds, func, stack, nargs, kwnames); x = trace_call_function(tstate, trace_info, 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);
@ -5925,7 +5944,7 @@ call_function(PyThreadState *tstate,
static PyObject * static PyObject *
do_call_core(PyThreadState *tstate, do_call_core(PyThreadState *tstate,
PyCodeAddressRange *bounds, PyTraceInfo *trace_info,
PyObject *func, PyObject *func,
PyObject *callargs, PyObject *callargs,
PyObject *kwdict) PyObject *kwdict)
@ -6488,18 +6507,19 @@ dtrace_function_return(PyFrameObject *f)
/* DTrace equivalent of maybe_call_line_trace. */ /* DTrace equivalent of maybe_call_line_trace. */
static void static void
maybe_dtrace_line(PyFrameObject *frame, maybe_dtrace_line(PyFrameObject *frame,
PyCodeAddressRange *bounds, int *instr_prev) PyTraceInfo *trace_info)
{ {
const char *co_filename, *co_name; const char *co_filename, *co_name;
/* If the last instruction executed isn't in the current /* If the last instruction executed isn't in the current
instruction window, reset the window. instruction window, reset the window.
*/ */
int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds); initialize_trace_info(trace_info, frame);
int line = _PyCode_CheckLineNumber(frame->f_lasti, &trace_info->bounds);
/* If the last instruction falls at the start of a line or if /* If the last instruction falls at the start of a line or if
it represents a jump backwards, update the frame's line it represents a jump backwards, update the frame's line
number and call the trace function. */ number and call the trace function. */
if (line != frame->f_lineno || frame->f_lasti < *instr_prev) { if (line != frame->f_lineno || frame->f_lasti < trace_info->instr_prev) {
if (line != -1) { if (line != -1) {
frame->f_lineno = line; frame->f_lineno = line;
co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename); co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
@ -6511,7 +6531,7 @@ maybe_dtrace_line(PyFrameObject *frame,
PyDTrace_LINE(co_filename, co_name, line); PyDTrace_LINE(co_filename, co_name, line);
} }
} }
*instr_prev = frame->f_lasti; trace_info->instr_prev = frame->f_lasti;
} }