xref: /third_party/python/Modules/_lsprof.c (revision 7db96d56)
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
13struct _ProfilerEntry;
14
15/* represents a function called from another function */
16typedef 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 */
26typedef 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
37typedef struct _ProfilerContext {
38    _PyTime_t t0;
39    _PyTime_t subt;
40    struct _ProfilerContext *previous;
41    ProfilerEntry *ctxEntry;
42} ProfilerContext;
43
44typedef 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]
60module _lsprof
61class _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
67typedef struct {
68    PyTypeObject *profiler_type;
69    PyTypeObject *stats_entry_type;
70    PyTypeObject *stats_subentry_type;
71} _lsprof_state;
72
73static inline _lsprof_state*
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
83static _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
112static inline _PyTime_t
113call_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
126static PyObject *
127normalizeUserObj(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
196static ProfilerEntry*
197newProfilerEntry(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
224static ProfilerEntry*
225getEntry(ProfilerObject *pObj, void *key)
226{
227    return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
228}
229
230static ProfilerSubEntry *
231getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
232{
233    return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
234                                                (void *)entry);
235}
236
237static ProfilerSubEntry *
238newSubEntry(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
256static int freeSubEntry(rotating_node_t *header, void *arg)
257{
258    ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
259    PyMem_Free(subentry);
260    return 0;
261}
262
263static 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
272static 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
289static void
290initContext(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
309static void
310Stop(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
338static void
339ptrace_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
378restorePyerr:
379    PyErr_Restore(last_type, last_value, last_tb);
380}
381
382static void
383ptrace_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
405static int
406profiler_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
463static int
464pending_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
477static 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
487static 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
496static 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
503static 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
510typedef struct {
511    PyObject *list;
512    PyObject *sublist;
513    double factor;
514    _lsprof_state *state;
515} statscollector_t;
516
517static 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
538static 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
583list of profiler_entry objects.
584
585getstats() -> list of profiler_entry objects
586
587Return all information collected by the profiler.
588Each profiler_entry is a tuple-like object with the
589following 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
598The calls attribute is either None or a list of
599profiler_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
608static PyObject *
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
636static int
637setSubcalls(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
646static int
647setBuiltins(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
657PyDoc_STRVAR(enable_doc, "\
658enable(subcalls=True, builtins=True)\n\
659\n\
660Start collecting profiling information.\n\
661If 'subcalls' is True, also records for each function\n\
662statistics separated according to its current caller.\n\
663If 'builtins' is True, records the time spent in\n\
664built-in functions separately from their caller.\n\
665");
666
667static PyObject*
668profiler_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
689static void
690flush_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
705PyDoc_STRVAR(disable_doc, "\
706disable()\n\
707\n\
708Stop collecting profiling information.\n\
709");
710
711static PyObject*
712profiler_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
727PyDoc_STRVAR(clear_doc, "\
728clear()\n\
729\n\
730Clear all profiling information collected so far.\n\
731");
732
733static PyObject*
734profiler_clear(ProfilerObject *pObj, PyObject* noarg)
735{
736    clearEntries(pObj);
737    Py_RETURN_NONE;
738}
739
740static int
741profiler_traverse(ProfilerObject *op, visitproc visit, void *arg)
742{
743    Py_VISIT(Py_TYPE(op));
744    return 0;
745}
746
747static void
748profiler_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
766static int
767profiler_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
789static 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
800PyDoc_STRVAR(profiler_doc, "\
801Profiler(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
810static 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
819static 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
827static PyMethodDef moduleMethods[] = {
828    {NULL, NULL}
829};
830
831static int
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
841static int
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
851static void
852_lsprof_free(void *module)
853{
854    _lsprof_clear((PyObject *)module);
855}
856
857static int
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
891static PyModuleDef_Slot _lsprofslots[] = {
892    {Py_mod_exec, _lsprof_exec},
893    {0, NULL}
894};
895
896static 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
908PyMODINIT_FUNC
909PyInit__lsprof(void)
910{
911    return PyModuleDef_Init(&_lsprofmodule);
912}
913