-
Notifications
You must be signed in to change notification settings - Fork 26
Faster tracing test #6
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
Changes from all commits
88fc80c
6458071
2cf3c98
5ba8650
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
The flag to check whether tracing is enabled for the thread is now kept on | ||
the C stack, instead of the heap, to streamline dispatch in the interpreter. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,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; | ||
|
||
|
||
|
@@ -1110,8 +1111,6 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, | |
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) | ||
|
@@ -1308,7 +1307,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(); \ | ||
|
@@ -1596,8 +1595,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; | ||
|
@@ -1617,11 +1615,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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, this is disturbing -- tstate (which is pretty much a global structure) now has a pointer to a local variable. That means you have to restore this on every exit from the function. Now, we know there's only one exit, and it's handled there, but it still makes me feel weird. And all this to make it possible to set a single int (perhaps even a single bit) from "outside" this function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know, and it would seem like overkill, if it weren't for how critical reads of that single int are. |
||
|
||
/* 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 | ||
|
@@ -1783,7 +1790,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) | |
|
||
/* 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() | ||
|
@@ -4544,7 +4551,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) | |
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 */ | ||
|
@@ -4568,7 +4575,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) | |
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)) { | ||
|
@@ -4585,6 +4592,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) | |
|
||
/* 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); | ||
|
@@ -5508,7 +5519,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; | ||
} | ||
|
@@ -5518,7 +5529,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; | ||
|
@@ -5529,15 +5540,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; | ||
} | ||
|
||
|
@@ -5591,15 +5602,15 @@ _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); | ||
tstate->c_profileobj = 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; | ||
} | ||
|
||
|
@@ -5627,22 +5638,20 @@ _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); | ||
tstate->c_traceobj = 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; | ||
|
@@ -5837,7 +5846,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)) { \ | ||
|
@@ -5918,7 +5927,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 { | ||
|
@@ -5951,7 +5960,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. | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.