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