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