diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2015-11-01 21:28:31 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2015-11-01 21:28:31 -0500 |
commit | dbaa663f9916c28e5fec55de3e61265bcce26baa (patch) | |
tree | bdb9ffddda1e2ffc2213a6f3ef2a504d5e64a021 /coverage/ctracer/tracer.c | |
parent | 2a546830a4e0f55b0c4d03625be21862e33e3f07 (diff) | |
download | python-coveragepy-git-dbaa663f9916c28e5fec55de3e61265bcce26baa.tar.gz |
Fix settrace(py_func). #436.
Diffstat (limited to 'coverage/ctracer/tracer.c')
-rw-r--r-- | coverage/ctracer/tracer.c | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c index e5f39d09..79bebac5 100644 --- a/coverage/ctracer/tracer.c +++ b/coverage/ctracer/tracer.c @@ -768,7 +768,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse #endif #if WHAT_LOG - if (what <= sizeof(what_sym)/sizeof(const char *)) { + if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) { ascii = MyText_AS_BYTES(frame->f_code->co_filename); printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), frame->f_lineno); Py_DECREF(ascii); @@ -859,6 +859,7 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) int what; int orig_lineno; PyObject *ret = NULL; + PyObject * ascii = NULL; static char *what_names[] = { "call", "exception", "line", "return", @@ -866,10 +867,6 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) NULL }; - #if WHAT_LOG - printf("pytrace\n"); - #endif - static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist, @@ -880,7 +877,7 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) /* In Python, the what argument is a string, we need to find an int for the C function. */ for (what = 0; what_names[what]; what++) { - PyObject *ascii = MyText_AS_BYTES(what_str); + ascii = MyText_AS_BYTES(what_str); int should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]); Py_DECREF(ascii); if (should_break) { @@ -888,6 +885,12 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) } } + #if WHAT_LOG + ascii = MyText_AS_BYTES(frame->f_code->co_filename); + printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), frame->f_lineno); + Py_DECREF(ascii); + #endif + /* Save off the frame's lineno, and use the forced one, if provided. */ orig_lineno = frame->f_lineno; if (lineno > 0) { @@ -904,8 +907,30 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) frame->f_lineno = orig_lineno; /* For better speed, install ourselves the C way so that future calls go - directly to CTracer_trace, without this intermediate function. */ - PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); + directly to CTracer_trace, without this intermediate function. + + Only do this if this is a CALL event, since new trace functions only + take effect then. If we don't condition it on CALL, then we'll clobber + the new trace function before it has a chance to get called. To + understand why, there are three internal values to track: frame.f_trace, + c_tracefunc, and c_traceobj. They are explained here: + http://nedbatchelder.com/text/trace-function.html + + Without the conditional on PyTrace_CALL, this is what happens: + + def func(): # f_trace c_tracefunc c_traceobj + # -------------- -------------- -------------- + # CTracer CTracer.trace CTracer + sys.settrace(my_func) + # CTracer trampoline my_func + # Now Python calls trampoline(CTracer), which calls this function + # which calls PyEval_SetTrace below, setting us as the tracer again: + # CTracer CTracer.trace CTracer + # and it's as if the settrace never happened. + */ + if (what == PyTrace_CALL) { + PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); + } done: return ret; |