2021-10-22 15:36:28 +02:00
|
|
|
#ifndef Py_BUILD_CORE_BUILTIN
|
|
|
|
# define Py_BUILD_CORE_MODULE 1
|
|
|
|
#endif
|
|
|
|
|
2006-02-08 13:53:56 +01:00
|
|
|
#include "Python.h"
|
2021-10-12 08:38:19 +02:00
|
|
|
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
2023-08-24 20:25:22 +02:00
|
|
|
#include "pycore_ceval.h" // _PyEval_SetProfile()
|
2021-10-13 14:09:13 +02:00
|
|
|
#include "pycore_pystate.h" // _PyThreadState_GET()
|
2006-02-08 13:53:56 +01:00
|
|
|
#include "rotatingtree.h"
|
|
|
|
|
|
|
|
/************************************************************/
|
|
|
|
/* Written by Brett Rosen and Ted Czotter */
|
|
|
|
|
|
|
|
struct _ProfilerEntry;
|
|
|
|
|
|
|
|
/* represents a function called from another function */
|
|
|
|
typedef struct _ProfilerSubEntry {
|
2010-05-09 17:52:27 +02:00
|
|
|
rotating_node_t header;
|
2019-04-11 12:11:46 +02:00
|
|
|
_PyTime_t tt;
|
|
|
|
_PyTime_t it;
|
2010-05-09 17:52:27 +02:00
|
|
|
long callcount;
|
|
|
|
long recursivecallcount;
|
|
|
|
long recursionLevel;
|
2006-02-08 13:53:56 +01:00
|
|
|
} ProfilerSubEntry;
|
|
|
|
|
|
|
|
/* represents a function or user defined block */
|
|
|
|
typedef struct _ProfilerEntry {
|
2010-05-09 17:52:27 +02:00
|
|
|
rotating_node_t header;
|
|
|
|
PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
|
2019-04-11 12:11:46 +02:00
|
|
|
_PyTime_t tt; /* total time in this entry */
|
|
|
|
_PyTime_t it; /* inline time in this entry (not in subcalls) */
|
2010-05-09 17:52:27 +02:00
|
|
|
long callcount; /* how many times this was called */
|
|
|
|
long recursivecallcount; /* how many times called recursively */
|
|
|
|
long recursionLevel;
|
|
|
|
rotating_node_t *calls;
|
2006-02-08 13:53:56 +01:00
|
|
|
} ProfilerEntry;
|
|
|
|
|
|
|
|
typedef struct _ProfilerContext {
|
2019-04-11 12:11:46 +02:00
|
|
|
_PyTime_t t0;
|
|
|
|
_PyTime_t subt;
|
2010-05-09 17:52:27 +02:00
|
|
|
struct _ProfilerContext *previous;
|
|
|
|
ProfilerEntry *ctxEntry;
|
2006-02-08 13:53:56 +01:00
|
|
|
} ProfilerContext;
|
|
|
|
|
|
|
|
typedef struct {
|
2010-05-09 17:52:27 +02:00
|
|
|
PyObject_HEAD
|
|
|
|
rotating_node_t *profilerEntries;
|
|
|
|
ProfilerContext *currentProfilerContext;
|
|
|
|
ProfilerContext *freelistProfilerContext;
|
|
|
|
int flags;
|
|
|
|
PyObject *externalTimer;
|
|
|
|
double externalTimerUnit;
|
2023-05-05 19:38:47 +02:00
|
|
|
int tool_id;
|
|
|
|
PyObject* missing;
|
2006-02-08 13:53:56 +01:00
|
|
|
} ProfilerObject;
|
|
|
|
|
|
|
|
#define POF_ENABLED 0x001
|
|
|
|
#define POF_SUBCALLS 0x002
|
|
|
|
#define POF_BUILTINS 0x004
|
|
|
|
#define POF_NOMEMORY 0x100
|
|
|
|
|
2020-09-21 14:40:42 +02:00
|
|
|
/*[clinic input]
|
|
|
|
module _lsprof
|
|
|
|
class _lsprof.Profiler "ProfilerObject *" "&ProfilerType"
|
|
|
|
[clinic start generated code]*/
|
|
|
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e349ac952152f336]*/
|
2006-02-08 13:53:56 +01:00
|
|
|
|
2020-09-21 14:40:42 +02:00
|
|
|
#include "clinic/_lsprof.c.h"
|
|
|
|
|
2020-09-23 12:33:21 +02:00
|
|
|
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;
|
|
|
|
}
|
2006-02-08 13:53:56 +01:00
|
|
|
|
|
|
|
/*** External Timers ***/
|
|
|
|
|
2019-04-11 12:11:46 +02:00
|
|
|
static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
|
2006-02-08 13:53:56 +01:00
|
|
|
{
|
2021-10-12 00:42:23 +02:00
|
|
|
PyObject *o = _PyObject_CallNoArgs(pObj->externalTimer);
|
2010-05-09 17:52:27 +02:00
|
|
|
if (o == NULL) {
|
|
|
|
PyErr_WriteUnraisable(pObj->externalTimer);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-04-11 12:11:46 +02:00
|
|
|
|
|
|
|
_PyTime_t result;
|
|
|
|
int err;
|
2010-05-09 17:52:27 +02:00
|
|
|
if (pObj->externalTimerUnit > 0.0) {
|
|
|
|
/* interpret the result as an integer that will be scaled
|
|
|
|
in profiler_getstats() */
|
2019-04-11 12:11:46 +02:00
|
|
|
err = _PyTime_FromNanosecondsObject(&result, o);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* interpret the result as a double measured in seconds.
|
2019-04-11 12:11:46 +02:00
|
|
|
As the profiler works with _PyTime_t internally
|
2010-05-09 17:52:27 +02:00
|
|
|
we convert it to a large integer */
|
2019-04-11 12:11:46 +02:00
|
|
|
err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
Py_DECREF(o);
|
2019-04-11 12:11:46 +02:00
|
|
|
if (err < 0) {
|
2010-05-09 17:52:27 +02:00
|
|
|
PyErr_WriteUnraisable(pObj->externalTimer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return result;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
2019-04-11 12:11:46 +02:00
|
|
|
static inline _PyTime_t
|
|
|
|
call_timer(ProfilerObject *pObj)
|
|
|
|
{
|
|
|
|
if (pObj->externalTimer != NULL) {
|
|
|
|
return CallExternalTimer(pObj);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return _PyTime_GetPerfCounter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-08 13:53:56 +01:00
|
|
|
|
|
|
|
/*** ProfilerObject ***/
|
|
|
|
|
|
|
|
static PyObject *
|
|
|
|
normalizeUserObj(PyObject *obj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
PyCFunctionObject *fn;
|
|
|
|
if (!PyCFunction_Check(obj)) {
|
2022-11-14 13:08:43 +01:00
|
|
|
return Py_NewRef(obj);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
/* 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;
|
2011-06-20 14:59:53 +02:00
|
|
|
PyObject *modname = NULL;
|
|
|
|
if (mod != NULL) {
|
|
|
|
if (PyUnicode_Check(mod)) {
|
2022-11-14 13:08:43 +01:00
|
|
|
modname = Py_NewRef(mod);
|
2010-12-09 00:31:48 +01:00
|
|
|
}
|
2011-06-20 14:59:53 +02:00
|
|
|
else if (PyModule_Check(mod)) {
|
|
|
|
modname = PyModule_GetNameObject(mod);
|
|
|
|
if (modname == NULL)
|
|
|
|
PyErr_Clear();
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-20 14:59:53 +02:00
|
|
|
if (modname != NULL) {
|
2016-11-16 09:17:58 +01:00
|
|
|
if (!_PyUnicode_EqualToASCIIString(modname, "builtins")) {
|
2011-06-20 14:59:53 +02:00
|
|
|
PyObject *result;
|
|
|
|
result = PyUnicode_FromFormat("<%U.%s>", modname,
|
|
|
|
fn->m_ml->ml_name);
|
|
|
|
Py_DECREF(modname);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
Py_DECREF(modname);
|
2010-12-10 19:14:16 +01:00
|
|
|
}
|
2011-06-20 14:59:53 +02:00
|
|
|
return PyUnicode_FromFormat("<%s>", fn->m_ml->ml_name);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
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);
|
2014-06-28 05:49:29 +02:00
|
|
|
PyObject *modname = fn->m_module;
|
|
|
|
|
2010-05-09 17:52:27 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-06-28 05:49:29 +02:00
|
|
|
/* Otherwise, use __module__ */
|
2010-05-09 17:52:27 +02:00
|
|
|
PyErr_Clear();
|
2014-06-28 05:49:29 +02:00
|
|
|
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);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ProfilerEntry*
|
|
|
|
newProfilerEntry(ProfilerObject *pObj, void *key, PyObject *userObj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
ProfilerEntry *self;
|
2013-07-07 16:21:41 +02:00
|
|
|
self = (ProfilerEntry*) PyMem_Malloc(sizeof(ProfilerEntry));
|
2010-05-09 17:52:27 +02:00
|
|
|
if (self == NULL) {
|
|
|
|
pObj->flags |= POF_NOMEMORY;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
userObj = normalizeUserObj(userObj);
|
|
|
|
if (userObj == NULL) {
|
|
|
|
PyErr_Clear();
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(self);
|
2010-05-09 17:52:27 +02:00
|
|
|
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;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ProfilerEntry*
|
|
|
|
getEntry(ProfilerObject *pObj, void *key)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
return (ProfilerEntry*) RotatingTree_Get(&pObj->profilerEntries, key);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
2010-05-09 17:52:27 +02:00
|
|
|
static ProfilerSubEntry *
|
2006-02-08 13:53:56 +01:00
|
|
|
getSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
return (ProfilerSubEntry*) RotatingTree_Get(&caller->calls,
|
|
|
|
(void *)entry);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ProfilerSubEntry *
|
|
|
|
newSubEntry(ProfilerObject *pObj, ProfilerEntry *caller, ProfilerEntry* entry)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
ProfilerSubEntry *self;
|
2013-07-07 16:21:41 +02:00
|
|
|
self = (ProfilerSubEntry*) PyMem_Malloc(sizeof(ProfilerSubEntry));
|
2010-05-09 17:52:27 +02:00
|
|
|
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;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int freeSubEntry(rotating_node_t *header, void *arg)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
ProfilerSubEntry *subentry = (ProfilerSubEntry*) header;
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(subentry);
|
2010-05-09 17:52:27 +02:00
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int freeEntry(rotating_node_t *header, void *arg)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
ProfilerEntry *entry = (ProfilerEntry*) header;
|
|
|
|
RotatingTree_Enum(entry->calls, freeSubEntry, NULL);
|
|
|
|
Py_DECREF(entry->userObj);
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(entry);
|
2010-05-09 17:52:27 +02:00
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void clearEntries(ProfilerObject *pObj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
|
|
|
|
pObj->profilerEntries = EMPTY_ROTATING_TREE;
|
|
|
|
/* release the memory hold by the ProfilerContexts */
|
|
|
|
if (pObj->currentProfilerContext) {
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(pObj->currentProfilerContext);
|
2010-05-09 17:52:27 +02:00
|
|
|
pObj->currentProfilerContext = NULL;
|
|
|
|
}
|
|
|
|
while (pObj->freelistProfilerContext) {
|
|
|
|
ProfilerContext *c = pObj->freelistProfilerContext;
|
|
|
|
pObj->freelistProfilerContext = c->previous;
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(c);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
pObj->freelistProfilerContext = NULL;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-04-11 12:11:46 +02:00
|
|
|
self->t0 = call_timer(pObj);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
|
|
|
|
{
|
2019-04-11 12:11:46 +02:00
|
|
|
_PyTime_t tt = call_timer(pObj) - self->t0;
|
|
|
|
_PyTime_t it = tt - self->subt;
|
2010-05-09 17:52:27 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ptrace_enter_call(PyObject *self, void *key, PyObject *userObj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
/* 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. */
|
2023-02-24 22:43:03 +01:00
|
|
|
PyObject *exc = PyErr_GetRaisedException();
|
2010-05-09 17:52:27 +02:00
|
|
|
|
|
|
|
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*)
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Malloc(sizeof(ProfilerContext));
|
2010-05-09 17:52:27 +02:00
|
|
|
if (pContext == NULL) {
|
|
|
|
pObj->flags |= POF_NOMEMORY;
|
|
|
|
goto restorePyerr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
initContext(pObj, pContext, profEntry);
|
Merged revisions 57778-58052 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r57820 | georg.brandl | 2007-08-31 08:59:27 +0200 (Fri, 31 Aug 2007) | 2 lines
Document new shorthand notation for index entries.
........
r57827 | georg.brandl | 2007-08-31 10:47:51 +0200 (Fri, 31 Aug 2007) | 2 lines
Fix subitem markup.
........
r57833 | martin.v.loewis | 2007-08-31 12:01:07 +0200 (Fri, 31 Aug 2007) | 1 line
Mark registry components as 64-bit on Win64.
........
r57854 | bill.janssen | 2007-08-31 21:02:23 +0200 (Fri, 31 Aug 2007) | 1 line
deprecate use of FakeSocket
........
r57855 | bill.janssen | 2007-08-31 21:02:46 +0200 (Fri, 31 Aug 2007) | 1 line
remove mentions of socket.ssl in comments
........
r57856 | bill.janssen | 2007-08-31 21:03:31 +0200 (Fri, 31 Aug 2007) | 1 line
remove use of non-existent SSLFakeSocket in apparently untested code
........
r57859 | martin.v.loewis | 2007-09-01 08:36:03 +0200 (Sat, 01 Sep 2007) | 3 lines
Bug #1737210: Change Manufacturer of Windows installer to PSF.
Will backport to 2.5.
........
r57865 | georg.brandl | 2007-09-01 09:51:24 +0200 (Sat, 01 Sep 2007) | 2 lines
Fix RST link (backport from Py3k).
........
r57876 | georg.brandl | 2007-09-01 17:49:49 +0200 (Sat, 01 Sep 2007) | 2 lines
Document sets' ">" and "<" operations (backport from py3k).
........
r57878 | skip.montanaro | 2007-09-01 19:40:03 +0200 (Sat, 01 Sep 2007) | 4 lines
Added a note and examples to explain that re.split does not split on an
empty pattern match. (issue 852532).
........
r57879 | walter.doerwald | 2007-09-01 20:18:09 +0200 (Sat, 01 Sep 2007) | 2 lines
Fix wrong function names.
........
r57880 | walter.doerwald | 2007-09-01 20:34:05 +0200 (Sat, 01 Sep 2007) | 2 lines
Fix typo.
........
r57889 | andrew.kuchling | 2007-09-01 22:31:59 +0200 (Sat, 01 Sep 2007) | 1 line
Markup fix
........
r57892 | andrew.kuchling | 2007-09-01 22:43:36 +0200 (Sat, 01 Sep 2007) | 1 line
Add various items
........
r57895 | andrew.kuchling | 2007-09-01 23:17:58 +0200 (Sat, 01 Sep 2007) | 1 line
Wording change
........
r57896 | andrew.kuchling | 2007-09-01 23:18:31 +0200 (Sat, 01 Sep 2007) | 1 line
Add more items
........
r57904 | ronald.oussoren | 2007-09-02 11:46:07 +0200 (Sun, 02 Sep 2007) | 3 lines
Macosx: this patch ensures that the value of MACOSX_DEPLOYMENT_TARGET used
by the Makefile is also used at configure-time.
........
r57925 | georg.brandl | 2007-09-03 09:16:46 +0200 (Mon, 03 Sep 2007) | 2 lines
Fix #883466: don't allow Unicode as arguments to quopri and uu codecs.
........
r57936 | matthias.klose | 2007-09-04 01:33:04 +0200 (Tue, 04 Sep 2007) | 2 lines
- Added support for linking the bsddb module against BerkeleyDB 4.6.x.
........
r57954 | mark.summerfield | 2007-09-04 10:16:15 +0200 (Tue, 04 Sep 2007) | 3 lines
Added cross-references plus a note about dict & list shallow copying.
........
r57958 | martin.v.loewis | 2007-09-04 11:51:57 +0200 (Tue, 04 Sep 2007) | 3 lines
Document that we rely on the OS to release the crypto
context. Fixes #1626801.
........
r57960 | martin.v.loewis | 2007-09-04 15:13:14 +0200 (Tue, 04 Sep 2007) | 3 lines
Patch #1388440: Add set_completion_display_matches_hook and
get_completion_type to readline.
........
r57961 | martin.v.loewis | 2007-09-04 16:19:28 +0200 (Tue, 04 Sep 2007) | 3 lines
Patch #1031213: Decode source line in SyntaxErrors back to its original
source encoding. Will backport to 2.5.
........
r57972 | matthias.klose | 2007-09-04 20:17:36 +0200 (Tue, 04 Sep 2007) | 3 lines
- Makefile.pre.in(buildbottest): Run an optional script pybuildbot.identify
to include some information about the build environment.
........
r57973 | matthias.klose | 2007-09-04 21:05:38 +0200 (Tue, 04 Sep 2007) | 2 lines
- Makefile.pre.in(buildbottest): Remove whitespace at eol.
........
r57975 | matthias.klose | 2007-09-04 22:46:02 +0200 (Tue, 04 Sep 2007) | 2 lines
- Fix libffi configure for hppa*-*-linux* | parisc*-*-linux*.
........
r57980 | bill.janssen | 2007-09-05 02:46:27 +0200 (Wed, 05 Sep 2007) | 1 line
SSL certificate distinguished names should be represented by tuples
........
r57985 | martin.v.loewis | 2007-09-05 08:39:17 +0200 (Wed, 05 Sep 2007) | 3 lines
Patch #1105: Explain that one needs to build the solution
to get dependencies right.
........
r57987 | armin.rigo | 2007-09-05 09:51:21 +0200 (Wed, 05 Sep 2007) | 4 lines
PyDict_GetItem() returns a borrowed reference.
There are probably a number of places that are open to attacks
such as the following one, in bltinmodule.c:min_max().
........
r57991 | martin.v.loewis | 2007-09-05 13:47:34 +0200 (Wed, 05 Sep 2007) | 3 lines
Patch #786737: Allow building in a tree of symlinks pointing to
a readonly source.
........
r57993 | georg.brandl | 2007-09-05 15:36:44 +0200 (Wed, 05 Sep 2007) | 2 lines
Backport from Py3k: Bug #1684991: explain lookup semantics for __special__ methods (new-style classes only).
........
r58004 | armin.rigo | 2007-09-06 10:30:51 +0200 (Thu, 06 Sep 2007) | 4 lines
Patch #1733973 by peaker:
ptrace_enter_call() assumes no exception is currently set.
This assumption is broken when throwing into a generator.
........
r58006 | armin.rigo | 2007-09-06 11:30:38 +0200 (Thu, 06 Sep 2007) | 4 lines
PyDict_GetItem() returns a borrowed reference.
This attack is against ceval.c:IMPORT_NAME, which calls an
object (__builtin__.__import__) without holding a reference to it.
........
r58013 | georg.brandl | 2007-09-06 16:49:56 +0200 (Thu, 06 Sep 2007) | 2 lines
Backport from 3k: #1116: fix reference to old filename.
........
r58021 | thomas.heller | 2007-09-06 22:26:20 +0200 (Thu, 06 Sep 2007) | 1 line
Fix typo: c_float represents to C float type.
........
r58022 | skip.montanaro | 2007-09-07 00:29:06 +0200 (Fri, 07 Sep 2007) | 3 lines
If this is correct for py3k branch and it's already in the release25-maint
branch, seems like it ought to be on the trunk as well.
........
r58023 | gregory.p.smith | 2007-09-07 00:59:59 +0200 (Fri, 07 Sep 2007) | 4 lines
Apply the fix from Issue1112 to make this test more robust and keep
windows happy.
........
r58031 | brett.cannon | 2007-09-07 05:17:50 +0200 (Fri, 07 Sep 2007) | 4 lines
Make uuid1 and uuid4 tests conditional on whether ctypes can be imported;
implementation of either function depends on ctypes but uuid as a whole does
not.
........
r58032 | brett.cannon | 2007-09-07 06:18:30 +0200 (Fri, 07 Sep 2007) | 6 lines
Fix a crasher where Python code managed to infinitely recurse in C code without
ever going back out to Python code in PyObject_Call(). Required introducing a
static RuntimeError instance so that normalizing an exception there is no
reliance on a recursive call that would put the exception system over the
recursion check itself.
........
r58034 | thomas.heller | 2007-09-07 08:32:17 +0200 (Fri, 07 Sep 2007) | 1 line
Add a 'c_longdouble' type to the ctypes module.
........
r58035 | thomas.heller | 2007-09-07 11:30:40 +0200 (Fri, 07 Sep 2007) | 1 line
Remove unneeded #include.
........
r58036 | thomas.heller | 2007-09-07 11:33:24 +0200 (Fri, 07 Sep 2007) | 6 lines
Backport from py3k branch:
Add a workaround for a strange bug on win64, when _ctypes is compiled
with the SDK compiler. This should fix the failing
Lib\ctypes\test\test_as_parameter.py test.
........
r58037 | georg.brandl | 2007-09-07 16:14:40 +0200 (Fri, 07 Sep 2007) | 2 lines
Fix a wrong indentation for sublists.
........
r58043 | georg.brandl | 2007-09-07 22:10:49 +0200 (Fri, 07 Sep 2007) | 2 lines
#1095: ln -f doesn't work portably, fix in Makefile.
........
r58049 | skip.montanaro | 2007-09-08 02:34:17 +0200 (Sat, 08 Sep 2007) | 1 line
be explicit about the actual location of the missing file
........
2007-09-08 19:39:28 +02:00
|
|
|
|
|
|
|
restorePyerr:
|
2023-02-24 22:43:03 +01:00
|
|
|
PyErr_SetRaisedException(exc);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ptrace_leave_call(PyObject *self, void *key)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
/* 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;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pending_exception(ProfilerObject *pObj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
if (pObj->flags & POF_NOMEMORY) {
|
|
|
|
pObj->flags -= POF_NOMEMORY;
|
|
|
|
PyErr_SetString(PyExc_MemoryError,
|
|
|
|
"memory was exhausted while profiling");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************/
|
|
|
|
|
|
|
|
static PyStructSequence_Field profiler_entry_fields[] = {
|
2010-05-09 17:52:27 +02:00
|
|
|
{"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}
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyStructSequence_Field profiler_subentry_fields[] = {
|
2010-05-09 17:52:27 +02:00
|
|
|
{"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}
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyStructSequence_Desc profiler_entry_desc = {
|
2020-09-23 12:33:21 +02:00
|
|
|
.name = "_lsprof.profiler_entry",
|
|
|
|
.fields = profiler_entry_fields,
|
2020-11-06 17:04:47 +01:00
|
|
|
.doc = NULL,
|
2020-09-23 12:33:21 +02:00
|
|
|
.n_in_sequence = 6
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyStructSequence_Desc profiler_subentry_desc = {
|
2020-09-23 12:33:21 +02:00
|
|
|
.name = "_lsprof.profiler_subentry",
|
|
|
|
.fields = profiler_subentry_fields,
|
2020-11-06 17:04:47 +01:00
|
|
|
.doc = NULL,
|
2020-09-23 12:33:21 +02:00
|
|
|
.n_in_sequence = 5
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
2010-05-09 17:52:27 +02:00
|
|
|
PyObject *list;
|
|
|
|
PyObject *sublist;
|
|
|
|
double factor;
|
2020-09-23 12:33:21 +02:00
|
|
|
_lsprof_state *state;
|
2006-02-08 13:53:56 +01:00
|
|
|
} statscollector_t;
|
|
|
|
|
|
|
|
static int statsForSubEntry(rotating_node_t *node, void *arg)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
ProfilerSubEntry *sentry = (ProfilerSubEntry*) node;
|
|
|
|
statscollector_t *collect = (statscollector_t*) arg;
|
|
|
|
ProfilerEntry *entry = (ProfilerEntry*) sentry->header.key;
|
|
|
|
int err;
|
|
|
|
PyObject *sinfo;
|
2020-09-23 12:33:21 +02:00
|
|
|
sinfo = PyObject_CallFunction((PyObject*) collect->state->stats_subentry_type,
|
2010-05-09 17:52:27 +02:00
|
|
|
"((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;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int statsForEntry(rotating_node_t *node, void *arg)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
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 {
|
2022-11-14 13:08:43 +01:00
|
|
|
collect->sublist = Py_NewRef(Py_None);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 12:33:21 +02:00
|
|
|
info = PyObject_CallFunction((PyObject*) collect->state->stats_entry_type,
|
2010-05-09 17:52:27 +02:00
|
|
|
"((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;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
2020-09-21 14:40:42 +02:00
|
|
|
/*[clinic input]
|
|
|
|
_lsprof.Profiler.getstats
|
2006-02-08 13:53:56 +01:00
|
|
|
|
2020-09-23 12:33:21 +02:00
|
|
|
cls: defining_class
|
|
|
|
|
2020-09-21 14:40:42 +02:00
|
|
|
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 *
|
2020-09-23 12:33:21 +02:00
|
|
|
_lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls)
|
|
|
|
/*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/
|
2006-02-08 13:53:56 +01:00
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
statscollector_t collect;
|
2023-02-24 21:16:29 +01:00
|
|
|
collect.state = _PyType_GetModuleState(cls);
|
2020-09-21 14:40:42 +02:00
|
|
|
if (pending_exception(self)) {
|
2010-05-09 17:52:27 +02:00
|
|
|
return NULL;
|
2020-03-13 16:39:12 +01:00
|
|
|
}
|
2020-09-21 14:40:42 +02:00
|
|
|
if (!self->externalTimer || self->externalTimerUnit == 0.0) {
|
2019-04-11 12:11:46 +02:00
|
|
|
_PyTime_t onesec = _PyTime_FromSeconds(1);
|
|
|
|
collect.factor = (double)1 / onesec;
|
|
|
|
}
|
|
|
|
else {
|
2020-09-21 14:40:42 +02:00
|
|
|
collect.factor = self->externalTimerUnit;
|
2019-04-11 12:11:46 +02:00
|
|
|
}
|
|
|
|
|
2010-05-09 17:52:27 +02:00
|
|
|
collect.list = PyList_New(0);
|
|
|
|
if (collect.list == NULL)
|
|
|
|
return NULL;
|
2020-09-21 14:40:42 +02:00
|
|
|
if (RotatingTree_Enum(self->profilerEntries, statsForEntry, &collect)
|
2010-05-09 17:52:27 +02:00
|
|
|
!= 0) {
|
|
|
|
Py_DECREF(collect.list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return collect.list;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
setSubcalls(ProfilerObject *pObj, int nvalue)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
if (nvalue == 0)
|
|
|
|
pObj->flags &= ~POF_SUBCALLS;
|
|
|
|
else if (nvalue > 0)
|
|
|
|
pObj->flags |= POF_SUBCALLS;
|
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
setBuiltins(ProfilerObject *pObj, int nvalue)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
if (nvalue == 0)
|
|
|
|
pObj->flags &= ~POF_BUILTINS;
|
|
|
|
else if (nvalue > 0) {
|
|
|
|
pObj->flags |= POF_BUILTINS;
|
|
|
|
}
|
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
2023-05-05 19:38:47 +02:00
|
|
|
PyObject* pystart_callback(ProfilerObject* self, PyObject *const *args, Py_ssize_t size)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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 (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 (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"},
|
2023-06-30 01:14:09 +02:00
|
|
|
{PY_MONITORING_EVENT_PY_THROW, "_pystart_callback"},
|
2023-05-05 19:38:47 +02:00
|
|
|
{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}
|
|
|
|
};
|
|
|
|
|
2006-02-08 13:53:56 +01:00
|
|
|
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)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
int subcalls = -1;
|
|
|
|
int builtins = -1;
|
|
|
|
static char *kwlist[] = {"subcalls", "builtins", 0};
|
2023-05-05 19:38:47 +02:00
|
|
|
int all_events = 0;
|
|
|
|
|
2022-12-03 20:52:21 +01:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pp:enable",
|
2010-05-09 17:52:27 +02:00
|
|
|
kwlist, &subcalls, &builtins))
|
|
|
|
return NULL;
|
2020-03-13 16:39:12 +01:00
|
|
|
if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-05-05 19:38:47 +02:00
|
|
|
PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
|
|
|
|
if (!monitoring) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyObject_CallMethod(monitoring, "use_tool_id", "is", self->tool_id, "cProfile") == NULL) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Another profiling tool is already active");
|
|
|
|
Py_DECREF(monitoring);
|
2010-05-09 17:52:27 +02:00
|
|
|
return NULL;
|
2020-03-13 16:39:12 +01:00
|
|
|
}
|
|
|
|
|
2023-05-05 19:38:47 +02:00
|
|
|
for (int i = 0; callback_table[i].callback_method; i++) {
|
|
|
|
PyObject* callback = PyObject_GetAttrString((PyObject*)self, callback_table[i].callback_method);
|
|
|
|
if (!callback) {
|
|
|
|
Py_DECREF(monitoring);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_XDECREF(PyObject_CallMethod(monitoring, "register_callback", "iiO", self->tool_id,
|
|
|
|
(1 << callback_table[i].event),
|
|
|
|
callback));
|
|
|
|
Py_DECREF(callback);
|
|
|
|
all_events |= (1 << callback_table[i].event);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PyObject_CallMethod(monitoring, "set_events", "ii", self->tool_id, all_events)) {
|
|
|
|
Py_DECREF(monitoring);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_DECREF(monitoring);
|
|
|
|
|
2010-05-09 17:52:27 +02:00
|
|
|
self->flags |= POF_ENABLED;
|
2017-01-23 08:47:21 +01:00
|
|
|
Py_RETURN_NONE;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flush_unmatched(ProfilerObject *pObj)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
while (pObj->currentProfilerContext) {
|
|
|
|
ProfilerContext *pContext = pObj->currentProfilerContext;
|
|
|
|
ProfilerEntry *profEntry= pContext->ctxEntry;
|
|
|
|
if (profEntry)
|
|
|
|
Stop(pObj, pContext, profEntry);
|
|
|
|
else
|
|
|
|
pObj->currentProfilerContext = pContext->previous;
|
|
|
|
if (pContext)
|
2013-07-07 16:21:41 +02:00
|
|
|
PyMem_Free(pContext);
|
2010-05-09 17:52:27 +02:00
|
|
|
}
|
2006-02-08 13:53:56 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(disable_doc, "\
|
|
|
|
disable()\n\
|
|
|
|
\n\
|
|
|
|
Stop collecting profiling information.\n\
|
|
|
|
");
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
profiler_disable(ProfilerObject *self, PyObject* noarg)
|
|
|
|
{
|
2023-05-05 19:38:47 +02:00
|
|
|
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);
|
2020-03-13 16:39:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pending_exception(self)) {
|
2010-05-09 17:52:27 +02:00
|
|
|
return NULL;
|
2020-03-13 16:39:12 +01:00
|
|
|
}
|
2017-01-23 08:47:21 +01:00
|
|
|
Py_RETURN_NONE;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PyDoc_STRVAR(clear_doc, "\
|
|
|
|
clear()\n\
|
|
|
|
\n\
|
|
|
|
Clear all profiling information collected so far.\n\
|
|
|
|
");
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
profiler_clear(ProfilerObject *pObj, PyObject* noarg)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
clearEntries(pObj);
|
2017-01-23 08:47:21 +01:00
|
|
|
Py_RETURN_NONE;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
2021-05-27 09:29:00 +02:00
|
|
|
static int
|
|
|
|
profiler_traverse(ProfilerObject *op, visitproc visit, void *arg)
|
|
|
|
{
|
|
|
|
Py_VISIT(Py_TYPE(op));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-02-08 13:53:56 +01:00
|
|
|
static void
|
|
|
|
profiler_dealloc(ProfilerObject *op)
|
|
|
|
{
|
2022-07-27 14:03:50 +02:00
|
|
|
PyObject_GC_UnTrack(op);
|
2020-03-13 16:39:12 +01:00
|
|
|
if (op->flags & POF_ENABLED) {
|
2021-10-13 14:09:13 +02:00
|
|
|
PyThreadState *tstate = _PyThreadState_GET();
|
2020-03-13 16:39:12 +01:00
|
|
|
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
|
2022-07-05 20:18:47 +02:00
|
|
|
_PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
|
2020-03-13 16:39:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-09 17:52:27 +02:00
|
|
|
flush_unmatched(op);
|
|
|
|
clearEntries(op);
|
|
|
|
Py_XDECREF(op->externalTimer);
|
2020-09-23 12:33:21 +02:00
|
|
|
PyTypeObject *tp = Py_TYPE(op);
|
|
|
|
tp->tp_free(op);
|
|
|
|
Py_DECREF(tp);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
|
|
|
|
{
|
2010-05-09 17:52:27 +02:00
|
|
|
PyObject *timer = NULL;
|
|
|
|
double timeunit = 0.0;
|
|
|
|
int subcalls = 1;
|
|
|
|
int builtins = 1;
|
|
|
|
static char *kwlist[] = {"timer", "timeunit",
|
|
|
|
"subcalls", "builtins", 0};
|
|
|
|
|
2022-12-03 20:52:21 +01:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odpp:Profiler", kwlist,
|
2010-05-09 17:52:27 +02:00
|
|
|
&timer, &timeunit,
|
|
|
|
&subcalls, &builtins))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
|
|
|
|
return -1;
|
|
|
|
pObj->externalTimerUnit = timeunit;
|
2022-11-14 13:08:43 +01:00
|
|
|
Py_XSETREF(pObj->externalTimer, Py_XNewRef(timer));
|
2023-05-05 19:38:47 +02:00
|
|
|
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);
|
2010-05-09 17:52:27 +02:00
|
|
|
return 0;
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef profiler_methods[] = {
|
2020-09-21 14:40:42 +02:00
|
|
|
_LSPROF_PROFILER_GETSTATS_METHODDEF
|
2023-05-05 19:38:47 +02:00
|
|
|
{"enable", _PyCFunction_CAST(profiler_enable),
|
2010-05-09 17:52:27 +02:00
|
|
|
METH_VARARGS | METH_KEYWORDS, enable_doc},
|
2023-05-05 19:38:47 +02:00
|
|
|
{"disable", (PyCFunction)profiler_disable,
|
2010-05-09 17:52:27 +02:00
|
|
|
METH_NOARGS, disable_doc},
|
2023-05-05 19:38:47 +02:00
|
|
|
{"clear", (PyCFunction)profiler_clear,
|
2010-05-09 17:52:27 +02:00
|
|
|
METH_NOARGS, clear_doc},
|
2023-05-05 19:38:47 +02:00
|
|
|
{"_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},
|
2010-05-09 17:52:27 +02:00
|
|
|
{NULL, NULL}
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
PyDoc_STRVAR(profiler_doc, "\
|
2018-08-03 11:09:57 +02:00
|
|
|
Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\
|
2006-02-08 13:53:56 +01:00
|
|
|
\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\
|
2018-08-03 11:09:57 +02:00
|
|
|
For custom timer functions returning integers, timeunit can\n\
|
2006-02-08 13:53:56 +01:00
|
|
|
be a float specifying a scale (i.e. how long each integer unit\n\
|
|
|
|
is, in seconds).\n\
|
|
|
|
");
|
|
|
|
|
2020-09-23 12:33:21 +02:00
|
|
|
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},
|
2021-05-27 09:29:00 +02:00
|
|
|
{Py_tp_traverse, profiler_traverse},
|
2020-09-23 12:33:21 +02:00
|
|
|
{0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyType_Spec _lsprof_profiler_type_spec = {
|
|
|
|
.name = "_lsprof.Profiler",
|
|
|
|
.basicsize = sizeof(ProfilerObject),
|
2021-06-17 12:06:09 +02:00
|
|
|
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
|
|
|
|
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
|
2020-09-23 12:33:21 +02:00
|
|
|
.slots = _lsprof_profiler_type_spec_slots,
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static PyMethodDef moduleMethods[] = {
|
2010-05-09 17:52:27 +02:00
|
|
|
{NULL, NULL}
|
2006-02-08 13:53:56 +01:00
|
|
|
};
|
|
|
|
|
2020-09-23 12:33:21 +02:00
|
|
|
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},
|
2023-05-05 23:11:27 +02:00
|
|
|
// 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},
|
2020-09-23 12:33:21 +02:00
|
|
|
{0, NULL}
|
|
|
|
};
|
2008-06-11 07:26:20 +02:00
|
|
|
|
|
|
|
static struct PyModuleDef _lsprofmodule = {
|
2010-05-09 17:52:27 +02:00
|
|
|
PyModuleDef_HEAD_INIT,
|
2020-09-23 12:33:21 +02:00
|
|
|
.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
|
2008-06-11 07:26:20 +02:00
|
|
|
};
|
|
|
|
|
2006-02-08 13:53:56 +01:00
|
|
|
PyMODINIT_FUNC
|
2008-06-11 07:26:20 +02:00
|
|
|
PyInit__lsprof(void)
|
2006-02-08 13:53:56 +01:00
|
|
|
{
|
2020-09-23 12:33:21 +02:00
|
|
|
return PyModuleDef_Init(&_lsprofmodule);
|
2006-02-08 13:53:56 +01:00
|
|
|
}
|