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