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