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