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