Source file | clang-analyzer | cppcheck | cpychecker | gcc | Notes |
---|---|---|---|---|---|
coverage-3.5.1b1/coverage/tracer.c | 0 | 0 | 4 | 0 | Incomplete coverage: 4 analysis failure(s) |
python3-python-coverage-3.5.1-0.1.b1.fc17/coverage/tracer.c | 0 | 0 | 5 | 0 | Incomplete coverage: 4 analysis failure(s) |
Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
coverage/tracer.c:458:1 | cpychecker | refcount-too-high | Tracer_trace | ob_refcnt of '*self' is 1 too high |
coverage/tracer.c:458:1 | cpychecker | refcount-too-high | Tracer_trace | ob_refcnt of '*tracename' is 1 too high |
coverage/tracer.c:532:5 | cpychecker | returns-NULL-without-setting-exception | Tracer_call | returning (PyObject*)NULL without setting an exception |
coverage/tracer.c:609:35 | cpychecker | flags-within-PyMethodDef | flags do not match callback signature for 'Tracer_get_stats' within PyMethodDef table |
Tool | Location | Function | Message | Data |
---|---|---|---|---|
cpychecker | coverage/tracer.c:165:1 | Tracer_dealloc | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:256:1 | Tracer_trace | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:482:1 | Tracer_call | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:715:23 | inittracer | None | CustomFields([('traceback', 'Traceback (most recent call last):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/__init__.py", line 131, in _check_refcounts\n self.options)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4355, in check_refcounts\n rep = impl_check_refcounts(ctxt, fun, options)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4213, in impl_check_refcounts\n limits=limits)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3053, in iter_traces\n transitions = curstate.get_transitions()\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2105, in get_transitions\n return self._get_transitions_for_stmt(stmt)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2121, in _get_transitions_for_stmt\n return self._get_transitions_for_GimpleCall(stmt)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2292, in _get_transitions_for_GimpleCall\n return meth(stmt, *args)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 2544, in impl_PyModule_AddObject\n s_success.cpython.steal_reference(v_value, stmt.loc)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 741, in steal_reference\n _steal_ref)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 551, in change_refcount\n check_isinstance(oldvalue, RefcountValue)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/gccutils.py", line 642, in check_isinstance\n raise TypeError(\'%s / %r is not an instance of %s\' % (obj, obj, types))\nTypeError: (long int)val [-0x7fffffffffffffff <= val <= 0x7fffffffffffffff] from coverage/tracer.c:714 / WithinRange(gcctype=\'long int\', loc=gcc.Location(file=\'coverage/tracer.c\', line=714), minvalue=-0x7fffffffffffffff, maxvalue=0x7fffffffffffffff) is not an instance of |
1 /* C-based Tracer for Coverage. */
2
3 #include "Python.h"
4 #include "compile.h" /* in 2.3, this wasn't part of Python.h */
5 #include "eval.h" /* or this. */
6 #include "structmember.h"
7 #include "frameobject.h"
8
9 /* Compile-time debugging helpers */
10 #undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
11 #undef TRACE_LOG /* Define to log our bookkeeping. */
12 #undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
13
14 #if COLLECT_STATS
15 #define STATS(x) x
16 #else
17 #define STATS(x)
18 #endif
19
20 /* Py 2.x and 3.x compatibility */
21
22 #ifndef Py_TYPE
23 #define Py_TYPE(o) (((PyObject*)(o))->ob_type)
24 #endif
25
26 #if PY_MAJOR_VERSION >= 3
27
28 #define MyText_Type PyUnicode_Type
29 #define MyText_Check(o) PyUnicode_Check(o)
30 #define MyText_AS_STRING(o) PyBytes_AS_STRING(PyUnicode_AsASCIIString(o))
31 #define MyInt_FromLong(l) PyLong_FromLong(l)
32
33 #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
34
35 #else
36
37 #define MyText_Type PyString_Type
38 #define MyText_Check(o) PyString_Check(o)
39 #define MyText_AS_STRING(o) PyString_AS_STRING(o)
40 #define MyInt_FromLong(l) PyInt_FromLong(l)
41
42 #define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
43
44 #endif /* Py3k */
45
46 /* The values returned to indicate ok or error. */
47 #define RET_OK 0
48 #define RET_ERROR -1
49
50 /* An entry on the data stack. For each call frame, we need to record the
51 dictionary to capture data, and the last line number executed in that
52 frame.
53 */
54 typedef struct {
55 PyObject * file_data; /* PyMem_Malloc'ed, a borrowed ref. */
56 int last_line;
57 } DataStackEntry;
58
59 /* The Tracer type. */
60
61 typedef struct {
62 PyObject_HEAD
63
64 /* Python objects manipulated directly by the Collector class. */
65 PyObject * should_trace;
66 PyObject * warn;
67 PyObject * data;
68 PyObject * should_trace_cache;
69 PyObject * arcs;
70
71 /* Has the tracer been started? */
72 int started;
73 /* Are we tracing arcs, or just lines? */
74 int tracing_arcs;
75
76 /*
77 The data stack is a stack of dictionaries. Each dictionary collects
78 data for a single source file. The data stack parallels the call stack:
79 each call pushes the new frame's file data onto the data stack, and each
80 return pops file data off.
81
82 The file data is a dictionary whose form depends on the tracing options.
83 If tracing arcs, the keys are line number pairs. If not tracing arcs,
84 the keys are line numbers. In both cases, the value is irrelevant
85 (None).
86 */
87 /* The index of the last-used entry in data_stack. */
88 int depth;
89 /* The file data at each level, or NULL if not recording. */
90 DataStackEntry * data_stack;
91 int data_stack_alloc; /* number of entries allocated at data_stack. */
92
93 /* The current file_data dictionary. Borrowed. */
94 PyObject * cur_file_data;
95
96 /* The line number of the last line recorded, for tracing arcs.
97 -1 means there was no previous line, as when entering a code object.
98 */
99 int last_line;
100
101 /* The parent frame for the last exception event, to fix missing returns. */
102 PyFrameObject * last_exc_back;
103 int last_exc_firstlineno;
104
105 #if COLLECT_STATS
106 struct {
107 unsigned int calls;
108 unsigned int lines;
109 unsigned int returns;
110 unsigned int exceptions;
111 unsigned int others;
112 unsigned int new_files;
113 unsigned int missed_returns;
114 unsigned int stack_reallocs;
115 unsigned int errors;
116 } stats;
117 #endif /* COLLECT_STATS */
118 } Tracer;
119
120 #define STACK_DELTA 100
121
122 static int
123 Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused)
124 {
125 #if COLLECT_STATS
126 self->stats.calls = 0;
127 self->stats.lines = 0;
128 self->stats.returns = 0;
129 self->stats.exceptions = 0;
130 self->stats.others = 0;
131 self->stats.new_files = 0;
132 self->stats.missed_returns = 0;
133 self->stats.stack_reallocs = 0;
134 self->stats.errors = 0;
135 #endif /* COLLECT_STATS */
136
137 self->should_trace = NULL;
138 self->warn = NULL;
139 self->data = NULL;
140 self->should_trace_cache = NULL;
141 self->arcs = NULL;
142
143 self->started = 0;
144 self->tracing_arcs = 0;
145
146 self->depth = -1;
147 self->data_stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry));
148 if (self->data_stack == NULL) {
149 STATS( self->stats.errors++; )
150 PyErr_NoMemory();
151 return RET_ERROR;
152 }
153 self->data_stack_alloc = STACK_DELTA;
154
155 self->cur_file_data = NULL;
156 self->last_line = -1;
157
158 self->last_exc_back = NULL;
159
160 return RET_OK;
161 }
162
163 static void
164 Tracer_dealloc(Tracer *self)
165 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 166 if (self->started) {
167 PyEval_SetTrace(NULL, NULL);
168 }
169
170 Py_XDECREF(self->should_trace);
171 Py_XDECREF(self->warn);
172 Py_XDECREF(self->data);
173 Py_XDECREF(self->should_trace_cache);
174
175 PyMem_Free(self->data_stack);
176
177 Py_TYPE(self)->tp_free((PyObject*)self);
178 }
179
180 #if TRACE_LOG
181 static const char *
182 indent(int n)
183 {
184 static const char * spaces =
185 " "
186 " "
187 " "
188 " "
189 ;
190 return spaces + strlen(spaces) - n*2;
191 }
192
193 static int logging = 0;
194 /* Set these constants to be a file substring and line number to start logging. */
195 static const char * start_file = "tests/views";
196 static int start_line = 27;
197
198 static void
199 showlog(int depth, int lineno, PyObject * filename, const char * msg)
200 {
201 if (logging) {
202 printf("%s%3d ", indent(depth), depth);
203 if (lineno) {
204 printf("%4d", lineno);
205 }
206 else {
207 printf(" ");
208 }
209 if (filename) {
210 printf(" %s", MyText_AS_STRING(filename));
211 }
212 if (msg) {
213 printf(" %s", msg);
214 }
215 printf("\n");
216 }
217 }
218
219 #define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
220 #else
221 #define SHOWLOG(a,b,c,d)
222 #endif /* TRACE_LOG */
223
224 #if WHAT_LOG
225 static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
226 #endif
227
228 /* Record a pair of integers in self->cur_file_data. */
229 static int
230 Tracer_record_pair(Tracer *self, int l1, int l2)
231 {
232 int ret = RET_OK;
233
234 PyObject * t = PyTuple_New(2);
235 if (t != NULL) {
236 PyTuple_SET_ITEM(t, 0, MyInt_FromLong(l1));
237 PyTuple_SET_ITEM(t, 1, MyInt_FromLong(l2));
238 if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) {
239 STATS( self->stats.errors++; )
240 ret = RET_ERROR;
241 }
242 Py_DECREF(t);
243 }
244 else {
245 STATS( self->stats.errors++; )
246 ret = RET_ERROR;
247 }
248 return ret;
249 }
250
251 /*
252 * The Trace Function
253 */
254 static int
255 Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
256 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 257 int ret = RET_OK;
258 PyObject * filename = NULL;
259 PyObject * tracename = NULL;
260
261 #if WHAT_LOG
262 if (what <= sizeof(what_sym)/sizeof(const char *)) {
263 printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(frame->f_code->co_filename), frame->f_lineno);
264 }
265 #endif
266
267 #if TRACE_LOG
268 if (strstr(MyText_AS_STRING(frame->f_code->co_filename), start_file) && frame->f_lineno == start_line) {
269 logging = 1;
270 }
271 #endif
272
273 /* See below for details on missing-return detection. */
274 if (self->last_exc_back) {
275 if (frame == self->last_exc_back) {
276 /* Looks like someone forgot to send a return event. We'll clear
277 the exception state and do the RETURN code here. Notice that the
278 frame we have in hand here is not the correct frame for the RETURN,
279 that frame is gone. Our handling for RETURN doesn't need the
280 actual frame, but we do log it, so that will look a little off if
281 you're looking at the detailed log.
282
283 If someday we need to examine the frame when doing RETURN, then
284 we'll need to keep more of the missed frame's state.
285 */
286 STATS( self->stats.missed_returns++; )
287 if (self->depth >= 0) {
288 if (self->tracing_arcs && self->cur_file_data) {
289 if (Tracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) {
290 return RET_ERROR;
291 }
292 }
293 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn");
294 self->cur_file_data = self->data_stack[self->depth].file_data;
295 self->last_line = self->data_stack[self->depth].last_line;
296 self->depth--;
297 }
298 }
299 self->last_exc_back = NULL;
300 }
301
302
303 switch (what) {
304 case PyTrace_CALL: /* 0 */
305 STATS( self->stats.calls++; )
306 /* Grow the stack. */
307 self->depth++;
308 if (self->depth >= self->data_stack_alloc) {
309 STATS( self->stats.stack_reallocs++; )
310 /* We've outgrown our data_stack array: make it bigger. */
311 int bigger = self->data_stack_alloc + STACK_DELTA;
312 DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack, bigger * sizeof(DataStackEntry));
313 if (bigger_data_stack == NULL) {
314 STATS( self->stats.errors++; )
315 PyErr_NoMemory();
316 self->depth--;
317 return RET_ERROR;
318 }
319 self->data_stack = bigger_data_stack;
320 self->data_stack_alloc = bigger;
321 }
322
323 /* Push the current state on the stack. */
324 self->data_stack[self->depth].file_data = self->cur_file_data;
325 self->data_stack[self->depth].last_line = self->last_line;
326
327 /* Check if we should trace this line. */
328 filename = frame->f_code->co_filename;
329 tracename = PyDict_GetItem(self->should_trace_cache, filename);
330 if (tracename == NULL) {
331 STATS( self->stats.new_files++; )
332 /* We've never considered this file before. */
333 /* Ask should_trace about it. */
334 PyObject * args = Py_BuildValue("(OO)", filename, frame);
335 tracename = PyObject_Call(self->should_trace, args, NULL);
336 Py_DECREF(args);
337 if (tracename == NULL) {
338 /* An error occurred inside should_trace. */
339 STATS( self->stats.errors++; )
340 return RET_ERROR;
341 }
342 if (PyDict_SetItem(self->should_trace_cache, filename, tracename) < 0) {
343 STATS( self->stats.errors++; )
344 return RET_ERROR;
345 }
346 }
347 else {
348 Py_INCREF(tracename);
349 }
350
351 /* If tracename is a string, then we're supposed to trace. */
352 if (MyText_Check(tracename)) {
353 PyObject * file_data = PyDict_GetItem(self->data, tracename);
354 if (file_data == NULL) {
355 file_data = PyDict_New();
356 if (file_data == NULL) {
357 STATS( self->stats.errors++; )
358 return RET_ERROR;
359 }
360 ret = PyDict_SetItem(self->data, tracename, file_data);
361 Py_DECREF(file_data);
362 if (ret < 0) {
363 STATS( self->stats.errors++; )
364 return RET_ERROR;
365 }
366 }
367 self->cur_file_data = file_data;
368 /* Make the frame right in case settrace(gettrace()) happens. */
369 Py_INCREF(self);
370 frame->f_trace = (PyObject*)self;
371 SHOWLOG(self->depth, frame->f_lineno, filename, "traced");
372 }
373 else {
374 self->cur_file_data = NULL;
375 SHOWLOG(self->depth, frame->f_lineno, filename, "skipped");
376 }
377
378 Py_DECREF(tracename);
379
380 self->last_line = -1;
381 break;
382
383 case PyTrace_RETURN: /* 3 */
384 STATS( self->stats.returns++; )
385 /* A near-copy of this code is above in the missing-return handler. */
386 if (self->depth >= 0) {
387 if (self->tracing_arcs && self->cur_file_data) {
388 int first = frame->f_code->co_firstlineno;
389 if (Tracer_record_pair(self, self->last_line, -first) < 0) {
390 return RET_ERROR;
391 }
392 }
393
394 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "return");
395 self->cur_file_data = self->data_stack[self->depth].file_data;
396 self->last_line = self->data_stack[self->depth].last_line;
397 self->depth--;
398 }
399 break;
400
401 case PyTrace_LINE: /* 2 */
402 STATS( self->stats.lines++; )
403 if (self->depth >= 0) {
404 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "line");
405 if (self->cur_file_data) {
406 /* We're tracing in this frame: record something. */
407 if (self->tracing_arcs) {
408 /* Tracing arcs: key is (last_line,this_line). */
409 if (Tracer_record_pair(self, self->last_line, frame->f_lineno) < 0) {
410 return RET_ERROR;
411 }
412 }
413 else {
414 /* Tracing lines: key is simply this_line. */
415 PyObject * this_line = MyInt_FromLong(frame->f_lineno);
416 if (this_line == NULL) {
417 STATS( self->stats.errors++; )
418 return RET_ERROR;
419 }
420 ret = PyDict_SetItem(self->cur_file_data, this_line, Py_None);
421 Py_DECREF(this_line);
422 if (ret < 0) {
423 STATS( self->stats.errors++; )
424 return RET_ERROR;
425 }
426 }
427 }
428 self->last_line = frame->f_lineno;
429 }
430 break;
431
432 case PyTrace_EXCEPTION:
433 /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
434 without a return event. To detect that, we'll keep a copy of the
435 parent frame for an exception event. If the next event is in that
436 frame, then we must have returned without a return event. We can
437 synthesize the missing event then.
438
439 Python itself fixed this problem in 2.4. Pyexpat still has the bug.
440 I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
441 If it gets fixed, this code should still work properly. Maybe some day
442 the bug will be fixed everywhere coverage.py is supported, and we can
443 remove this missing-return detection.
444
445 More about this fix: http://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
446 */
447 STATS( self->stats.exceptions++; )
448 self->last_exc_back = frame->f_back;
449 self->last_exc_firstlineno = frame->f_code->co_firstlineno;
450 break;
451
452 default:
453 STATS( self->stats.others++; )
454 break;
455 }
456
457 return RET_OK;
458 }
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
459
460 /*
461 * Python has two ways to set the trace function: sys.settrace(fn), which
462 * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
463 * a C function and a Python object. The way these work together is that
464 * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
465 * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
466 * simply returns the Python object used as the second argument to
467 * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
468 * means it must be callable to be used in sys.settrace().
469 *
470 * So we make our self callable, equivalent to invoking our trace function.
471 *
472 * To help with the process of replaying stored frames, this function has an
473 * optional keyword argument:
474 *
475 * def Tracer_call(frame, event, arg, lineno=0)
476 *
477 * If provided, the lineno argument is used as the line number, and the
478 * frame's f_lineno member is ignored.
479 */
480 static PyObject *
481 Tracer_call(Tracer *self, PyObject *args, PyObject *kwds)
482 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 483 PyFrameObject *frame;
484 PyObject *what_str;
485 PyObject *arg;
486 int lineno = 0;
487 int what;
488 int orig_lineno;
489 PyObject *ret = NULL;
490
491 static char *what_names[] = {
492 "call", "exception", "line", "return",
493 "c_call", "c_exception", "c_return",
494 NULL
495 };
496
497 #if WHAT_LOG
498 printf("pytrace\n");
499 #endif
500
501 static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
502
503 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
504 &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
505 goto done;
506 }
507
508 /* In Python, the what argument is a string, we need to find an int
509 for the C function. */
510 for (what = 0; what_names[what]; what++) {
511 if (!strcmp(MyText_AS_STRING(what_str), what_names[what])) {
512 break;
513 }
514 }
515
516 /* Save off the frame's lineno, and use the forced one, if provided. */
517 orig_lineno = frame->f_lineno;
518 if (lineno > 0) {
519 frame->f_lineno = lineno;
520 }
521
522 /* Invoke the C function, and return ourselves. */
523 if (Tracer_trace(self, frame, what, arg) == RET_OK) {
524 Py_INCREF(self);
525 ret = (PyObject *)self;
526 }
527
528 /* Clean up. */
529 frame->f_lineno = orig_lineno;
530
531 done:
532 return ret;
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
533 }
534
535 static PyObject *
536 Tracer_start(Tracer *self, PyObject *args_unused)
537 {
538 PyEval_SetTrace((Py_tracefunc)Tracer_trace, (PyObject*)self);
539 self->started = 1;
540 self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs);
541 self->last_line = -1;
542
543 /* start() returns a trace function usable with sys.settrace() */
544 Py_INCREF(self);
545 return (PyObject *)self;
546 }
547
548 static PyObject *
549 Tracer_stop(Tracer *self, PyObject *args_unused)
550 {
551 if (self->started) {
552 PyEval_SetTrace(NULL, NULL);
553 self->started = 0;
554 }
555
556 return Py_BuildValue("");
557 }
558
559 static PyObject *
560 Tracer_get_stats(Tracer *self)
561 {
562 #if COLLECT_STATS
563 return Py_BuildValue(
564 "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI}",
565 "calls", self->stats.calls,
566 "lines", self->stats.lines,
567 "returns", self->stats.returns,
568 "exceptions", self->stats.exceptions,
569 "others", self->stats.others,
570 "new_files", self->stats.new_files,
571 "missed_returns", self->stats.missed_returns,
572 "stack_reallocs", self->stats.stack_reallocs,
573 "stack_alloc", self->data_stack_alloc,
574 "errors", self->stats.errors
575 );
576 #else
577 return Py_BuildValue("");
578 #endif /* COLLECT_STATS */
579 }
580
581 static PyMemberDef
582 Tracer_members[] = {
583 { "should_trace", T_OBJECT, offsetof(Tracer, should_trace), 0,
584 PyDoc_STR("Function indicating whether to trace a file.") },
585
586 { "warn", T_OBJECT, offsetof(Tracer, warn), 0,
587 PyDoc_STR("Function for issuing warnings.") },
588
589 { "data", T_OBJECT, offsetof(Tracer, data), 0,
590 PyDoc_STR("The raw dictionary of trace data.") },
591
592 { "should_trace_cache", T_OBJECT, offsetof(Tracer, should_trace_cache), 0,
593 PyDoc_STR("Dictionary caching should_trace results.") },
594
595 { "arcs", T_OBJECT, offsetof(Tracer, arcs), 0,
596 PyDoc_STR("Should we trace arcs, or just lines?") },
597
598 { NULL }
599 };
600
601 static PyMethodDef
602 Tracer_methods[] = {
603 { "start", (PyCFunction) Tracer_start, METH_VARARGS,
604 PyDoc_STR("Start the tracer") },
605
606 { "stop", (PyCFunction) Tracer_stop, METH_VARARGS,
607 PyDoc_STR("Stop the tracer") },
608
609 { "get_stats", (PyCFunction) Tracer_get_stats, METH_VARARGS,
(emitted by cpychecker) 610 PyDoc_STR("Get statistics about the tracing") },
611
612 { NULL }
613 };
614
615 static PyTypeObject
616 TracerType = {
617 MyType_HEAD_INIT
618 "coverage.Tracer", /*tp_name*/
619 sizeof(Tracer), /*tp_basicsize*/
620 0, /*tp_itemsize*/
621 (destructor)Tracer_dealloc, /*tp_dealloc*/
622 0, /*tp_print*/
623 0, /*tp_getattr*/
624 0, /*tp_setattr*/
625 0, /*tp_compare*/
626 0, /*tp_repr*/
627 0, /*tp_as_number*/
628 0, /*tp_as_sequence*/
629 0, /*tp_as_mapping*/
630 0, /*tp_hash */
631 (ternaryfunc)Tracer_call, /*tp_call*/
632 0, /*tp_str*/
633 0, /*tp_getattro*/
634 0, /*tp_setattro*/
635 0, /*tp_as_buffer*/
636 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
637 "Tracer objects", /* tp_doc */
638 0, /* tp_traverse */
639 0, /* tp_clear */
640 0, /* tp_richcompare */
641 0, /* tp_weaklistoffset */
642 0, /* tp_iter */
643 0, /* tp_iternext */
644 Tracer_methods, /* tp_methods */
645 Tracer_members, /* tp_members */
646 0, /* tp_getset */
647 0, /* tp_base */
648 0, /* tp_dict */
649 0, /* tp_descr_get */
650 0, /* tp_descr_set */
651 0, /* tp_dictoffset */
652 (initproc)Tracer_init, /* tp_init */
653 0, /* tp_alloc */
654 0, /* tp_new */
655 };
656
657 /* Module definition */
658
659 #define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
660
661 #if PY_MAJOR_VERSION >= 3
662
663 static PyModuleDef
664 moduledef = {
665 PyModuleDef_HEAD_INIT,
666 "coverage.tracer",
667 MODULE_DOC,
668 -1,
669 NULL, /* methods */
670 NULL,
671 NULL, /* traverse */
672 NULL, /* clear */
673 NULL
674 };
675
676
677 PyObject *
678 PyInit_tracer(void)
679 {
680 PyObject * mod = PyModule_Create(&moduledef);
681 if (mod == NULL) {
682 return NULL;
683 }
684
685 TracerType.tp_new = PyType_GenericNew;
686 if (PyType_Ready(&TracerType) < 0) {
687 Py_DECREF(mod);
688 return NULL;
689 }
690
691 Py_INCREF(&TracerType);
692 PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType);
693
694 return mod;
695 }
696
697 #else
698
699 void
700 inittracer(void)
701 {
702 PyObject * mod;
703
704 mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
705 if (mod == NULL) {
706 return;
707 }
708
709 TracerType.tp_new = PyType_GenericNew;
710 if (PyType_Ready(&TracerType) < 0) {
711 return;
712 }
713
714 Py_INCREF(&TracerType);
715 PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType);
traceback: Traceback (most recent call last):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/__init__.py", line 131, in _check_refcounts
self.options)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4355, in check_refcounts
rep = impl_check_refcounts(ctxt, fun, options)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4213, in impl_check_refcounts
limits=limits)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3053, in iter_traces
transitions = curstate.get_transitions()
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2105, in get_transitions
return self._get_transitions_for_stmt(stmt)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2121, in _get_transitions_for_stmt
return self._get_transitions_for_GimpleCall(stmt)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2292, in _get_transitions_for_GimpleCall
return meth(stmt, *args)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 2544, in impl_PyModule_AddObject
s_success.cpython.steal_reference(v_value, stmt.loc)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 741, in steal_reference
_steal_ref)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 551, in change_refcount
check_isinstance(oldvalue, RefcountValue)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/gccutils.py", line 642, in check_isinstance
raise TypeError('%s / %r is not an instance of %s' % (obj, obj, types))
TypeError: (long int)val [-0x7fffffffffffffff <= val <= 0x7fffffffffffffff] from coverage/tracer.c:714 / WithinRange(gcctype='long int', loc=gcc.Location(file='coverage/tracer.c', line=714), minvalue=-0x7fffffffffffffff, maxvalue=0x7fffffffffffffff) is not an instance of <class 'libcpychecker.refcounts.RefcountValue'>
716 }
717
718 #endif /* Py3k */
Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
coverage/tracer.c:458:1 | cpychecker | refcount-too-high | Tracer_trace | ob_refcnt of '*self' is 1 too high |
coverage/tracer.c:458:1 | cpychecker | refcount-too-high | Tracer_trace | ob_refcnt of '*tracename' is 1 too high |
coverage/tracer.c:532:5 | cpychecker | refcount-too-high | Tracer_call | ob_refcnt of new ref from (unknown) PyUnicodeUCS4_AsASCIIString is 1 too high |
coverage/tracer.c:532:5 | cpychecker | returns-NULL-without-setting-exception | Tracer_call | returning (PyObject*)NULL without setting an exception |
coverage/tracer.c:609:35 | cpychecker | flags-within-PyMethodDef | flags do not match callback signature for 'Tracer_get_stats' within PyMethodDef table |
Tool | Location | Function | Message | Data |
---|---|---|---|---|
cpychecker | coverage/tracer.c:165:1 | Tracer_dealloc | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:256:1 | Tracer_trace | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:482:1 | Tracer_call | Message(text='this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') | CustomFields([('maxtrans', 256)]) |
cpychecker | coverage/tracer.c:692:23 | PyInit_tracer | None | CustomFields([('traceback', 'Traceback (most recent call last):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/__init__.py", line 131, in _check_refcounts\n self.options)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4355, in check_refcounts\n rep = impl_check_refcounts(ctxt, fun, options)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4213, in impl_check_refcounts\n limits=limits)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces\n depth + 1):\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3053, in iter_traces\n transitions = curstate.get_transitions()\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2105, in get_transitions\n return self._get_transitions_for_stmt(stmt)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2121, in _get_transitions_for_stmt\n return self._get_transitions_for_GimpleCall(stmt)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2292, in _get_transitions_for_GimpleCall\n return meth(stmt, *args)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 2544, in impl_PyModule_AddObject\n s_success.cpython.steal_reference(v_value, stmt.loc)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 741, in steal_reference\n _steal_ref)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 551, in change_refcount\n check_isinstance(oldvalue, RefcountValue)\n File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/gccutils.py", line 642, in check_isinstance\n raise TypeError(\'%s / %r is not an instance of %s\' % (obj, obj, types))\nTypeError: (long int)val [-0x7fffffffffffffff <= val <= 0x7fffffffffffffff] from coverage/tracer.c:691 / WithinRange(gcctype=\'long int\', loc=gcc.Location(file=\'coverage/tracer.c\', line=691), minvalue=-0x7fffffffffffffff, maxvalue=0x7fffffffffffffff) is not an instance of |
1 /* C-based Tracer for Coverage. */
2
3 #include "Python.h"
4 #include "compile.h" /* in 2.3, this wasn't part of Python.h */
5 #include "eval.h" /* or this. */
6 #include "structmember.h"
7 #include "frameobject.h"
8
9 /* Compile-time debugging helpers */
10 #undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
11 #undef TRACE_LOG /* Define to log our bookkeeping. */
12 #undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
13
14 #if COLLECT_STATS
15 #define STATS(x) x
16 #else
17 #define STATS(x)
18 #endif
19
20 /* Py 2.x and 3.x compatibility */
21
22 #ifndef Py_TYPE
23 #define Py_TYPE(o) (((PyObject*)(o))->ob_type)
24 #endif
25
26 #if PY_MAJOR_VERSION >= 3
27
28 #define MyText_Type PyUnicode_Type
29 #define MyText_Check(o) PyUnicode_Check(o)
30 #define MyText_AS_STRING(o) PyBytes_AS_STRING(PyUnicode_AsASCIIString(o))
31 #define MyInt_FromLong(l) PyLong_FromLong(l)
32
33 #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
34
35 #else
36
37 #define MyText_Type PyString_Type
38 #define MyText_Check(o) PyString_Check(o)
39 #define MyText_AS_STRING(o) PyString_AS_STRING(o)
40 #define MyInt_FromLong(l) PyInt_FromLong(l)
41
42 #define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
43
44 #endif /* Py3k */
45
46 /* The values returned to indicate ok or error. */
47 #define RET_OK 0
48 #define RET_ERROR -1
49
50 /* An entry on the data stack. For each call frame, we need to record the
51 dictionary to capture data, and the last line number executed in that
52 frame.
53 */
54 typedef struct {
55 PyObject * file_data; /* PyMem_Malloc'ed, a borrowed ref. */
56 int last_line;
57 } DataStackEntry;
58
59 /* The Tracer type. */
60
61 typedef struct {
62 PyObject_HEAD
63
64 /* Python objects manipulated directly by the Collector class. */
65 PyObject * should_trace;
66 PyObject * warn;
67 PyObject * data;
68 PyObject * should_trace_cache;
69 PyObject * arcs;
70
71 /* Has the tracer been started? */
72 int started;
73 /* Are we tracing arcs, or just lines? */
74 int tracing_arcs;
75
76 /*
77 The data stack is a stack of dictionaries. Each dictionary collects
78 data for a single source file. The data stack parallels the call stack:
79 each call pushes the new frame's file data onto the data stack, and each
80 return pops file data off.
81
82 The file data is a dictionary whose form depends on the tracing options.
83 If tracing arcs, the keys are line number pairs. If not tracing arcs,
84 the keys are line numbers. In both cases, the value is irrelevant
85 (None).
86 */
87 /* The index of the last-used entry in data_stack. */
88 int depth;
89 /* The file data at each level, or NULL if not recording. */
90 DataStackEntry * data_stack;
91 int data_stack_alloc; /* number of entries allocated at data_stack. */
92
93 /* The current file_data dictionary. Borrowed. */
94 PyObject * cur_file_data;
95
96 /* The line number of the last line recorded, for tracing arcs.
97 -1 means there was no previous line, as when entering a code object.
98 */
99 int last_line;
100
101 /* The parent frame for the last exception event, to fix missing returns. */
102 PyFrameObject * last_exc_back;
103 int last_exc_firstlineno;
104
105 #if COLLECT_STATS
106 struct {
107 unsigned int calls;
108 unsigned int lines;
109 unsigned int returns;
110 unsigned int exceptions;
111 unsigned int others;
112 unsigned int new_files;
113 unsigned int missed_returns;
114 unsigned int stack_reallocs;
115 unsigned int errors;
116 } stats;
117 #endif /* COLLECT_STATS */
118 } Tracer;
119
120 #define STACK_DELTA 100
121
122 static int
123 Tracer_init(Tracer *self, PyObject *args_unused, PyObject *kwds_unused)
124 {
125 #if COLLECT_STATS
126 self->stats.calls = 0;
127 self->stats.lines = 0;
128 self->stats.returns = 0;
129 self->stats.exceptions = 0;
130 self->stats.others = 0;
131 self->stats.new_files = 0;
132 self->stats.missed_returns = 0;
133 self->stats.stack_reallocs = 0;
134 self->stats.errors = 0;
135 #endif /* COLLECT_STATS */
136
137 self->should_trace = NULL;
138 self->warn = NULL;
139 self->data = NULL;
140 self->should_trace_cache = NULL;
141 self->arcs = NULL;
142
143 self->started = 0;
144 self->tracing_arcs = 0;
145
146 self->depth = -1;
147 self->data_stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry));
148 if (self->data_stack == NULL) {
149 STATS( self->stats.errors++; )
150 PyErr_NoMemory();
151 return RET_ERROR;
152 }
153 self->data_stack_alloc = STACK_DELTA;
154
155 self->cur_file_data = NULL;
156 self->last_line = -1;
157
158 self->last_exc_back = NULL;
159
160 return RET_OK;
161 }
162
163 static void
164 Tracer_dealloc(Tracer *self)
165 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 166 if (self->started) {
167 PyEval_SetTrace(NULL, NULL);
168 }
169
170 Py_XDECREF(self->should_trace);
171 Py_XDECREF(self->warn);
172 Py_XDECREF(self->data);
173 Py_XDECREF(self->should_trace_cache);
174
175 PyMem_Free(self->data_stack);
176
177 Py_TYPE(self)->tp_free((PyObject*)self);
178 }
179
180 #if TRACE_LOG
181 static const char *
182 indent(int n)
183 {
184 static const char * spaces =
185 " "
186 " "
187 " "
188 " "
189 ;
190 return spaces + strlen(spaces) - n*2;
191 }
192
193 static int logging = 0;
194 /* Set these constants to be a file substring and line number to start logging. */
195 static const char * start_file = "tests/views";
196 static int start_line = 27;
197
198 static void
199 showlog(int depth, int lineno, PyObject * filename, const char * msg)
200 {
201 if (logging) {
202 printf("%s%3d ", indent(depth), depth);
203 if (lineno) {
204 printf("%4d", lineno);
205 }
206 else {
207 printf(" ");
208 }
209 if (filename) {
210 printf(" %s", MyText_AS_STRING(filename));
211 }
212 if (msg) {
213 printf(" %s", msg);
214 }
215 printf("\n");
216 }
217 }
218
219 #define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
220 #else
221 #define SHOWLOG(a,b,c,d)
222 #endif /* TRACE_LOG */
223
224 #if WHAT_LOG
225 static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
226 #endif
227
228 /* Record a pair of integers in self->cur_file_data. */
229 static int
230 Tracer_record_pair(Tracer *self, int l1, int l2)
231 {
232 int ret = RET_OK;
233
234 PyObject * t = PyTuple_New(2);
235 if (t != NULL) {
236 PyTuple_SET_ITEM(t, 0, MyInt_FromLong(l1));
237 PyTuple_SET_ITEM(t, 1, MyInt_FromLong(l2));
238 if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) {
239 STATS( self->stats.errors++; )
240 ret = RET_ERROR;
241 }
242 Py_DECREF(t);
243 }
244 else {
245 STATS( self->stats.errors++; )
246 ret = RET_ERROR;
247 }
248 return ret;
249 }
250
251 /*
252 * The Trace Function
253 */
254 static int
255 Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
256 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 257 int ret = RET_OK;
258 PyObject * filename = NULL;
259 PyObject * tracename = NULL;
260
261 #if WHAT_LOG
262 if (what <= sizeof(what_sym)/sizeof(const char *)) {
263 printf("trace: %s @ %s %d\n", what_sym[what], MyText_AS_STRING(frame->f_code->co_filename), frame->f_lineno);
264 }
265 #endif
266
267 #if TRACE_LOG
268 if (strstr(MyText_AS_STRING(frame->f_code->co_filename), start_file) && frame->f_lineno == start_line) {
269 logging = 1;
270 }
271 #endif
272
273 /* See below for details on missing-return detection. */
274 if (self->last_exc_back) {
275 if (frame == self->last_exc_back) {
276 /* Looks like someone forgot to send a return event. We'll clear
277 the exception state and do the RETURN code here. Notice that the
278 frame we have in hand here is not the correct frame for the RETURN,
279 that frame is gone. Our handling for RETURN doesn't need the
280 actual frame, but we do log it, so that will look a little off if
281 you're looking at the detailed log.
282
283 If someday we need to examine the frame when doing RETURN, then
284 we'll need to keep more of the missed frame's state.
285 */
286 STATS( self->stats.missed_returns++; )
287 if (self->depth >= 0) {
288 if (self->tracing_arcs && self->cur_file_data) {
289 if (Tracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) {
290 return RET_ERROR;
291 }
292 }
293 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn");
294 self->cur_file_data = self->data_stack[self->depth].file_data;
295 self->last_line = self->data_stack[self->depth].last_line;
296 self->depth--;
297 }
298 }
299 self->last_exc_back = NULL;
300 }
301
302
303 switch (what) {
304 case PyTrace_CALL: /* 0 */
305 STATS( self->stats.calls++; )
306 /* Grow the stack. */
307 self->depth++;
308 if (self->depth >= self->data_stack_alloc) {
309 STATS( self->stats.stack_reallocs++; )
310 /* We've outgrown our data_stack array: make it bigger. */
311 int bigger = self->data_stack_alloc + STACK_DELTA;
312 DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack, bigger * sizeof(DataStackEntry));
313 if (bigger_data_stack == NULL) {
314 STATS( self->stats.errors++; )
315 PyErr_NoMemory();
316 self->depth--;
317 return RET_ERROR;
318 }
319 self->data_stack = bigger_data_stack;
320 self->data_stack_alloc = bigger;
321 }
322
323 /* Push the current state on the stack. */
324 self->data_stack[self->depth].file_data = self->cur_file_data;
325 self->data_stack[self->depth].last_line = self->last_line;
326
327 /* Check if we should trace this line. */
328 filename = frame->f_code->co_filename;
329 tracename = PyDict_GetItem(self->should_trace_cache, filename);
330 if (tracename == NULL) {
331 STATS( self->stats.new_files++; )
332 /* We've never considered this file before. */
333 /* Ask should_trace about it. */
334 PyObject * args = Py_BuildValue("(OO)", filename, frame);
335 tracename = PyObject_Call(self->should_trace, args, NULL);
336 Py_DECREF(args);
337 if (tracename == NULL) {
338 /* An error occurred inside should_trace. */
339 STATS( self->stats.errors++; )
340 return RET_ERROR;
341 }
342 if (PyDict_SetItem(self->should_trace_cache, filename, tracename) < 0) {
343 STATS( self->stats.errors++; )
344 return RET_ERROR;
345 }
346 }
347 else {
348 Py_INCREF(tracename);
349 }
350
351 /* If tracename is a string, then we're supposed to trace. */
352 if (MyText_Check(tracename)) {
353 PyObject * file_data = PyDict_GetItem(self->data, tracename);
354 if (file_data == NULL) {
355 file_data = PyDict_New();
356 if (file_data == NULL) {
357 STATS( self->stats.errors++; )
358 return RET_ERROR;
359 }
360 ret = PyDict_SetItem(self->data, tracename, file_data);
361 Py_DECREF(file_data);
362 if (ret < 0) {
363 STATS( self->stats.errors++; )
364 return RET_ERROR;
365 }
366 }
367 self->cur_file_data = file_data;
368 /* Make the frame right in case settrace(gettrace()) happens. */
369 Py_INCREF(self);
370 frame->f_trace = (PyObject*)self;
371 SHOWLOG(self->depth, frame->f_lineno, filename, "traced");
372 }
373 else {
374 self->cur_file_data = NULL;
375 SHOWLOG(self->depth, frame->f_lineno, filename, "skipped");
376 }
377
378 Py_DECREF(tracename);
379
380 self->last_line = -1;
381 break;
382
383 case PyTrace_RETURN: /* 3 */
384 STATS( self->stats.returns++; )
385 /* A near-copy of this code is above in the missing-return handler. */
386 if (self->depth >= 0) {
387 if (self->tracing_arcs && self->cur_file_data) {
388 int first = frame->f_code->co_firstlineno;
389 if (Tracer_record_pair(self, self->last_line, -first) < 0) {
390 return RET_ERROR;
391 }
392 }
393
394 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "return");
395 self->cur_file_data = self->data_stack[self->depth].file_data;
396 self->last_line = self->data_stack[self->depth].last_line;
397 self->depth--;
398 }
399 break;
400
401 case PyTrace_LINE: /* 2 */
402 STATS( self->stats.lines++; )
403 if (self->depth >= 0) {
404 SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "line");
405 if (self->cur_file_data) {
406 /* We're tracing in this frame: record something. */
407 if (self->tracing_arcs) {
408 /* Tracing arcs: key is (last_line,this_line). */
409 if (Tracer_record_pair(self, self->last_line, frame->f_lineno) < 0) {
410 return RET_ERROR;
411 }
412 }
413 else {
414 /* Tracing lines: key is simply this_line. */
415 PyObject * this_line = MyInt_FromLong(frame->f_lineno);
416 if (this_line == NULL) {
417 STATS( self->stats.errors++; )
418 return RET_ERROR;
419 }
420 ret = PyDict_SetItem(self->cur_file_data, this_line, Py_None);
421 Py_DECREF(this_line);
422 if (ret < 0) {
423 STATS( self->stats.errors++; )
424 return RET_ERROR;
425 }
426 }
427 }
428 self->last_line = frame->f_lineno;
429 }
430 break;
431
432 case PyTrace_EXCEPTION:
433 /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
434 without a return event. To detect that, we'll keep a copy of the
435 parent frame for an exception event. If the next event is in that
436 frame, then we must have returned without a return event. We can
437 synthesize the missing event then.
438
439 Python itself fixed this problem in 2.4. Pyexpat still has the bug.
440 I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
441 If it gets fixed, this code should still work properly. Maybe some day
442 the bug will be fixed everywhere coverage.py is supported, and we can
443 remove this missing-return detection.
444
445 More about this fix: http://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
446 */
447 STATS( self->stats.exceptions++; )
448 self->last_exc_back = frame->f_back;
449 self->last_exc_firstlineno = frame->f_code->co_firstlineno;
450 break;
451
452 default:
453 STATS( self->stats.others++; )
454 break;
455 }
456
457 return RET_OK;
458 }
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
459
460 /*
461 * Python has two ways to set the trace function: sys.settrace(fn), which
462 * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
463 * a C function and a Python object. The way these work together is that
464 * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
465 * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
466 * simply returns the Python object used as the second argument to
467 * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
468 * means it must be callable to be used in sys.settrace().
469 *
470 * So we make our self callable, equivalent to invoking our trace function.
471 *
472 * To help with the process of replaying stored frames, this function has an
473 * optional keyword argument:
474 *
475 * def Tracer_call(frame, event, arg, lineno=0)
476 *
477 * If provided, the lineno argument is used as the line number, and the
478 * frame's f_lineno member is ignored.
479 */
480 static PyObject *
481 Tracer_call(Tracer *self, PyObject *args, PyObject *kwds)
482 {
Message this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed maxtrans: 256 483 PyFrameObject *frame;
484 PyObject *what_str;
485 PyObject *arg;
486 int lineno = 0;
487 int what;
488 int orig_lineno;
489 PyObject *ret = NULL;
490
491 static char *what_names[] = {
492 "call", "exception", "line", "return",
493 "c_call", "c_exception", "c_return",
494 NULL
495 };
496
497 #if WHAT_LOG
498 printf("pytrace\n");
499 #endif
500
501 static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
502
503 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
504 &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
505 goto done;
506 }
507
508 /* In Python, the what argument is a string, we need to find an int
509 for the C function. */
510 for (what = 0; what_names[what]; what++) {
511 if (!strcmp(MyText_AS_STRING(what_str), what_names[what])) {
512 break;
513 }
514 }
515
516 /* Save off the frame's lineno, and use the forced one, if provided. */
517 orig_lineno = frame->f_lineno;
518 if (lineno > 0) {
519 frame->f_lineno = lineno;
520 }
521
522 /* Invoke the C function, and return ourselves. */
523 if (Tracer_trace(self, frame, what, arg) == RET_OK) {
524 Py_INCREF(self);
525 ret = (PyObject *)self;
526 }
527
528 /* Clean up. */
529 frame->f_lineno = orig_lineno;
530
531 done:
532 return ret;
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by cpychecker)TODO: a detailed trace is available in the data model (not yet rendered in this report)
533 }
534
535 static PyObject *
536 Tracer_start(Tracer *self, PyObject *args_unused)
537 {
538 PyEval_SetTrace((Py_tracefunc)Tracer_trace, (PyObject*)self);
539 self->started = 1;
540 self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs);
541 self->last_line = -1;
542
543 /* start() returns a trace function usable with sys.settrace() */
544 Py_INCREF(self);
545 return (PyObject *)self;
546 }
547
548 static PyObject *
549 Tracer_stop(Tracer *self, PyObject *args_unused)
550 {
551 if (self->started) {
552 PyEval_SetTrace(NULL, NULL);
553 self->started = 0;
554 }
555
556 return Py_BuildValue("");
557 }
558
559 static PyObject *
560 Tracer_get_stats(Tracer *self)
561 {
562 #if COLLECT_STATS
563 return Py_BuildValue(
564 "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI}",
565 "calls", self->stats.calls,
566 "lines", self->stats.lines,
567 "returns", self->stats.returns,
568 "exceptions", self->stats.exceptions,
569 "others", self->stats.others,
570 "new_files", self->stats.new_files,
571 "missed_returns", self->stats.missed_returns,
572 "stack_reallocs", self->stats.stack_reallocs,
573 "stack_alloc", self->data_stack_alloc,
574 "errors", self->stats.errors
575 );
576 #else
577 return Py_BuildValue("");
578 #endif /* COLLECT_STATS */
579 }
580
581 static PyMemberDef
582 Tracer_members[] = {
583 { "should_trace", T_OBJECT, offsetof(Tracer, should_trace), 0,
584 PyDoc_STR("Function indicating whether to trace a file.") },
585
586 { "warn", T_OBJECT, offsetof(Tracer, warn), 0,
587 PyDoc_STR("Function for issuing warnings.") },
588
589 { "data", T_OBJECT, offsetof(Tracer, data), 0,
590 PyDoc_STR("The raw dictionary of trace data.") },
591
592 { "should_trace_cache", T_OBJECT, offsetof(Tracer, should_trace_cache), 0,
593 PyDoc_STR("Dictionary caching should_trace results.") },
594
595 { "arcs", T_OBJECT, offsetof(Tracer, arcs), 0,
596 PyDoc_STR("Should we trace arcs, or just lines?") },
597
598 { NULL }
599 };
600
601 static PyMethodDef
602 Tracer_methods[] = {
603 { "start", (PyCFunction) Tracer_start, METH_VARARGS,
604 PyDoc_STR("Start the tracer") },
605
606 { "stop", (PyCFunction) Tracer_stop, METH_VARARGS,
607 PyDoc_STR("Stop the tracer") },
608
609 { "get_stats", (PyCFunction) Tracer_get_stats, METH_VARARGS,
(emitted by cpychecker) 610 PyDoc_STR("Get statistics about the tracing") },
611
612 { NULL }
613 };
614
615 static PyTypeObject
616 TracerType = {
617 MyType_HEAD_INIT
618 "coverage.Tracer", /*tp_name*/
619 sizeof(Tracer), /*tp_basicsize*/
620 0, /*tp_itemsize*/
621 (destructor)Tracer_dealloc, /*tp_dealloc*/
622 0, /*tp_print*/
623 0, /*tp_getattr*/
624 0, /*tp_setattr*/
625 0, /*tp_compare*/
626 0, /*tp_repr*/
627 0, /*tp_as_number*/
628 0, /*tp_as_sequence*/
629 0, /*tp_as_mapping*/
630 0, /*tp_hash */
631 (ternaryfunc)Tracer_call, /*tp_call*/
632 0, /*tp_str*/
633 0, /*tp_getattro*/
634 0, /*tp_setattro*/
635 0, /*tp_as_buffer*/
636 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
637 "Tracer objects", /* tp_doc */
638 0, /* tp_traverse */
639 0, /* tp_clear */
640 0, /* tp_richcompare */
641 0, /* tp_weaklistoffset */
642 0, /* tp_iter */
643 0, /* tp_iternext */
644 Tracer_methods, /* tp_methods */
645 Tracer_members, /* tp_members */
646 0, /* tp_getset */
647 0, /* tp_base */
648 0, /* tp_dict */
649 0, /* tp_descr_get */
650 0, /* tp_descr_set */
651 0, /* tp_dictoffset */
652 (initproc)Tracer_init, /* tp_init */
653 0, /* tp_alloc */
654 0, /* tp_new */
655 };
656
657 /* Module definition */
658
659 #define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
660
661 #if PY_MAJOR_VERSION >= 3
662
663 static PyModuleDef
664 moduledef = {
665 PyModuleDef_HEAD_INIT,
666 "coverage.tracer",
667 MODULE_DOC,
668 -1,
669 NULL, /* methods */
670 NULL,
671 NULL, /* traverse */
672 NULL, /* clear */
673 NULL
674 };
675
676
677 PyObject *
678 PyInit_tracer(void)
679 {
680 PyObject * mod = PyModule_Create(&moduledef);
681 if (mod == NULL) {
682 return NULL;
683 }
684
685 TracerType.tp_new = PyType_GenericNew;
686 if (PyType_Ready(&TracerType) < 0) {
687 Py_DECREF(mod);
688 return NULL;
689 }
690
691 Py_INCREF(&TracerType);
692 PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType);
traceback: Traceback (most recent call last):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/__init__.py", line 131, in _check_refcounts
self.options)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4355, in check_refcounts
rep = impl_check_refcounts(ctxt, fun, options)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 4213, in impl_check_refcounts
limits=limits)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3089, in iter_traces
depth + 1):
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 3053, in iter_traces
transitions = curstate.get_transitions()
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2105, in get_transitions
return self._get_transitions_for_stmt(stmt)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2121, in _get_transitions_for_stmt
return self._get_transitions_for_GimpleCall(stmt)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/absinterp.py", line 2292, in _get_transitions_for_GimpleCall
return meth(stmt, *args)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 2544, in impl_PyModule_AddObject
s_success.cpython.steal_reference(v_value, stmt.loc)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 741, in steal_reference
_steal_ref)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/libcpychecker/refcounts.py", line 551, in change_refcount
check_isinstance(oldvalue, RefcountValue)
File "/usr/lib/gcc/x86_64-redhat-linux/4.7.2/plugin/python2/gccutils.py", line 642, in check_isinstance
raise TypeError('%s / %r is not an instance of %s' % (obj, obj, types))
TypeError: (long int)val [-0x7fffffffffffffff <= val <= 0x7fffffffffffffff] from coverage/tracer.c:691 / WithinRange(gcctype='long int', loc=gcc.Location(file='coverage/tracer.c', line=691), minvalue=-0x7fffffffffffffff, maxvalue=0x7fffffffffffffff) is not an instance of <class 'libcpychecker.refcounts.RefcountValue'>
693
694 return mod;
695 }
696
697 #else
698
699 void
700 inittracer(void)
701 {
702 PyObject * mod;
703
704 mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
705 if (mod == NULL) {
706 return;
707 }
708
709 TracerType.tp_new = PyType_GenericNew;
710 if (PyType_Ready(&TracerType) < 0) {
711 return;
712 }
713
714 Py_INCREF(&TracerType);
715 PyModule_AddObject(mod, "Tracer", (PyObject *)&TracerType);
716 }
717
718 #endif /* Py3k */