LCOV - code coverage report
Current view: | top level - Modules - _lsprof.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 32 | 377 | 8.5 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 6 | 34 | 17.6 %
|  |  | Branches: | 18 | 183 | 9.8 %

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

Generated by: LCOV version 1.14