• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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