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