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