diff options
Diffstat (limited to 'coverage/tracer.c')
-rw-r--r-- | coverage/tracer.c | 170 |
1 files changed, 110 insertions, 60 deletions
diff --git a/coverage/tracer.c b/coverage/tracer.c index e046596a..97dd113b 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -27,7 +27,8 @@ #define MyText_Type PyUnicode_Type #define MyText_Check(o) PyUnicode_Check(o) -#define MyText_AS_STRING(o) PyBytes_AS_STRING(PyUnicode_AsASCIIString(o)) +#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o) +#define MyText_AS_STRING(o) PyBytes_AS_STRING(o) #define MyInt_FromLong(l) PyLong_FromLong(l) #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0) @@ -36,6 +37,7 @@ #define MyText_Type PyString_Type #define MyText_Check(o) PyString_Check(o) +#define MyText_AS_BYTES(o) (Py_INCREF(o), o) #define MyText_AS_STRING(o) PyString_AS_STRING(o) #define MyInt_FromLong(l) PyInt_FromLong(l) @@ -56,7 +58,7 @@ typedef struct { int last_line; } DataStackEntry; -/* The Tracer type. */ +/* The CTracer type. */ typedef struct { PyObject_HEAD @@ -115,12 +117,12 @@ typedef struct { unsigned int errors; } stats; #endif /* COLLECT_STATS */ -} Tracer; +} CTracer; #define STACK_DELTA 100 static int -Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused) +CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) { #if COLLECT_STATS self->stats.calls = 0; @@ -161,7 +163,7 @@ Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused) } static void -Tracer_dealloc(Tracer *self) +CTracer_dealloc(CTracer *self) { if (self->started) { PyEval_SetTrace(NULL, NULL); @@ -207,7 +209,9 @@ showlog(int depth, int lineno, PyObject * filename, const char * msg) printf(" "); } if (filename) { - printf(" %s", MyText_AS_STRING(filename)); + PyObject *ascii = MyText_AS_BYTES(filename); + printf(" %s", MyText_AS_STRING(ascii)); + Py_DECREF(ascii); } if (msg) { printf(" %s", msg); @@ -227,14 +231,12 @@ static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "}; /* Record a pair of integers in self->cur_file_data. */ static int -Tracer_record_pair(Tracer *self, int l1, int l2) +CTracer_record_pair(CTracer *self, int l1, int l2) { int ret = RET_OK; - PyObject * t = PyTuple_New(2); + PyObject * t = Py_BuildValue("(ii)", l1, l2); if (t != NULL) { - PyTuple_SET_ITEM(t, 0, MyInt_FromLong(l1)); - PyTuple_SET_ITEM(t, 1, MyInt_FromLong(l2)); if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) { STATS( self->stats.errors++; ) ret = RET_ERROR; @@ -252,22 +254,29 @@ Tracer_record_pair(Tracer *self, int l1, int l2) * The Trace Function */ static int -Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) +CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) { int ret = RET_OK; PyObject * filename = NULL; PyObject * tracename = NULL; + #if WHAT_LOG || TRACE_LOG + PyObject * ascii = NULL; + #endif #if WHAT_LOG if (what <= sizeof(what_sym)/sizeof(const char *)) { - printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(frame->f_code->co_filename), frame->f_lineno); + ascii = MyText_AS_BYTES(frame->f_code->co_filename); + printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(ascii), frame->f_lineno); + Py_DECREF(ascii); } #endif #if TRACE_LOG - if (strstr(MyText_AS_STRING(frame->f_code->co_filename), start_file) && frame->f_lineno == start_line) { + ascii = MyText_AS_BYTES(frame->f_code->co_filename); + if (strstr(MyText_AS_STRING(ascii), start_file) && frame->f_lineno == start_line) { logging = 1; } + Py_DECREF(ascii); #endif /* See below for details on missing-return detection. */ @@ -286,7 +295,7 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) STATS( self->stats.missed_returns++; ) if (self->depth >= 0) { if (self->tracing_arcs && self->cur_file_data) { - if (Tracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) { + if (CTracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) { return RET_ERROR; } } @@ -365,6 +374,9 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) } } self->cur_file_data = file_data; + /* Make the frame right in case settrace(gettrace()) happens. */ + Py_INCREF(self); + frame->f_trace = (PyObject*)self; SHOWLOG(self->depth, frame->f_lineno, filename, "traced"); } else { @@ -383,7 +395,7 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) if (self->depth >= 0) { if (self->tracing_arcs && self->cur_file_data) { int first = frame->f_code->co_firstlineno; - if (Tracer_record_pair(self, self->last_line, -first) < 0) { + if (CTracer_record_pair(self, self->last_line, -first) < 0) { return RET_ERROR; } } @@ -403,7 +415,7 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) /* We're tracing in this frame: record something. */ if (self->tracing_arcs) { /* Tracing arcs: key is (last_line,this_line). */ - if (Tracer_record_pair(self, self->last_line, frame->f_lineno) < 0) { + if (CTracer_record_pair(self, self->last_line, frame->f_lineno) < 0) { return RET_ERROR; } } @@ -455,57 +467,98 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) } /* - * A sys.settrace-compatible function that invokes our C trace function. + * Python has two ways to set the trace function: sys.settrace(fn), which + * takes a Python callable, and PyEval_SetTrace(func, obj), which takes + * a C function and a Python object. The way these work together is that + * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the + * Python callable as the object in PyEval_SetTrace. So sys.gettrace() + * simply returns the Python object used as the second argument to + * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which + * means it must be callable to be used in sys.settrace(). + * + * So we make our self callable, equivalent to invoking our trace function. + * + * To help with the process of replaying stored frames, this function has an + * optional keyword argument: + * + * def CTracer_call(frame, event, arg, lineno=0) + * + * If provided, the lineno argument is used as the line number, and the + * frame's f_lineno member is ignored. */ static PyObject * -Tracer_pytrace(Tracer *self, PyObject *args) +CTracer_call(CTracer *self, PyObject *args, PyObject *kwds) { PyFrameObject *frame; PyObject *what_str; - PyObject *arg_unused; + PyObject *arg; + int lineno = 0; int what; + int orig_lineno; + PyObject *ret = NULL; + static char *what_names[] = { "call", "exception", "line", "return", "c_call", "c_exception", "c_return", NULL }; - if (!PyArg_ParseTuple(args, "O!O!O:Tracer_pytrace", - &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg_unused)) { + #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, + &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) { goto done; } /* 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++) { - if (!strcmp(MyText_AS_STRING(what_str), what_names[what])) { + PyObject *ascii = MyText_AS_BYTES(what_str); + int should_break = !strcmp(MyText_AS_STRING(ascii), what_names[what]); + Py_DECREF(ascii); + if (should_break) { break; } } + /* Save off the frame's lineno, and use the forced one, if provided. */ + orig_lineno = frame->f_lineno; + if (lineno > 0) { + frame->f_lineno = lineno; + } + /* Invoke the C function, and return ourselves. */ - if (Tracer_trace(self, frame, what, arg_unused) == RET_OK) { - return PyObject_GetAttrString((PyObject*)self, "pytrace"); + if (CTracer_trace(self, frame, what, arg) == RET_OK) { + Py_INCREF(self); + ret = (PyObject *)self; } + /* Clean up. */ + frame->f_lineno = orig_lineno; + done: - return NULL; + return ret; } static PyObject * -Tracer_start(Tracer *self, PyObject *args_unused) +CTracer_start(CTracer *self, PyObject *args_unused) { - PyEval_SetTrace((Py_tracefunc)Tracer_trace, (PyObject*)self); + PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); self->started = 1; self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs); self->last_line = -1; /* start() returns a trace function usable with sys.settrace() */ - return PyObject_GetAttrString((PyObject*)self, "pytrace"); + Py_INCREF(self); + return (PyObject *)self; } static PyObject * -Tracer_stop(Tracer *self, PyObject *args_unused) +CTracer_stop(CTracer *self, PyObject *args_unused) { if (self->started) { PyEval_SetTrace(NULL, NULL); @@ -516,7 +569,7 @@ Tracer_stop(Tracer *self, PyObject *args_unused) } static PyObject * -Tracer_get_stats(Tracer *self) +CTracer_get_stats(CTracer *self) { #if COLLECT_STATS return Py_BuildValue( @@ -538,49 +591,46 @@ Tracer_get_stats(Tracer *self) } static PyMemberDef -Tracer_members[] = { - { "should_trace", T_OBJECT, offsetof(Tracer, should_trace), 0, +CTracer_members[] = { + { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0, PyDoc_STR("Function indicating whether to trace a file.") }, - { "warn", T_OBJECT, offsetof(Tracer, warn), 0, + { "warn", T_OBJECT, offsetof(CTracer, warn), 0, PyDoc_STR("Function for issuing warnings.") }, - { "data", T_OBJECT, offsetof(Tracer, data), 0, + { "data", T_OBJECT, offsetof(CTracer, data), 0, PyDoc_STR("The raw dictionary of trace data.") }, - { "should_trace_cache", T_OBJECT, offsetof(Tracer, should_trace_cache), 0, + { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0, PyDoc_STR("Dictionary caching should_trace results.") }, - { "arcs", T_OBJECT, offsetof(Tracer, arcs), 0, + { "arcs", T_OBJECT, offsetof(CTracer, arcs), 0, PyDoc_STR("Should we trace arcs, or just lines?") }, { NULL } }; static PyMethodDef -Tracer_methods[] = { - { "pytrace", (PyCFunction) Tracer_pytrace, METH_VARARGS, - PyDoc_STR("A trace function compatible with sys.settrace()") }, - - { "start", (PyCFunction) Tracer_start, METH_VARARGS, +CTracer_methods[] = { + { "start", (PyCFunction) CTracer_start, METH_VARARGS, PyDoc_STR("Start the tracer") }, - { "stop", (PyCFunction) Tracer_stop, METH_VARARGS, + { "stop", (PyCFunction) CTracer_stop, METH_VARARGS, PyDoc_STR("Stop the tracer") }, - { "get_stats", (PyCFunction) Tracer_get_stats, METH_VARARGS, + { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS, PyDoc_STR("Get statistics about the tracing") }, { NULL } }; static PyTypeObject -TracerType = { +CTracerType = { MyType_HEAD_INIT - "coverage.Tracer", /*tp_name*/ - sizeof(Tracer), /*tp_basicsize*/ + "coverage.CTracer", /*tp_name*/ + sizeof(CTracer), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)Tracer_dealloc, /*tp_dealloc*/ + (destructor)CTracer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -590,28 +640,28 @@ TracerType = { 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ - 0, /*tp_call*/ + (ternaryfunc)CTracer_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "Tracer objects", /* tp_doc */ + "CTracer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - Tracer_methods, /* tp_methods */ - Tracer_members, /* tp_members */ + CTracer_methods, /* tp_methods */ + CTracer_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)Tracer_init, /* tp_init */ + (initproc)CTracer_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; @@ -644,14 +694,14 @@ PyInit_tracer(void) return NULL; } - TracerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TracerType) < 0) { + CTracerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CTracerType) < 0) { Py_DECREF(mod); return NULL; } - Py_INCREF(&TracerType); - PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType); + Py_INCREF(&CTracerType); + PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType); return mod; } @@ -668,13 +718,13 @@ inittracer(void) return; } - TracerType.tp_new = PyType_GenericNew; - if (PyType_Ready(&TracerType) < 0) { + CTracerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CTracerType) < 0) { return; } - Py_INCREF(&TracerType); - PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType); + Py_INCREF(&CTracerType); + PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType); } #endif /* Py3k */ |