summaryrefslogtreecommitdiff
path: root/coverage/tracer.c
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/tracer.c')
-rw-r--r--coverage/tracer.c170
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 */