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