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