1 #include "Python.h"
2 #include "frameobject.h"
3 #include "rotatingtree.h"
4
5 /*** Selection of a high-precision timer ***/
6
7 #ifdef MS_WINDOWS
8
9 #include <windows.h>
10
11 static long long
hpTimer(void)12 hpTimer(void)
13 {
14 LARGE_INTEGER li;
15 QueryPerformanceCounter(&li);
16 return li.QuadPart;
17 }
18
19 static double
hpTimerUnit(void)20 hpTimerUnit(void)
21 {
22 LARGE_INTEGER li;
23 if (QueryPerformanceFrequency(&li))
24 return 1.0 / li.QuadPart;
25 else
26 return 0.000001; /* unlikely */
27 }
28
29 #else /* !MS_WINDOWS */
30
31 #ifndef HAVE_GETTIMEOFDAY
32 #error "This module requires gettimeofday() on non-Windows platforms!"
33 #endif
34
35 #include <sys/resource.h>
36 #include <sys/times.h>
37
38 static long long
hpTimer(void)39 hpTimer(void)
40 {
41 struct timeval tv;
42 long long ret;
43 #ifdef GETTIMEOFDAY_NO_TZ
44 gettimeofday(&tv);
45 #else
46 gettimeofday(&tv, (struct timezone *)NULL);
47 #endif
48 ret = tv.tv_sec;
49 ret = ret * 1000000 + tv.tv_usec;
50 return ret;
51 }
52
53 static double
hpTimerUnit(void)54 hpTimerUnit(void)
55 {
56 return 0.000001;
57 }
58
59 #endif /* MS_WINDOWS */
60
61 /************************************************************/
62 /* Written by Brett Rosen and Ted Czotter */
63
64 struct _ProfilerEntry;
65
66 /* represents a function called from another function */
67 typedef struct _ProfilerSubEntry {
68 rotating_node_t header;
69 long long tt;
70 long long it;
71 long callcount;
72 long recursivecallcount;
73 long recursionLevel;
74 } ProfilerSubEntry;
75
76 /* represents a function or user defined block */
77 typedef struct _ProfilerEntry {
78 rotating_node_t header;
79 PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
80 long long tt; /* total time in this entry */
81 long long it; /* inline time in this entry (not in subcalls) */
82 long callcount; /* how many times this was called */
83 long recursivecallcount; /* how many times called recursively */
84 long recursionLevel;
85 rotating_node_t *calls;
86 } ProfilerEntry;
87
88 typedef struct _ProfilerContext {
89 long long t0;
90 long long subt;
91 struct _ProfilerContext *previous;
92 ProfilerEntry *ctxEntry;
93 } ProfilerContext;
94
95 typedef struct {
96 PyObject_HEAD
97 rotating_node_t *profilerEntries;
98 ProfilerContext *currentProfilerContext;
99 ProfilerContext *freelistProfilerContext;
100 int flags;
101 PyObject *externalTimer;
102 double externalTimerUnit;
103 } ProfilerObject;
104
105 #define POF_ENABLED 0x001
106 #define POF_SUBCALLS 0x002
107 #define POF_BUILTINS 0x004
108 #define POF_NOMEMORY 0x100
109
110 static PyTypeObject PyProfiler_Type;
111
112 #define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type)
113 #define PyProfiler_CheckExact(op) (Py_TYPE(op) == &PyProfiler_Type)
114
115 /*** External Timers ***/
116
117 #define DOUBLE_TIMER_PRECISION 4294967296.0
118 static PyObject *empty_tuple;
119
CallExternalTimer(ProfilerObject * pObj)120 static long long CallExternalTimer(ProfilerObject *pObj)
121 {
122 long long result;
123 PyObject *o = PyObject_Call(pObj->externalTimer, empty_tuple, NULL);
124 if (o == NULL) {
125 PyErr_WriteUnraisable(pObj->externalTimer);
126 return 0;
127 }
128 if (pObj->externalTimerUnit > 0.0) {
129 /* interpret the result as an integer that will be scaled
130 in profiler_getstats() */
131 result = PyLong_AsLongLong(o);
132 }
133 else {
134 /* interpret the result as a double measured in seconds.
135 As the profiler works with long long internally
136 we convert it to a large integer */
137 double val = PyFloat_AsDouble(o);
138 /* error handling delayed to the code below */
139 result = (long long) (val * DOUBLE_TIMER_PRECISION);
140 }
141 Py_DECREF(o);
142 if (PyErr_Occurred()) {
143 PyErr_WriteUnraisable(pObj->externalTimer);
144 return 0;
145 }
146 return result;
147 }
148
149 #define CALL_TIMER(pObj) ((pObj)->externalTimer ? \
150 CallExternalTimer(pObj) : \
151 hpTimer())
152
153 /*** ProfilerObject ***/
154
155 static PyObject *
normalizeUserObj(PyObject * obj)156 normalizeUserObj(PyObject *obj)
157 {
158 PyCFunctionObject *fn;
159 if (!PyCFunction_Check(obj)) {
160 Py_INCREF(obj);
161 return obj;
162 }
163 /* Replace built-in function objects with a descriptive string
164 because of built-in methods -- keeping a reference to
165 __self__ is probably not a good idea. */
166 fn = (PyCFunctionObject *)obj;
167
168 if (fn->m_self == NULL) {
169 /* built-in function: look up the module name */
170 PyObject *mod = fn->m_module;
171 PyObject *modname = NULL;
172 if (mod != NULL) {
173 if (PyUnicode_Check(mod)) {
174 modname = mod;
175 Py_INCREF(modname);
176 }
177 else if (PyModule_Check(mod)) {
178 modname = PyModule_GetNameObject(mod);
179 if (modname == NULL)
180 PyErr_Clear();
181 }
182 }
183 if (modname != NULL) {
184 if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
185 PyObject *result;
186 result = PyUnicode_FromFormat("<%U.%s>", modname,
187 fn->m_ml->ml_name);
188 Py_DECREF(modname);
189 return result;
190 }
191 Py_DECREF(modname);
192 }
193 return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
194 }
195 else {
196 /* built-in method: try to return
197 repr(getattr(type(__self__), __name__))
198 */
199 PyObject *self = fn->m_self;
200 PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name);
201 PyObject *modname = fn->m_module;
202
203 if (name != NULL) {
204 PyObject *mo = _PyType_Lookup(Py_TYPE(self), name);
205 Py_XINCREF(mo);
206 Py_DECREF(name);
207 if (mo != NULL) {
208 PyObject *res = PyObject_Repr(mo);
209 Py_DECREF(mo);
210 if (res != NULL)
211 return res;
212 }
213 }
214 /* Otherwise, use __module__ */
215 PyErr_Clear();
216 if (modname != NULL && PyUnicode_Check(modname))
217 return PyUnicode_FromFormat("<built-in method %S.%s>",
218 modname, fn->m_ml->ml_name);
219 else
220 return PyUnicode_FromFormat("<built-in method %s>",
221 fn->m_ml->ml_name);
222 }
223 }
224
225 static ProfilerEntry*
newProfilerEntry(ProfilerObject * pObj,void * key,PyObject * userObj)226 newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
227 {
228 ProfilerEntry *self;
229 self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
230 if (self == NULL) {
231 pObj->flags |= POF_NOMEMORY;
232 return NULL;
233 }
234 userObj = normalizeUserObj(userObj);
235 if (userObj == NULL) {
236 PyErr_Clear();
237 PyMem_Free(self);
238 pObj->flags |= POF_NOMEMORY;
239 return NULL;
240 }
241 self->header.key = key;
242 self->userObj = userObj;
243 self->tt = 0;
244 self->it = 0;
245 self->callcount = 0;
246 self->recursivecallcount = 0;
247 self->recursionLevel = 0;
248 self->calls = EMPTY_ROTATING_TREE;
249 RotatingTree_Add(&pObj->profilerEntries, &self->header);
250 return self;
251 }
252
253 static ProfilerEntry*
getEntry(ProfilerObject * pObj,void * key)254 getEntry(ProfilerObject *pObj, void *key)
255 {
256 return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
257 }
258
259 static ProfilerSubEntry *
getSubEntry(ProfilerObject * pObj,ProfilerEntry * caller,ProfilerEntry * entry)260 getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
261 {
262 return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
263 (void *)entry);
264 }
265
266 static ProfilerSubEntry *
newSubEntry(ProfilerObject * pObj,ProfilerEntry * caller,ProfilerEntry * entry)267 newSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
268 {
269 ProfilerSubEntry *self;
270 self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
271 if (self == NULL) {
272 pObj->flags |= POF_NOMEMORY;
273 return NULL;
274 }
275 self->header.key = (void *)entry;
276 self->tt = 0;
277 self->it = 0;
278 self->callcount = 0;
279 self->recursivecallcount = 0;
280 self->recursionLevel = 0;
281 RotatingTree_Add(&caller->calls, &self->header);
282 return self;
283 }
284
freeSubEntry(rotating_node_t * header,void * arg)285 static int freeSubEntry(rotating_node_t *header, void *arg)
286 {
287 ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
288 PyMem_Free(subentry);
289 return 0;
290 }
291
freeEntry(rotating_node_t * header,void * arg)292 static int freeEntry(rotating_node_t *header, void *arg)
293 {
294 ProfilerEntry *entry = (ProfilerEntry*) header;
295 RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
296 Py_DECREF(entry->userObj);
297 PyMem_Free(entry);
298 return 0;
299 }
300
clearEntries(ProfilerObject * pObj)301 static void clearEntries(ProfilerObject *pObj)
302 {
303 RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
304 pObj->profilerEntries = EMPTY_ROTATING_TREE;
305 /* release the memory hold by the ProfilerContexts */
306 if (pObj->currentProfilerContext) {
307 PyMem_Free(pObj->currentProfilerContext);
308 pObj->currentProfilerContext = NULL;
309 }
310 while (pObj->freelistProfilerContext) {
311 ProfilerContext *c = pObj->freelistProfilerContext;
312 pObj->freelistProfilerContext = c->previous;
313 PyMem_Free(c);
314 }
315 pObj->freelistProfilerContext = NULL;
316 }
317
318 static void
initContext(ProfilerObject * pObj,ProfilerContext * self,ProfilerEntry * entry)319 initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
320 {
321 self->ctxEntry = entry;
322 self->subt = 0;
323 self->previous = pObj->currentProfilerContext;
324 pObj->currentProfilerContext = self;
325 ++entry->recursionLevel;
326 if ((pObj->flags & POF_SUBCALLS) && self->previous) {
327 /* find or create an entry for me in my caller's entry */
328 ProfilerEntry *caller = self->previous->ctxEntry;
329 ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
330 if (subentry == NULL)
331 subentry = newSubEntry(pObj, caller, entry);
332 if (subentry)
333 ++subentry->recursionLevel;
334 }
335 self->t0 = CALL_TIMER(pObj);
336 }
337
338 static void
Stop(ProfilerObject * pObj,ProfilerContext * self,ProfilerEntry * entry)339 Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
340 {
341 long long tt = CALL_TIMER(pObj) - self->t0;
342 long long it = tt - self->subt;
343 if (self->previous)
344 self->previous->subt += tt;
345 pObj->currentProfilerContext = self->previous;
346 if (--entry->recursionLevel == 0)
347 entry->tt += tt;
348 else
349 ++entry->recursivecallcount;
350 entry->it += it;
351 entry->callcount++;
352 if ((pObj->flags & POF_SUBCALLS) && self->previous) {
353 /* find or create an entry for me in my caller's entry */
354 ProfilerEntry *caller = self->previous->ctxEntry;
355 ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
356 if (subentry) {
357 if (--subentry->recursionLevel == 0)
358 subentry->tt += tt;
359 else
360 ++subentry->recursivecallcount;
361 subentry->it += it;
362 ++subentry->callcount;
363 }
364 }
365 }
366
367 static void
ptrace_enter_call(PyObject * self,void * key,PyObject * userObj)368 ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
369 {
370 /* entering a call to the function identified by 'key'
371 (which can be a PyCodeObject or a PyMethodDef pointer) */
372 ProfilerObject *pObj = (ProfilerObject*)self;
373 ProfilerEntry *profEntry;
374 ProfilerContext *pContext;
375
376 /* In the case of entering a generator expression frame via a
377 * throw (gen_send_ex(.., 1)), we may already have an
378 * Exception set here. We must not mess around with this
379 * exception, and some of the code under here assumes that
380 * PyErr_* is its own to mess around with, so we have to
381 * save and restore any current exception. */
382 PyObject *last_type, *last_value, *last_tb;
383 PyErr_Fetch(&last_type, &last_value, &last_tb);
384
385 profEntry = getEntry(pObj, key);
386 if (profEntry == NULL) {
387 profEntry = newProfilerEntry(pObj, key, userObj);
388 if (profEntry == NULL)
389 goto restorePyerr;
390 }
391 /* grab a ProfilerContext out of the free list */
392 pContext = pObj->freelistProfilerContext;
393 if (pContext) {
394 pObj->freelistProfilerContext = pContext->previous;
395 }
396 else {
397 /* free list exhausted, allocate a new one */
398 pContext = (ProfilerContext*)
399 PyMem_Malloc(sizeof(ProfilerContext));
400 if (pContext == NULL) {
401 pObj->flags |= POF_NOMEMORY;
402 goto restorePyerr;
403 }
404 }
405 initContext(pObj, pContext, profEntry);
406
407 restorePyerr:
408 PyErr_Restore(last_type, last_value, last_tb);
409 }
410
411 static void
ptrace_leave_call(PyObject * self,void * key)412 ptrace_leave_call(PyObject *self, void *key)
413 {
414 /* leaving a call to the function identified by 'key' */
415 ProfilerObject *pObj = (ProfilerObject*)self;
416 ProfilerEntry *profEntry;
417 ProfilerContext *pContext;
418
419 pContext = pObj->currentProfilerContext;
420 if (pContext == NULL)
421 return;
422 profEntry = getEntry(pObj, key);
423 if (profEntry) {
424 Stop(pObj, pContext, profEntry);
425 }
426 else {
427 pObj->currentProfilerContext = pContext->previous;
428 }
429 /* put pContext into the free list */
430 pContext->previous = pObj->freelistProfilerContext;
431 pObj->freelistProfilerContext = pContext;
432 }
433
434 static int
profiler_callback(PyObject * self,PyFrameObject * frame,int what,PyObject * arg)435 profiler_callback(PyObject *self, PyFrameObject *frame, int what,
436 PyObject *arg)
437 {
438 switch (what) {
439
440 /* the 'frame' of a called function is about to start its execution */
441 case PyTrace_CALL:
442 ptrace_enter_call(self, (void *)frame->f_code,
443 (PyObject *)frame->f_code);
444 break;
445
446 /* the 'frame' of a called function is about to finish
447 (either normally or with an exception) */
448 case PyTrace_RETURN:
449 ptrace_leave_call(self, (void *)frame->f_code);
450 break;
451
452 /* case PyTrace_EXCEPTION:
453 If the exception results in the function exiting, a
454 PyTrace_RETURN event will be generated, so we don't need to
455 handle it. */
456
457 /* the Python function 'frame' is issuing a call to the built-in
458 function 'arg' */
459 case PyTrace_C_CALL:
460 if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
461 && PyCFunction_Check(arg)) {
462 ptrace_enter_call(self,
463 ((PyCFunctionObject *)arg)->m_ml,
464 arg);
465 }
466 break;
467
468 /* the call to the built-in function 'arg' is returning into its
469 caller 'frame' */
470 case PyTrace_C_RETURN: /* ...normally */
471 case PyTrace_C_EXCEPTION: /* ...with an exception set */
472 if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
473 && PyCFunction_Check(arg)) {
474 ptrace_leave_call(self,
475 ((PyCFunctionObject *)arg)->m_ml);
476 }
477 break;
478
479 default:
480 break;
481 }
482 return 0;
483 }
484
485 static int
pending_exception(ProfilerObject * pObj)486 pending_exception(ProfilerObject *pObj)
487 {
488 if (pObj->flags & POF_NOMEMORY) {
489 pObj->flags -= POF_NOMEMORY;
490 PyErr_SetString(PyExc_MemoryError,
491 "memory was exhausted while profiling");
492 return -1;
493 }
494 return 0;
495 }
496
497 /************************************************************/
498
499 static PyStructSequence_Field profiler_entry_fields[] = {
500 {"code", "code object or built-in function name"},
501 {"callcount", "how many times this was called"},
502 {"reccallcount", "how many times called recursively"},
503 {"totaltime", "total time in this entry"},
504 {"inlinetime", "inline time in this entry (not in subcalls)"},
505 {"calls", "details of the calls"},
506 {0}
507 };
508
509 static PyStructSequence_Field profiler_subentry_fields[] = {
510 {"code", "called code object or built-in function name"},
511 {"callcount", "how many times this is called"},
512 {"reccallcount", "how many times this is called recursively"},
513 {"totaltime", "total time spent in this call"},
514 {"inlinetime", "inline time (not in further subcalls)"},
515 {0}
516 };
517
518 static PyStructSequence_Desc profiler_entry_desc = {
519 "_lsprof.profiler_entry", /* name */
520 NULL, /* doc */
521 profiler_entry_fields,
522 6
523 };
524
525 static PyStructSequence_Desc profiler_subentry_desc = {
526 "_lsprof.profiler_subentry", /* name */
527 NULL, /* doc */
528 profiler_subentry_fields,
529 5
530 };
531
532 static int initialized;
533 static PyTypeObject StatsEntryType;
534 static PyTypeObject StatsSubEntryType;
535
536
537 typedef struct {
538 PyObject *list;
539 PyObject *sublist;
540 double factor;
541 } statscollector_t;
542
statsForSubEntry(rotating_node_t * node,void * arg)543 static int statsForSubEntry(rotating_node_t *node, void *arg)
544 {
545 ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
546 statscollector_t *collect = (statscollector_t*) arg;
547 ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
548 int err;
549 PyObject *sinfo;
550 sinfo = PyObject_CallFunction((PyObject*) &StatsSubEntryType,
551 "((Olldd))",
552 entry->userObj,
553 sentry->callcount,
554 sentry->recursivecallcount,
555 collect->factor * sentry->tt,
556 collect->factor * sentry->it);
557 if (sinfo == NULL)
558 return -1;
559 err = PyList_Append(collect->sublist, sinfo);
560 Py_DECREF(sinfo);
561 return err;
562 }
563
statsForEntry(rotating_node_t * node,void * arg)564 static int statsForEntry(rotating_node_t *node, void *arg)
565 {
566 ProfilerEntry *entry = (ProfilerEntry*) node;
567 statscollector_t *collect = (statscollector_t*) arg;
568 PyObject *info;
569 int err;
570 if (entry->callcount == 0)
571 return 0; /* skip */
572
573 if (entry->calls != EMPTY_ROTATING_TREE) {
574 collect->sublist = PyList_New(0);
575 if (collect->sublist == NULL)
576 return -1;
577 if (RotatingTree_Enum(entry->calls,
578 statsForSubEntry, collect) != 0) {
579 Py_DECREF(collect->sublist);
580 return -1;
581 }
582 }
583 else {
584 Py_INCREF(Py_None);
585 collect->sublist = Py_None;
586 }
587
588 info = PyObject_CallFunction((PyObject*) &StatsEntryType,
589 "((OllddO))",
590 entry->userObj,
591 entry->callcount,
592 entry->recursivecallcount,
593 collect->factor * entry->tt,
594 collect->factor * entry->it,
595 collect->sublist);
596 Py_DECREF(collect->sublist);
597 if (info == NULL)
598 return -1;
599 err = PyList_Append(collect->list, info);
600 Py_DECREF(info);
601 return err;
602 }
603
604 PyDoc_STRVAR(getstats_doc, "\
605 getstats() -> list of profiler_entry objects\n\
606 \n\
607 Return all information collected by the profiler.\n\
608 Each profiler_entry is a tuple-like object with the\n\
609 following attributes:\n\
610 \n\
611 code code object\n\
612 callcount how many times this was called\n\
613 reccallcount how many times called recursively\n\
614 totaltime total time in this entry\n\
615 inlinetime inline time in this entry (not in subcalls)\n\
616 calls details of the calls\n\
617 \n\
618 The calls attribute is either None or a list of\n\
619 profiler_subentry objects:\n\
620 \n\
621 code called code object\n\
622 callcount how many times this is called\n\
623 reccallcount how many times this is called recursively\n\
624 totaltime total time spent in this call\n\
625 inlinetime inline time (not in further subcalls)\n\
626 ");
627
628 static PyObject*
profiler_getstats(ProfilerObject * pObj,PyObject * noarg)629 profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
630 {
631 statscollector_t collect;
632 if (pending_exception(pObj))
633 return NULL;
634 if (!pObj->externalTimer)
635 collect.factor = hpTimerUnit();
636 else if (pObj->externalTimerUnit > 0.0)
637 collect.factor = pObj->externalTimerUnit;
638 else
639 collect.factor = 1.0 / DOUBLE_TIMER_PRECISION;
640 collect.list = PyList_New(0);
641 if (collect.list == NULL)
642 return NULL;
643 if (RotatingTree_Enum(pObj->profilerEntries, statsForEntry, &collect)
644 != 0) {
645 Py_DECREF(collect.list);
646 return NULL;
647 }
648 return collect.list;
649 }
650
651 static int
setSubcalls(ProfilerObject * pObj,int nvalue)652 setSubcalls(ProfilerObject *pObj, int nvalue)
653 {
654 if (nvalue == 0)
655 pObj->flags &= ~POF_SUBCALLS;
656 else if (nvalue > 0)
657 pObj->flags |= POF_SUBCALLS;
658 return 0;
659 }
660
661 static int
setBuiltins(ProfilerObject * pObj,int nvalue)662 setBuiltins(ProfilerObject *pObj, int nvalue)
663 {
664 if (nvalue == 0)
665 pObj->flags &= ~POF_BUILTINS;
666 else if (nvalue > 0) {
667 pObj->flags |= POF_BUILTINS;
668 }
669 return 0;
670 }
671
672 PyDoc_STRVAR(enable_doc, "\
673 enable(subcalls=True, builtins=True)\n\
674 \n\
675 Start collecting profiling information.\n\
676 If 'subcalls' is True, also records for each function\n\
677 statistics separated according to its current caller.\n\
678 If 'builtins' is True, records the time spent in\n\
679 built-in functions separately from their caller.\n\
680 ");
681
682 static PyObject*
profiler_enable(ProfilerObject * self,PyObject * args,PyObject * kwds)683 profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
684 {
685 int subcalls = -1;
686 int builtins = -1;
687 static char *kwlist[] = {"subcalls", "builtins", 0};
688 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
689 kwlist, &subcalls, &builtins))
690 return NULL;
691 if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0)
692 return NULL;
693 PyEval_SetProfile(profiler_callback, (PyObject*)self);
694 self->flags |= POF_ENABLED;
695 Py_RETURN_NONE;
696 }
697
698 static void
flush_unmatched(ProfilerObject * pObj)699 flush_unmatched(ProfilerObject *pObj)
700 {
701 while (pObj->currentProfilerContext) {
702 ProfilerContext *pContext = pObj->currentProfilerContext;
703 ProfilerEntry *profEntry= pContext->ctxEntry;
704 if (profEntry)
705 Stop(pObj, pContext, profEntry);
706 else
707 pObj->currentProfilerContext = pContext->previous;
708 if (pContext)
709 PyMem_Free(pContext);
710 }
711
712 }
713
714 PyDoc_STRVAR(disable_doc, "\
715 disable()\n\
716 \n\
717 Stop collecting profiling information.\n\
718 ");
719
720 static PyObject*
profiler_disable(ProfilerObject * self,PyObject * noarg)721 profiler_disable(ProfilerObject *self, PyObject* noarg)
722 {
723 self->flags &= ~POF_ENABLED;
724 PyEval_SetProfile(NULL, NULL);
725 flush_unmatched(self);
726 if (pending_exception(self))
727 return NULL;
728 Py_RETURN_NONE;
729 }
730
731 PyDoc_STRVAR(clear_doc, "\
732 clear()\n\
733 \n\
734 Clear all profiling information collected so far.\n\
735 ");
736
737 static PyObject*
profiler_clear(ProfilerObject * pObj,PyObject * noarg)738 profiler_clear(ProfilerObject *pObj, PyObject* noarg)
739 {
740 clearEntries(pObj);
741 Py_RETURN_NONE;
742 }
743
744 static void
profiler_dealloc(ProfilerObject * op)745 profiler_dealloc(ProfilerObject *op)
746 {
747 if (op->flags & POF_ENABLED)
748 PyEval_SetProfile(NULL, NULL);
749 flush_unmatched(op);
750 clearEntries(op);
751 Py_XDECREF(op->externalTimer);
752 Py_TYPE(op)->tp_free(op);
753 }
754
755 static int
profiler_init(ProfilerObject * pObj,PyObject * args,PyObject * kw)756 profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
757 {
758 PyObject *timer = NULL;
759 double timeunit = 0.0;
760 int subcalls = 1;
761 int builtins = 1;
762 static char *kwlist[] = {"timer", "timeunit",
763 "subcalls", "builtins", 0};
764
765 if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odii:Profiler", kwlist,
766 &timer, &timeunit,
767 &subcalls, &builtins))
768 return -1;
769
770 if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
771 return -1;
772 pObj->externalTimerUnit = timeunit;
773 Py_XINCREF(timer);
774 Py_XSETREF(pObj->externalTimer, timer);
775 return 0;
776 }
777
778 static PyMethodDef profiler_methods[] = {
779 {"getstats", (PyCFunction)profiler_getstats,
780 METH_NOARGS, getstats_doc},
781 {"enable", (PyCFunction)profiler_enable,
782 METH_VARARGS | METH_KEYWORDS, enable_doc},
783 {"disable", (PyCFunction)profiler_disable,
784 METH_NOARGS, disable_doc},
785 {"clear", (PyCFunction)profiler_clear,
786 METH_NOARGS, clear_doc},
787 {NULL, NULL}
788 };
789
790 PyDoc_STRVAR(profiler_doc, "\
791 Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
792 \n\
793 Builds a profiler object using the specified timer function.\n\
794 The default timer is a fast built-in one based on real time.\n\
795 For custom timer functions returning integers, timeunit can\n\
796 be a float specifying a scale (i.e. how long each integer unit\n\
797 is, in seconds).\n\
798 ");
799
800 static PyTypeObject PyProfiler_Type = {
801 PyVarObject_HEAD_INIT(NULL, 0)
802 "_lsprof.Profiler", /* tp_name */
803 sizeof(ProfilerObject), /* tp_basicsize */
804 0, /* tp_itemsize */
805 (destructor)profiler_dealloc, /* tp_dealloc */
806 0, /* tp_print */
807 0, /* tp_getattr */
808 0, /* tp_setattr */
809 0, /* tp_reserved */
810 0, /* tp_repr */
811 0, /* tp_as_number */
812 0, /* tp_as_sequence */
813 0, /* tp_as_mapping */
814 0, /* tp_hash */
815 0, /* tp_call */
816 0, /* tp_str */
817 0, /* tp_getattro */
818 0, /* tp_setattro */
819 0, /* tp_as_buffer */
820 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
821 profiler_doc, /* tp_doc */
822 0, /* tp_traverse */
823 0, /* tp_clear */
824 0, /* tp_richcompare */
825 0, /* tp_weaklistoffset */
826 0, /* tp_iter */
827 0, /* tp_iternext */
828 profiler_methods, /* tp_methods */
829 0, /* tp_members */
830 0, /* tp_getset */
831 0, /* tp_base */
832 0, /* tp_dict */
833 0, /* tp_descr_get */
834 0, /* tp_descr_set */
835 0, /* tp_dictoffset */
836 (initproc)profiler_init, /* tp_init */
837 PyType_GenericAlloc, /* tp_alloc */
838 PyType_GenericNew, /* tp_new */
839 PyObject_Del, /* tp_free */
840 };
841
842 static PyMethodDef moduleMethods[] = {
843 {NULL, NULL}
844 };
845
846
847 static struct PyModuleDef _lsprofmodule = {
848 PyModuleDef_HEAD_INIT,
849 "_lsprof",
850 "Fast profiler",
851 -1,
852 moduleMethods,
853 NULL,
854 NULL,
855 NULL,
856 NULL
857 };
858
859 PyMODINIT_FUNC
PyInit__lsprof(void)860 PyInit__lsprof(void)
861 {
862 PyObject *module, *d;
863 module = PyModule_Create(&_lsprofmodule);
864 if (module == NULL)
865 return NULL;
866 d = PyModule_GetDict(module);
867 if (PyType_Ready(&PyProfiler_Type) < 0)
868 return NULL;
869 PyDict_SetItemString(d, "Profiler", (PyObject *)&PyProfiler_Type);
870
871 if (!initialized) {
872 if (PyStructSequence_InitType2(&StatsEntryType,
873 &profiler_entry_desc) < 0)
874 return NULL;
875 if (PyStructSequence_InitType2(&StatsSubEntryType,
876 &profiler_subentry_desc) < 0)
877 return NULL;
878 }
879 Py_INCREF((PyObject*) &StatsEntryType);
880 Py_INCREF((PyObject*) &StatsSubEntryType);
881 PyModule_AddObject(module, "profiler_entry",
882 (PyObject*) &StatsEntryType);
883 PyModule_AddObject(module, "profiler_subentry",
884 (PyObject*) &StatsSubEntryType);
885 empty_tuple = PyTuple_New(0);
886 initialized = 1;
887 return module;
888 }
889