• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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