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