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