123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083 |
- #ifndef Py_BUILD_CORE_BUILTIN
- # define Py_BUILD_CORE_MODULE 1
- #endif
- #include "Python.h"
- #include "pycore_call.h" // _PyObject_CallNoArgs()
- #include "pycore_pystate.h" // _PyThreadState_GET()
- #include "rotatingtree.h"
- /************************************************************/
- /* Written by Brett Rosen and Ted Czotter */
- struct _ProfilerEntry;
- /* represents a function called from another function */
- typedef struct _ProfilerSubEntry {
- rotating_node_t header;
- _PyTime_t tt;
- _PyTime_t it;
- long callcount;
- long recursivecallcount;
- long recursionLevel;
- } ProfilerSubEntry;
- /* represents a function or user defined block */
- typedef struct _ProfilerEntry {
- rotating_node_t header;
- PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
- _PyTime_t tt; /* total time in this entry */
- _PyTime_t it; /* inline time in this entry (not in subcalls) */
- long callcount; /* how many times this was called */
- long recursivecallcount; /* how many times called recursively */
- long recursionLevel;
- rotating_node_t *calls;
- } ProfilerEntry;
- typedef struct _ProfilerContext {
- _PyTime_t t0;
- _PyTime_t subt;
- struct _ProfilerContext *previous;
- ProfilerEntry *ctxEntry;
- } ProfilerContext;
- typedef struct {
- PyObject_HEAD
- rotating_node_t *profilerEntries;
- ProfilerContext *currentProfilerContext;
- ProfilerContext *freelistProfilerContext;
- int flags;
- PyObject *externalTimer;
- double externalTimerUnit;
- int tool_id;
- PyObject* missing;
- } ProfilerObject;
- #define POF_ENABLED 0x001
- #define POF_SUBCALLS 0x002
- #define POF_BUILTINS 0x004
- #define POF_EXT_TIMER 0x008
- #define POF_NOMEMORY 0x100
- /*[clinic input]
- module _lsprof
- class _lsprof.Profiler "ProfilerObject *" "&ProfilerType"
- [clinic start generated code]*/
- /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e349ac952152f336]*/
- #include "clinic/_lsprof.c.h"
- typedef struct {
- PyTypeObject *profiler_type;
- PyTypeObject *stats_entry_type;
- PyTypeObject *stats_subentry_type;
- } _lsprof_state;
- static inline _lsprof_state*
- _lsprof_get_state(PyObject *module)
- {
- void *state = PyModule_GetState(module);
- assert(state != NULL);
- return (_lsprof_state *)state;
- }
- /*** External Timers ***/
- static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
- {
- PyObject *o = NULL;
- // External timer can do arbitrary things so we need a flag to prevent
- // horrible things to happen
- pObj->flags |= POF_EXT_TIMER;
- o = _PyObject_CallNoArgs(pObj->externalTimer);
- pObj->flags &= ~POF_EXT_TIMER;
- if (o == NULL) {
- PyErr_WriteUnraisable(pObj->externalTimer);
- return 0;
- }
- _PyTime_t result;
- int err;
- if (pObj->externalTimerUnit > 0.0) {
- /* interpret the result as an integer that will be scaled
- in profiler_getstats() */
- err = _PyTime_FromNanosecondsObject(&result, o);
- }
- else {
- /* interpret the result as a double measured in seconds.
- As the profiler works with _PyTime_t internally
- we convert it to a large integer */
- err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
- }
- Py_DECREF(o);
- if (err < 0) {
- PyErr_WriteUnraisable(pObj->externalTimer);
- return 0;
- }
- return result;
- }
- static inline _PyTime_t
- call_timer(ProfilerObject *pObj)
- {
- if (pObj->externalTimer != NULL) {
- return CallExternalTimer(pObj);
- }
- else {
- return _PyTime_GetPerfCounter();
- }
- }
- /*** ProfilerObject ***/
- static PyObject *
- normalizeUserObj(PyObject *obj)
- {
- PyCFunctionObject *fn;
- if (!PyCFunction_Check(obj)) {
- return Py_NewRef(obj);
- }
- /* Replace built-in function objects with a descriptive string
- because of built-in methods -- keeping a reference to
- __self__ is probably not a good idea. */
- fn = (PyCFunctionObject *)obj;
- if (fn->m_self == NULL) {
- /* built-in function: look up the module name */
- PyObject *mod = fn->m_module;
- PyObject *modname = NULL;
- if (mod != NULL) {
- if (PyUnicode_Check(mod)) {
- modname = Py_NewRef(mod);
- }
- else if (PyModule_Check(mod)) {
- modname = PyModule_GetNameObject(mod);
- if (modname == NULL)
- PyErr_Clear();
- }
- }
- if (modname != NULL) {
- if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
- PyObject *result;
- result = PyUnicode_FromFormat("<%U.%s>", modname,
- fn->m_ml->ml_name);
- Py_DECREF(modname);
- return result;
- }
- Py_DECREF(modname);
- }
- return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
- }
- else {
- /* built-in method: try to return
- repr(getattr(type(__self__), __name__))
- */
- PyObject *self = fn->m_self;
- PyObject *name = PyUnicode_FromString(fn->m_ml->ml_name);
- PyObject *modname = fn->m_module;
- if (name != NULL) {
- PyObject *mo = _PyType_Lookup(Py_TYPE(self), name);
- Py_XINCREF(mo);
- Py_DECREF(name);
- if (mo != NULL) {
- PyObject *res = PyObject_Repr(mo);
- Py_DECREF(mo);
- if (res != NULL)
- return res;
- }
- }
- /* Otherwise, use __module__ */
- PyErr_Clear();
- if (modname != NULL && PyUnicode_Check(modname))
- return PyUnicode_FromFormat("<built-in method %S.%s>",
- modname, fn->m_ml->ml_name);
- else
- return PyUnicode_FromFormat("<built-in method %s>",
- fn->m_ml->ml_name);
- }
- }
- static ProfilerEntry*
- newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
- {
- ProfilerEntry *self;
- self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
- if (self == NULL) {
- pObj->flags |= POF_NOMEMORY;
- return NULL;
- }
- userObj = normalizeUserObj(userObj);
- if (userObj == NULL) {
- PyErr_Clear();
- PyMem_Free(self);
- pObj->flags |= POF_NOMEMORY;
- return NULL;
- }
- self->header.key = key;
- self->userObj = userObj;
- self->tt = 0;
- self->it = 0;
- self->callcount = 0;
- self->recursivecallcount = 0;
- self->recursionLevel = 0;
- self->calls = EMPTY_ROTATING_TREE;
- RotatingTree_Add(&pObj->profilerEntries, &self->header);
- return self;
- }
- static ProfilerEntry*
- getEntry(ProfilerObject *pObj, void *key)
- {
- return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
- }
- static ProfilerSubEntry *
- getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
- {
- return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
- (void *)entry);
- }
- static ProfilerSubEntry *
- newSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
- {
- ProfilerSubEntry *self;
- self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
- if (self == NULL) {
- pObj->flags |= POF_NOMEMORY;
- return NULL;
- }
- self->header.key = (void *)entry;
- self->tt = 0;
- self->it = 0;
- self->callcount = 0;
- self->recursivecallcount = 0;
- self->recursionLevel = 0;
- RotatingTree_Add(&caller->calls, &self->header);
- return self;
- }
- static int freeSubEntry(rotating_node_t *header, void *arg)
- {
- ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
- PyMem_Free(subentry);
- return 0;
- }
- static int freeEntry(rotating_node_t *header, void *arg)
- {
- ProfilerEntry *entry = (ProfilerEntry*) header;
- RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
- Py_DECREF(entry->userObj);
- PyMem_Free(entry);
- return 0;
- }
- static void clearEntries(ProfilerObject *pObj)
- {
- RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
- pObj->profilerEntries = EMPTY_ROTATING_TREE;
- /* release the memory hold by the ProfilerContexts */
- if (pObj->currentProfilerContext) {
- PyMem_Free(pObj->currentProfilerContext);
- pObj->currentProfilerContext = NULL;
- }
- while (pObj->freelistProfilerContext) {
- ProfilerContext *c = pObj->freelistProfilerContext;
- pObj->freelistProfilerContext = c->previous;
- PyMem_Free(c);
- }
- pObj->freelistProfilerContext = NULL;
- }
- static void
- initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
- {
- self->ctxEntry = entry;
- self->subt = 0;
- self->previous = pObj->currentProfilerContext;
- pObj->currentProfilerContext = self;
- ++entry->recursionLevel;
- if ((pObj->flags & POF_SUBCALLS) && self->previous) {
- /* find or create an entry for me in my caller's entry */
- ProfilerEntry *caller = self->previous->ctxEntry;
- ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
- if (subentry == NULL)
- subentry = newSubEntry(pObj, caller, entry);
- if (subentry)
- ++subentry->recursionLevel;
- }
- self->t0 = call_timer(pObj);
- }
- static void
- Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
- {
- _PyTime_t tt = call_timer(pObj) - self->t0;
- _PyTime_t it = tt - self->subt;
- if (self->previous)
- self->previous->subt += tt;
- pObj->currentProfilerContext = self->previous;
- if (--entry->recursionLevel == 0)
- entry->tt += tt;
- else
- ++entry->recursivecallcount;
- entry->it += it;
- entry->callcount++;
- if ((pObj->flags & POF_SUBCALLS) && self->previous) {
- /* find or create an entry for me in my caller's entry */
- ProfilerEntry *caller = self->previous->ctxEntry;
- ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
- if (subentry) {
- if (--subentry->recursionLevel == 0)
- subentry->tt += tt;
- else
- ++subentry->recursivecallcount;
- subentry->it += it;
- ++subentry->callcount;
- }
- }
- }
- static void
- ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
- {
- /* entering a call to the function identified by 'key'
- (which can be a PyCodeObject or a PyMethodDef pointer) */
- ProfilerObject *pObj = (ProfilerObject*)self;
- ProfilerEntry *profEntry;
- ProfilerContext *pContext;
- /* In the case of entering a generator expression frame via a
- * throw (gen_send_ex(.., 1)), we may already have an
- * Exception set here. We must not mess around with this
- * exception, and some of the code under here assumes that
- * PyErr_* is its own to mess around with, so we have to
- * save and restore any current exception. */
- PyObject *exc = PyErr_GetRaisedException();
- profEntry = getEntry(pObj, key);
- if (profEntry == NULL) {
- profEntry = newProfilerEntry(pObj, key, userObj);
- if (profEntry == NULL)
- goto restorePyerr;
- }
- /* grab a ProfilerContext out of the free list */
- pContext = pObj->freelistProfilerContext;
- if (pContext) {
- pObj->freelistProfilerContext = pContext->previous;
- }
- else {
- /* free list exhausted, allocate a new one */
- pContext = (ProfilerContext*)
- PyMem_Malloc(sizeof(ProfilerContext));
- if (pContext == NULL) {
- pObj->flags |= POF_NOMEMORY;
- goto restorePyerr;
- }
- }
- initContext(pObj, pContext, profEntry);
- restorePyerr:
- PyErr_SetRaisedException(exc);
- }
- static void
- ptrace_leave_call(PyObject *self, void *key)
- {
- /* leaving a call to the function identified by 'key' */
- ProfilerObject *pObj = (ProfilerObject*)self;
- ProfilerEntry *profEntry;
- ProfilerContext *pContext;
- pContext = pObj->currentProfilerContext;
- if (pContext == NULL)
- return;
- profEntry = getEntry(pObj, key);
- if (profEntry) {
- Stop(pObj, pContext, profEntry);
- }
- else {
- pObj->currentProfilerContext = pContext->previous;
- }
- /* put pContext into the free list */
- pContext->previous = pObj->freelistProfilerContext;
- pObj->freelistProfilerContext = pContext;
- }
- static int
- pending_exception(ProfilerObject *pObj)
- {
- if (pObj->flags & POF_NOMEMORY) {
- pObj->flags -= POF_NOMEMORY;
- PyErr_SetString(PyExc_MemoryError,
- "memory was exhausted while profiling");
- return -1;
- }
- return 0;
- }
- /************************************************************/
- static PyStructSequence_Field profiler_entry_fields[] = {
- {"code", "code object or built-in function name"},
- {"callcount", "how many times this was called"},
- {"reccallcount", "how many times called recursively"},
- {"totaltime", "total time in this entry"},
- {"inlinetime", "inline time in this entry (not in subcalls)"},
- {"calls", "details of the calls"},
- {0}
- };
- static PyStructSequence_Field profiler_subentry_fields[] = {
- {"code", "called code object or built-in function name"},
- {"callcount", "how many times this is called"},
- {"reccallcount", "how many times this is called recursively"},
- {"totaltime", "total time spent in this call"},
- {"inlinetime", "inline time (not in further subcalls)"},
- {0}
- };
- static PyStructSequence_Desc profiler_entry_desc = {
- .name = "_lsprof.profiler_entry",
- .fields = profiler_entry_fields,
- .doc = NULL,
- .n_in_sequence = 6
- };
- static PyStructSequence_Desc profiler_subentry_desc = {
- .name = "_lsprof.profiler_subentry",
- .fields = profiler_subentry_fields,
- .doc = NULL,
- .n_in_sequence = 5
- };
- typedef struct {
- PyObject *list;
- PyObject *sublist;
- double factor;
- _lsprof_state *state;
- } statscollector_t;
- static int statsForSubEntry(rotating_node_t *node, void *arg)
- {
- ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
- statscollector_t *collect = (statscollector_t*) arg;
- ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
- int err;
- PyObject *sinfo;
- sinfo = PyObject_CallFunction((PyObject*) collect->state->stats_subentry_type,
- "((Olldd))",
- entry->userObj,
- sentry->callcount,
- sentry->recursivecallcount,
- collect->factor * sentry->tt,
- collect->factor * sentry->it);
- if (sinfo == NULL)
- return -1;
- err = PyList_Append(collect->sublist, sinfo);
- Py_DECREF(sinfo);
- return err;
- }
- static int statsForEntry(rotating_node_t *node, void *arg)
- {
- ProfilerEntry *entry = (ProfilerEntry*) node;
- statscollector_t *collect = (statscollector_t*) arg;
- PyObject *info;
- int err;
- if (entry->callcount == 0)
- return 0; /* skip */
- if (entry->calls != EMPTY_ROTATING_TREE) {
- collect->sublist = PyList_New(0);
- if (collect->sublist == NULL)
- return -1;
- if (RotatingTree_Enum(entry->calls,
- statsForSubEntry, collect) != 0) {
- Py_DECREF(collect->sublist);
- return -1;
- }
- }
- else {
- collect->sublist = Py_NewRef(Py_None);
- }
- info = PyObject_CallFunction((PyObject*) collect->state->stats_entry_type,
- "((OllddO))",
- entry->userObj,
- entry->callcount,
- entry->recursivecallcount,
- collect->factor * entry->tt,
- collect->factor * entry->it,
- collect->sublist);
- Py_DECREF(collect->sublist);
- if (info == NULL)
- return -1;
- err = PyList_Append(collect->list, info);
- Py_DECREF(info);
- return err;
- }
- /*[clinic input]
- _lsprof.Profiler.getstats
- cls: defining_class
- list of profiler_entry objects.
- getstats() -> list of profiler_entry objects
- Return all information collected by the profiler.
- Each profiler_entry is a tuple-like object with the
- following attributes:
- code code object
- callcount how many times this was called
- reccallcount how many times called recursively
- totaltime total time in this entry
- inlinetime inline time in this entry (not in subcalls)
- calls details of the calls
- The calls attribute is either None or a list of
- profiler_subentry objects:
- code called code object
- callcount how many times this is called
- reccallcount how many times this is called recursively
- totaltime total time spent in this call
- inlinetime inline time (not in further subcalls)
- [clinic start generated code]*/
- static PyObject *
- _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls)
- /*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/
- {
- statscollector_t collect;
- collect.state = _PyType_GetModuleState(cls);
- if (pending_exception(self)) {
- return NULL;
- }
- if (!self->externalTimer || self->externalTimerUnit == 0.0) {
- _PyTime_t onesec = _PyTime_FromSeconds(1);
- collect.factor = (double)1 / onesec;
- }
- else {
- collect.factor = self->externalTimerUnit;
- }
- collect.list = PyList_New(0);
- if (collect.list == NULL)
- return NULL;
- if (RotatingTree_Enum(self->profilerEntries, statsForEntry, &collect)
- != 0) {
- Py_DECREF(collect.list);
- return NULL;
- }
- return collect.list;
- }
- static int
- setSubcalls(ProfilerObject *pObj, int nvalue)
- {
- if (nvalue == 0)
- pObj->flags &= ~POF_SUBCALLS;
- else if (nvalue > 0)
- pObj->flags |= POF_SUBCALLS;
- return 0;
- }
- static int
- setBuiltins(ProfilerObject *pObj, int nvalue)
- {
- if (nvalue == 0)
- pObj->flags &= ~POF_BUILTINS;
- else if (nvalue > 0) {
- pObj->flags |= POF_BUILTINS;
- }
- return 0;
- }
- PyObject* pystart_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size)
- {
- if (size < 2) {
- PyErr_Format(PyExc_TypeError,
- "_pystart_callback expected 2 arguments, got %zd",
- size);
- return NULL;
- }
- PyObject* code = args[0];
- ptrace_enter_call((PyObject*)self, (void *)code, (PyObject *)code);
- Py_RETURN_NONE;
- }
- PyObject* pyreturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size)
- {
- if (size < 3) {
- PyErr_Format(PyExc_TypeError,
- "_pyreturn_callback expected 3 arguments, got %zd",
- size);
- return NULL;
- }
- PyObject* code = args[0];
- ptrace_leave_call((PyObject*)self, (void *)code);
- Py_RETURN_NONE;
- }
- PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg, PyObject* missing)
- {
- // return a new reference
- if (PyCFunction_Check(callable)) {
- Py_INCREF(callable);
- return (PyObject*)((PyCFunctionObject *)callable);
- }
- if (Py_TYPE(callable) == &PyMethodDescr_Type) {
- /* For backwards compatibility need to
- * convert to builtin method */
- /* If no arg, skip */
- if (self_arg == missing) {
- return NULL;
- }
- PyObject *meth = Py_TYPE(callable)->tp_descr_get(
- callable, self_arg, (PyObject*)Py_TYPE(self_arg));
- if (meth == NULL) {
- return NULL;
- }
- if (PyCFunction_Check(meth)) {
- return (PyObject*)((PyCFunctionObject *)meth);
- }
- }
- return NULL;
- }
- PyObject* ccall_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size)
- {
- if (size < 4) {
- PyErr_Format(PyExc_TypeError,
- "_ccall_callback expected 4 arguments, got %zd",
- size);
- return NULL;
- }
- if (self->flags & POF_BUILTINS) {
- PyObject* callable = args[2];
- PyObject* self_arg = args[3];
- PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing);
- if (cfunc) {
- ptrace_enter_call((PyObject*)self,
- ((PyCFunctionObject *)cfunc)->m_ml,
- cfunc);
- Py_DECREF(cfunc);
- }
- }
- Py_RETURN_NONE;
- }
- PyObject* creturn_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size)
- {
- if (size < 4) {
- PyErr_Format(PyExc_TypeError,
- "_creturn_callback expected 4 arguments, got %zd",
- size);
- return NULL;
- }
- if (self->flags & POF_BUILTINS) {
- PyObject* callable = args[2];
- PyObject* self_arg = args[3];
- PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing);
- if (cfunc) {
- ptrace_leave_call((PyObject*)self,
- ((PyCFunctionObject *)cfunc)->m_ml);
- Py_DECREF(cfunc);
- }
- }
- Py_RETURN_NONE;
- }
- static const struct {
- int event;
- const char* callback_method;
- } callback_table[] = {
- {PY_MONITORING_EVENT_PY_START, "_pystart_callback"},
- {PY_MONITORING_EVENT_PY_RESUME, "_pystart_callback"},
- {PY_MONITORING_EVENT_PY_THROW, "_pystart_callback"},
- {PY_MONITORING_EVENT_PY_RETURN, "_pyreturn_callback"},
- {PY_MONITORING_EVENT_PY_YIELD, "_pyreturn_callback"},
- {PY_MONITORING_EVENT_PY_UNWIND, "_pyreturn_callback"},
- {PY_MONITORING_EVENT_CALL, "_ccall_callback"},
- {PY_MONITORING_EVENT_C_RETURN, "_creturn_callback"},
- {PY_MONITORING_EVENT_C_RAISE, "_creturn_callback"},
- {0, NULL}
- };
- PyDoc_STRVAR(enable_doc, "\
- enable(subcalls=True, builtins=True)\n\
- \n\
- Start collecting profiling information.\n\
- If 'subcalls' is True, also records for each function\n\
- statistics separated according to its current caller.\n\
- If 'builtins' is True, records the time spent in\n\
- built-in functions separately from their caller.\n\
- ");
- static PyObject*
- profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
- {
- int subcalls = -1;
- int builtins = -1;
- static char *kwlist[] = {"subcalls", "builtins", 0};
- int all_events = 0;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pp:enable",
- kwlist, &subcalls, &builtins))
- return NULL;
- if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
- return NULL;
- }
- PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
- if (!monitoring) {
- return NULL;
- }
- PyObject *check = PyObject_CallMethod(monitoring,
- "use_tool_id", "is",
- self->tool_id, "cProfile");
- if (check == NULL) {
- PyErr_Format(PyExc_ValueError, "Another profiling tool is already active");
- goto error;
- }
- Py_DECREF(check);
- for (int i = 0; callback_table[i].callback_method; i++) {
- int event = (1 << callback_table[i].event);
- PyObject* callback = PyObject_GetAttrString((PyObject*)self, callback_table[i].callback_method);
- if (!callback) {
- goto error;
- }
- PyObject *register_result = PyObject_CallMethod(monitoring, "register_callback",
- "iiO", self->tool_id,
- event, callback);
- Py_DECREF(callback);
- if (register_result == NULL) {
- goto error;
- }
- Py_DECREF(register_result);
- all_events |= event;
- }
- PyObject *event_result = PyObject_CallMethod(monitoring, "set_events", "ii",
- self->tool_id, all_events);
- if (event_result == NULL) {
- goto error;
- }
- Py_DECREF(event_result);
- Py_DECREF(monitoring);
- self->flags |= POF_ENABLED;
- Py_RETURN_NONE;
- error:
- Py_DECREF(monitoring);
- return NULL;
- }
- static void
- flush_unmatched(ProfilerObject *pObj)
- {
- while (pObj->currentProfilerContext) {
- ProfilerContext *pContext = pObj->currentProfilerContext;
- ProfilerEntry *profEntry= pContext->ctxEntry;
- if (profEntry)
- Stop(pObj, pContext, profEntry);
- else
- pObj->currentProfilerContext = pContext->previous;
- if (pContext)
- PyMem_Free(pContext);
- }
- }
- PyDoc_STRVAR(disable_doc, "\
- disable()\n\
- \n\
- Stop collecting profiling information.\n\
- ");
- static PyObject*
- profiler_disable(ProfilerObject *self, PyObject* noarg)
- {
- if (self->flags & POF_EXT_TIMER) {
- PyErr_SetString(PyExc_RuntimeError,
- "cannot disable profiler in external timer");
- return NULL;
- }
- if (self->flags & POF_ENABLED) {
- PyObject* result = NULL;
- PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
- if (!monitoring) {
- return NULL;
- }
- for (int i = 0; callback_table[i].callback_method; i++) {
- result = PyObject_CallMethod(monitoring, "register_callback", "iiO", self->tool_id,
- (1 << callback_table[i].event), Py_None);
- if (!result) {
- Py_DECREF(monitoring);
- return NULL;
- }
- Py_DECREF(result);
- }
- result = PyObject_CallMethod(monitoring, "set_events", "ii", self->tool_id, 0);
- if (!result) {
- Py_DECREF(monitoring);
- return NULL;
- }
- Py_DECREF(result);
- result = PyObject_CallMethod(monitoring, "free_tool_id", "i", self->tool_id);
- if (!result) {
- Py_DECREF(monitoring);
- return NULL;
- }
- Py_DECREF(result);
- Py_DECREF(monitoring);
- self->flags &= ~POF_ENABLED;
- flush_unmatched(self);
- }
- if (pending_exception(self)) {
- return NULL;
- }
- Py_RETURN_NONE;
- }
- PyDoc_STRVAR(clear_doc, "\
- clear()\n\
- \n\
- Clear all profiling information collected so far.\n\
- ");
- static PyObject*
- profiler_clear(ProfilerObject *pObj, PyObject* noarg)
- {
- if (pObj->flags & POF_EXT_TIMER) {
- PyErr_SetString(PyExc_RuntimeError,
- "cannot clear profiler in external timer");
- return NULL;
- }
- clearEntries(pObj);
- Py_RETURN_NONE;
- }
- static int
- profiler_traverse(ProfilerObject *op, visitproc visit, void *arg)
- {
- Py_VISIT(Py_TYPE(op));
- Py_VISIT(op->externalTimer);
- return 0;
- }
- static void
- profiler_dealloc(ProfilerObject *op)
- {
- PyObject_GC_UnTrack(op);
- if (op->flags & POF_ENABLED) {
- PyThreadState *tstate = _PyThreadState_GET();
- if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
- _PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
- }
- }
- flush_unmatched(op);
- clearEntries(op);
- Py_XDECREF(op->externalTimer);
- PyTypeObject *tp = Py_TYPE(op);
- tp->tp_free(op);
- Py_DECREF(tp);
- }
- static int
- profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
- {
- PyObject *timer = NULL;
- double timeunit = 0.0;
- int subcalls = 1;
- int builtins = 1;
- static char *kwlist[] = {"timer", "timeunit",
- "subcalls", "builtins", 0};
- if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odpp:Profiler", kwlist,
- &timer, &timeunit,
- &subcalls, &builtins))
- return -1;
- if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
- return -1;
- pObj->externalTimerUnit = timeunit;
- Py_XSETREF(pObj->externalTimer, Py_XNewRef(timer));
- pObj->tool_id = PY_MONITORING_PROFILER_ID;
- PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
- if (!monitoring) {
- return -1;
- }
- pObj->missing = PyObject_GetAttrString(monitoring, "MISSING");
- if (!pObj->missing) {
- Py_DECREF(monitoring);
- return -1;
- }
- Py_DECREF(monitoring);
- return 0;
- }
- static PyMethodDef profiler_methods[] = {
- _LSPROF_PROFILER_GETSTATS_METHODDEF
- {"enable", _PyCFunction_CAST(profiler_enable),
- METH_VARARGS | METH_KEYWORDS, enable_doc},
- {"disable", (PyCFunction)profiler_disable,
- METH_NOARGS, disable_doc},
- {"clear", (PyCFunction)profiler_clear,
- METH_NOARGS, clear_doc},
- {"_pystart_callback", _PyCFunction_CAST(pystart_callback),
- METH_FASTCALL, NULL},
- {"_pyreturn_callback", _PyCFunction_CAST(pyreturn_callback),
- METH_FASTCALL, NULL},
- {"_ccall_callback", _PyCFunction_CAST(ccall_callback),
- METH_FASTCALL, NULL},
- {"_creturn_callback", _PyCFunction_CAST(creturn_callback),
- METH_FASTCALL, NULL},
- {NULL, NULL}
- };
- PyDoc_STRVAR(profiler_doc, "\
- Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
- \n\
- Builds a profiler object using the specified timer function.\n\
- The default timer is a fast built-in one based on real time.\n\
- For custom timer functions returning integers, timeunit can\n\
- be a float specifying a scale (i.e. how long each integer unit\n\
- is, in seconds).\n\
- ");
- static PyType_Slot _lsprof_profiler_type_spec_slots[] = {
- {Py_tp_doc, (void *)profiler_doc},
- {Py_tp_methods, profiler_methods},
- {Py_tp_dealloc, profiler_dealloc},
- {Py_tp_init, profiler_init},
- {Py_tp_traverse, profiler_traverse},
- {0, 0}
- };
- static PyType_Spec _lsprof_profiler_type_spec = {
- .name = "_lsprof.Profiler",
- .basicsize = sizeof(ProfilerObject),
- .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
- .slots = _lsprof_profiler_type_spec_slots,
- };
- static PyMethodDef moduleMethods[] = {
- {NULL, NULL}
- };
- static int
- _lsprof_traverse(PyObject *module, visitproc visit, void *arg)
- {
- _lsprof_state *state = _lsprof_get_state(module);
- Py_VISIT(state->profiler_type);
- Py_VISIT(state->stats_entry_type);
- Py_VISIT(state->stats_subentry_type);
- return 0;
- }
- static int
- _lsprof_clear(PyObject *module)
- {
- _lsprof_state *state = _lsprof_get_state(module);
- Py_CLEAR(state->profiler_type);
- Py_CLEAR(state->stats_entry_type);
- Py_CLEAR(state->stats_subentry_type);
- return 0;
- }
- static void
- _lsprof_free(void *module)
- {
- _lsprof_clear((PyObject *)module);
- }
- static int
- _lsprof_exec(PyObject *module)
- {
- _lsprof_state *state = PyModule_GetState(module);
- state->profiler_type = (PyTypeObject *)PyType_FromModuleAndSpec(
- module, &_lsprof_profiler_type_spec, NULL);
- if (state->profiler_type == NULL) {
- return -1;
- }
- if (PyModule_AddType(module, state->profiler_type) < 0) {
- return -1;
- }
- state->stats_entry_type = PyStructSequence_NewType(&profiler_entry_desc);
- if (state->stats_entry_type == NULL) {
- return -1;
- }
- if (PyModule_AddType(module, state->stats_entry_type) < 0) {
- return -1;
- }
- state->stats_subentry_type = PyStructSequence_NewType(&profiler_subentry_desc);
- if (state->stats_subentry_type == NULL) {
- return -1;
- }
- if (PyModule_AddType(module, state->stats_subentry_type) < 0) {
- return -1;
- }
- return 0;
- }
- static PyModuleDef_Slot _lsprofslots[] = {
- {Py_mod_exec, _lsprof_exec},
- // XXX gh-103092: fix isolation.
- {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
- //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
- {0, NULL}
- };
- static struct PyModuleDef _lsprofmodule = {
- PyModuleDef_HEAD_INIT,
- .m_name = "_lsprof",
- .m_doc = "Fast profiler",
- .m_size = sizeof(_lsprof_state),
- .m_methods = moduleMethods,
- .m_slots = _lsprofslots,
- .m_traverse = _lsprof_traverse,
- .m_clear = _lsprof_clear,
- .m_free = _lsprof_free
- };
- PyMODINIT_FUNC
- PyInit__lsprof(void)
- {
- return PyModuleDef_Init(&_lsprofmodule);
- }
|