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