This change adjusts the profiling/tracing support so that the common

path (with no profile/trace function) through eval_code2() and
eval_frame() avoids several checks.

In the common cases of calls, returns, and exception propogation,
eval_code2() and eval_frame() used to test two values in the
thread-state: the profiling function and the tracing function.  With
this change, a flag is set in the thread-state if either of these is
active, allowing a single check to suffice when both are NULL.  This
also simplifies the code needed when either function is in use but is
already active (to avoid profiling/tracing the profiler/tracer); the
flag is set to 0 when the profile/trace code is entered, allowing the
same check to suffice for "already in the tracer" for call/return/
exception events.
This commit is contained in:
Fred Drake 2001-07-03 23:39:52 +00:00
parent d89a2e7731
commit 9e3ad78444
3 changed files with 67 additions and 47 deletions

View File

@ -49,6 +49,7 @@ typedef struct _ts {
int recursion_depth;
int ticker;
int tracing;
int use_tracing;
Py_tracefunc c_profilefunc;
Py_tracefunc c_tracefunc;

View File

@ -1890,8 +1890,11 @@ eval_frame(PyFrameObject *f)
f->f_lasti = INSTR_OFFSET();
/* Inline call_trace() for performance: */
tstate->tracing++;
tstate->use_tracing = 0;
err = (tstate->c_tracefunc)(tstate->c_traceobj, f,
PyTrace_LINE, Py_None);
tstate->use_tracing = (tstate->c_tracefunc
|| tstate->c_profilefunc);
tstate->tracing--;
break;
@ -2142,12 +2145,14 @@ eval_frame(PyFrameObject *f)
f->f_lasti -= 2;
PyTraceBack_Here(f);
if (tstate->c_tracefunc)
call_exc_trace(tstate->c_tracefunc,
tstate->c_traceobj, f);
if (tstate->c_profilefunc)
call_exc_trace(tstate->c_profilefunc,
tstate->c_profileobj, f);
if (tstate->use_tracing) {
if (tstate->c_tracefunc)
call_exc_trace(tstate->c_tracefunc,
tstate->c_traceobj, f);
if (tstate->c_profilefunc)
call_exc_trace(tstate->c_profilefunc,
tstate->c_profileobj,f);
}
}
/* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */
@ -2228,24 +2233,26 @@ eval_frame(PyFrameObject *f)
if (why != WHY_RETURN && why != WHY_YIELD)
retval = NULL;
if (tstate->c_tracefunc && !tstate->tracing) {
if (why == WHY_RETURN || why == WHY_YIELD) {
if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
f, PyTrace_RETURN, retval)) {
if (tstate->use_tracing) {
if (tstate->c_tracefunc
&& (why == WHY_RETURN || why == WHY_YIELD)) {
if (call_trace(tstate->c_tracefunc,
tstate->c_traceobj, f,
PyTrace_RETURN, retval)) {
Py_XDECREF(retval);
retval = NULL;
why = WHY_EXCEPTION;
}
}
}
if (tstate->c_profilefunc && !tstate->tracing
&& (why == WHY_RETURN || why == WHY_YIELD)) {
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
f, PyTrace_RETURN, retval)) {
Py_XDECREF(retval);
retval = NULL;
why = WHY_EXCEPTION;
if (tstate->c_profilefunc
&& (why == WHY_RETURN || why == WHY_YIELD)) {
if (call_trace(tstate->c_profilefunc,
tstate->c_profileobj, f,
PyTrace_RETURN, retval)) {
Py_XDECREF(retval);
retval = NULL;
why = WHY_EXCEPTION;
}
}
}
@ -2471,35 +2478,38 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
}
}
if (tstate->c_tracefunc != NULL && !tstate->tracing) {
/* tstate->sys_tracefunc, if defined, is a function that
will be called on *every* entry to a code block.
Its return value, if not None, is a function that
will be called at the start of each executed line
of code. (Actually, the function must return
itself in order to continue tracing.)
The trace functions are called with three arguments:
a pointer to the current frame, a string indicating
why the function is called, and an argument which
depends on the situation. The global trace function
(sys.trace) is also called whenever an exception
is detected. */
if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
f, PyTrace_CALL, Py_None)) {
/* XXX Need way to compute arguments?? */
/* Trace function raised an error */
goto fail;
if (tstate->use_tracing) {
if (tstate->c_tracefunc != NULL) {
/* tstate->c_tracefunc, if defined, is a
function that will be called on *every* entry
to a code block. Its return value, if not
None, is a function that will be called at
the start of each executed line of code.
(Actually, the function must return itself
in order to continue tracing.) The trace
functions are called with three arguments:
a pointer to the current frame, a string
indicating why the function is called, and
an argument which depends on the situation.
The global trace function is also called
whenever an exception is detected. */
if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
f, PyTrace_CALL, Py_None)) {
/* XXX Need way to compute arguments?? */
/* Trace function raised an error */
goto fail;
}
}
}
if (tstate->c_profilefunc != NULL) {
/* Similar for sys_profilefunc, except it needn't return
itself and isn't called for "line" events */
if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
f, PyTrace_CALL, Py_None)) {
/* XXX Need way to compute arguments?? */
/* Profile function raised an error */
goto fail;
if (tstate->c_profilefunc != NULL) {
/* Similar for c_profilefunc, except it needn't
return itself and isn't called for "line" events */
if (call_trace(tstate->c_profilefunc,
tstate->c_profileobj,
f, PyTrace_CALL, Py_None)) {
/* XXX Need way to compute arguments?? */
/* Profile function raised an error */
goto fail;
}
}
}
@ -2803,7 +2813,10 @@ call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
if (tstate->tracing)
return 0;
tstate->tracing++;
tstate->use_tracing = 0;
result = func(obj, frame, what, arg);
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
return result;
}
@ -2816,9 +2829,11 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
Py_XINCREF(arg);
tstate->c_profilefunc = NULL;
tstate->c_profileobj = NULL;
tstate->use_tracing = tstate->c_tracefunc != NULL;
Py_XDECREF(temp);
tstate->c_profilefunc = func;
tstate->c_profileobj = arg;
tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
}
void
@ -2829,9 +2844,12 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
Py_XINCREF(arg);
tstate->c_tracefunc = NULL;
tstate->c_traceobj = NULL;
tstate->use_tracing = tstate->c_profilefunc != NULL;
Py_XDECREF(temp);
tstate->c_tracefunc = func;
tstate->c_traceobj = arg;
tstate->use_tracing = ((func != NULL)
|| (tstate->c_profilefunc != NULL));
}
PyObject *

View File

@ -109,6 +109,7 @@ PyThreadState_New(PyInterpreterState *interp)
tstate->recursion_depth = 0;
tstate->ticker = 0;
tstate->tracing = 0;
tstate->use_tracing = 0;
tstate->dict = NULL;