summaryrefslogtreecommitdiff
path: root/coverage/ctracer
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2015-11-01 21:28:31 -0500
committerNed Batchelder <ned@nedbatchelder.com>2015-11-01 21:28:31 -0500
commit8243b70daeb5b3072bcbe17361ca17539719da11 (patch)
tree0325331e3e17bf263a21578e1c82c9861553c478 /coverage/ctracer
parent69400e4f57a42deb98392f5149ce26e704b29b21 (diff)
downloadpython-coveragepy-8243b70daeb5b3072bcbe17361ca17539719da11.tar.gz
Fix settrace(py_func). #436.
Diffstat (limited to 'coverage/ctracer')
-rw-r--r--coverage/ctracer/tracer.c41
1 files changed, 33 insertions, 8 deletions
diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c
index e5f39d0..79bebac 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;