summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-04-13 10:08:14 (GMT)
committerGitHub <noreply@github.com>2021-04-13 10:08:14 (GMT)
commit9e7b2076fb4380987ad0262c4c0ca900b06475ad (patch)
treed7a4902fa70e0014849fd77f07d89f39bb2e7494 /Python/ceval.c
parentc2b7a66b91cdb96fbfdb160f96797208ddc5e436 (diff)
downloadcpython-9e7b2076fb4380987ad0262c4c0ca900b06475ad.zip
cpython-9e7b2076fb4380987ad0262c4c0ca900b06475ad.tar.gz
cpython-9e7b2076fb4380987ad0262c4c0ca900b06475ad.tar.bz2
bpo-43760: Speed up check for tracing in interpreter dispatch (#25276)
* Remove redundant tracing_possible field from interpreter state. * Move 'use_tracing' from tstate onto C stack, for fastest possible checking in dispatch logic. * Add comments stressing the importance stack discipline when dealing with CFrames. * Add NEWS
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c55
1 files changed, 32 insertions, 23 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index c121160..53b596b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -37,6 +37,7 @@ 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.
+ CFrame cframe;
} PyTraceInfo;
@@ -1109,8 +1110,6 @@ fail:
static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause);
static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
-#define _Py_TracingPossible(ceval) ((ceval)->tracing_possible)
-
PyObject *
PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
@@ -1307,7 +1306,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
#define DISPATCH() \
{ \
- if (_Py_TracingPossible(ceval2) OR_DTRACE_LINE OR_LLTRACE) { \
+ if (trace_info.cframe.use_tracing OR_DTRACE_LINE OR_LLTRACE) { \
goto tracing_dispatch; \
} \
f->f_lasti = INSTR_OFFSET(); \
@@ -1595,8 +1594,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
int oparg; /* Current opcode argument, if any */
PyObject **fastlocals, **freevars;
PyObject *retval = NULL; /* Return value */
- struct _ceval_state * const ceval2 = &tstate->interp->ceval;
- _Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
+ _Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker;
PyCodeObject *co;
const _Py_CODEUNIT *first_instr;
@@ -1616,11 +1614,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
/* Mark trace_info as uninitialized */
trace_info.code = NULL;
+ /* WARNING: Because the CFrame lives on the C stack,
+ * but can be accessed from a heap allocated object (tstate)
+ * strict stack discipline must be maintained.
+ */
+ CFrame *prev_cframe = tstate->cframe;
+ trace_info.cframe.use_tracing = prev_cframe->use_tracing;
+ trace_info.cframe.previous = prev_cframe;
+ tstate->cframe = &trace_info.cframe;
+
/* push frame */
tstate->frame = f;
co = f->f_code;
- if (tstate->use_tracing) {
+ if (trace_info.cframe.use_tracing) {
if (tstate->c_tracefunc != NULL) {
/* tstate->c_tracefunc, if defined, is a
function that will be called on *every* entry
@@ -1782,7 +1789,7 @@ main_loop:
/* line-by-line tracing support */
- if (_Py_TracingPossible(ceval2) &&
+ if (trace_info.cframe.use_tracing &&
tstate->c_tracefunc != NULL && !tstate->tracing) {
int err;
/* see maybe_call_line_trace()
@@ -4543,7 +4550,7 @@ exception_unwind:
PUSH(val);
PUSH(exc);
JUMPTO(handler);
- if (_Py_TracingPossible(ceval2)) {
+ if (trace_info.cframe.use_tracing) {
trace_info.instr_prev = INT_MAX;
}
/* Resume normal execution */
@@ -4567,7 +4574,7 @@ exception_unwind:
f->f_stackdepth = 0;
f->f_state = FRAME_RAISED;
exiting:
- if (tstate->use_tracing) {
+ if (trace_info.cframe.use_tracing) {
if (tstate->c_tracefunc) {
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f, &trace_info, PyTrace_RETURN, retval)) {
@@ -4584,6 +4591,10 @@ exiting:
/* pop frame */
exit_eval_frame:
+ /* Restore previous cframe */
+ tstate->cframe = trace_info.cframe.previous;
+ tstate->cframe->use_tracing = trace_info.cframe.use_tracing;
+
if (PyDTrace_FUNCTION_RETURN_ENABLED())
dtrace_function_return(f);
_Py_LeaveRecursiveCall(tstate);
@@ -5507,7 +5518,7 @@ call_trace(Py_tracefunc func, PyObject *obj,
if (tstate->tracing)
return 0;
tstate->tracing++;
- tstate->use_tracing = 0;
+ tstate->cframe->use_tracing = 0;
if (frame->f_lasti < 0) {
frame->f_lineno = frame->f_code->co_firstlineno;
}
@@ -5517,7 +5528,7 @@ call_trace(Py_tracefunc func, PyObject *obj,
}
result = func(obj, frame, what, arg);
frame->f_lineno = 0;
- tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ tstate->cframe->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
return result;
@@ -5528,15 +5539,15 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
{
PyThreadState *tstate = _PyThreadState_GET();
int save_tracing = tstate->tracing;
- int save_use_tracing = tstate->use_tracing;
+ int save_use_tracing = tstate->cframe->use_tracing;
PyObject *result;
tstate->tracing = 0;
- tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ tstate->cframe->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
result = PyObject_Call(func, args, NULL);
tstate->tracing = save_tracing;
- tstate->use_tracing = save_use_tracing;
+ tstate->cframe->use_tracing = save_use_tracing;
return result;
}
@@ -5590,7 +5601,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_profilefunc = NULL;
tstate->c_profileobj = NULL;
/* Must make sure that tracing is not ignored if 'profileobj' is freed */
- tstate->use_tracing = tstate->c_tracefunc != NULL;
+ tstate->cframe->use_tracing = tstate->c_tracefunc != NULL;
Py_XDECREF(profileobj);
Py_XINCREF(arg);
@@ -5598,7 +5609,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_profilefunc = func;
/* Flag that tracing or profiling is turned on */
- tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
+ tstate->cframe->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
return 0;
}
@@ -5626,14 +5637,12 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
return -1;
}
- struct _ceval_state *ceval2 = &tstate->interp->ceval;
PyObject *traceobj = tstate->c_traceobj;
- ceval2->tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
tstate->c_tracefunc = NULL;
tstate->c_traceobj = NULL;
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
- tstate->use_tracing = (tstate->c_profilefunc != NULL);
+ tstate->cframe->use_tracing = (tstate->c_profilefunc != NULL);
Py_XDECREF(traceobj);
Py_XINCREF(arg);
@@ -5641,7 +5650,7 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_tracefunc = func;
/* Flag that tracing or profiling is turned on */
- tstate->use_tracing = ((func != NULL)
+ tstate->cframe->use_tracing = ((func != NULL)
|| (tstate->c_profilefunc != NULL));
return 0;
@@ -5836,7 +5845,7 @@ PyEval_GetFuncDesc(PyObject *func)
}
#define C_TRACE(x, call) \
-if (tstate->use_tracing && tstate->c_profilefunc) { \
+if (trace_info->cframe.use_tracing && tstate->c_profilefunc) { \
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
tstate, tstate->frame, trace_info, \
PyTrace_C_CALL, func)) { \
@@ -5917,7 +5926,7 @@ call_function(PyThreadState *tstate,
Py_ssize_t nargs = oparg - nkwargs;
PyObject **stack = (*pp_stack) - nargs - nkwargs;
- if (tstate->use_tracing) {
+ if (trace_info->cframe.use_tracing) {
x = trace_call_function(tstate, trace_info, func, stack, nargs, kwnames);
}
else {
@@ -5950,7 +5959,7 @@ do_call_core(PyThreadState *tstate,
}
else if (Py_IS_TYPE(func, &PyMethodDescr_Type)) {
Py_ssize_t nargs = PyTuple_GET_SIZE(callargs);
- if (nargs > 0 && tstate->use_tracing) {
+ if (nargs > 0 && trace_info->cframe.use_tracing) {
/* We need to create a temporary bound method as argument
for profiling.