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