cpython/Modules/_lsprof.c
Thomas Wouters 89d996e5c2 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 17:39:28 +00:00

890 lines
24 KiB
C

#include "Python.h"
#include "compile.h"
#include "frameobject.h"
#include "structseq.h"
#include "rotatingtree.h"
#if !defined(HAVE_LONG_LONG)
#error "This module requires long longs!"
#endif
/*** Selection of a high-precision timer ***/
#ifdef MS_WINDOWS
#include <windows.h>
static PY_LONG_LONG
hpTimer(void)
{
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return li.QuadPart;
}
static double
hpTimerUnit(void)
{
LARGE_INTEGER li;
if (QueryPerformanceFrequency(&li))
return 1.0 / li.QuadPart;
else
return 0.000001; /* unlikely */
}
#else /* !MS_WINDOWS */
#ifndef HAVE_GETTIMEOFDAY
#error "This module requires gettimeofday() on non-Windows platforms!"
#endif
#if (defined(PYOS_OS2) && defined(PYCC_GCC))
#include <sys/time.h>
#else
#include <sys/resource.h>
#include <sys/times.h>
#endif
static PY_LONG_LONG
hpTimer(void)
{
struct timeval tv;
PY_LONG_LONG ret;
#ifdef GETTIMEOFDAY_NO_TZ
gettimeofday(&tv);
#else
gettimeofday(&tv, (struct timezone *)NULL);
#endif
ret = tv.tv_sec;
ret = ret * 1000000 + tv.tv_usec;
return ret;
}
static double
hpTimerUnit(void)
{
return 0.000001;
}
#endif /* MS_WINDOWS */
/************************************************************/
/* Written by Brett Rosen and Ted Czotter */
struct _ProfilerEntry;
/* represents a function called from another function */
typedef struct _ProfilerSubEntry {
rotating_node_t header;
PY_LONG_LONG tt;
PY_LONG_LONG 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 */
PY_LONG_LONG tt; /* total time in this entry */
PY_LONG_LONG 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 {
PY_LONG_LONG t0;
PY_LONG_LONG 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;
} ProfilerObject;
#define POF_ENABLED 0x001
#define POF_SUBCALLS 0x002
#define POF_BUILTINS 0x004
#define POF_NOMEMORY 0x100
static PyTypeObject PyProfiler_Type;
#define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type)
#define PyProfiler_CheckExact(op) (Py_Type(op) == &PyProfiler_Type)
/*** External Timers ***/
#define DOUBLE_TIMER_PRECISION 4294967296.0
static PyObject *empty_tuple;
static PY_LONG_LONG CallExternalTimer(ProfilerObject *pObj)
{
PY_LONG_LONG result;
PyObject *o = PyObject_Call(pObj->externalTimer, empty_tuple, NULL);
if (o == NULL) {
PyErr_WriteUnraisable(pObj->externalTimer);
return 0;
}
if (pObj->externalTimerUnit > 0.0) {
/* interpret the result as an integer that will be scaled
in profiler_getstats() */
result = PyLong_AsLongLong(o);
}
else {
/* interpret the result as a double measured in seconds.
As the profiler works with PY_LONG_LONG internally
we convert it to a large integer */
double val = PyFloat_AsDouble(o);
/* error handling delayed to the code below */
result = (PY_LONG_LONG) (val * DOUBLE_TIMER_PRECISION);
}
Py_DECREF(o);
if (PyErr_Occurred()) {
PyErr_WriteUnraisable((PyObject *) pObj);
return 0;
}
return result;
}
#define CALL_TIMER(pObj) ((pObj)->externalTimer ? \
CallExternalTimer(pObj) : \
hpTimer())
/*** ProfilerObject ***/
static PyObject *
normalizeUserObj(PyObject *obj)
{
PyCFunctionObject *fn;
if (!PyCFunction_Check(obj)) {
Py_INCREF(obj);
return 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;
const char *modname;
if (mod && PyString_Check(mod)) {
modname = PyString_AS_STRING(mod);
}
else if (mod && PyUnicode_Check(mod)) {
modname = PyUnicode_AsString(mod);
}
else if (mod && PyModule_Check(mod)) {
modname = PyModule_GetName(mod);
if (modname == NULL) {
PyErr_Clear();
modname = "__builtin__";
}
}
else {
modname = "__builtin__";
}
if (strcmp(modname, "__builtin__") != 0)
return PyUnicode_FromFormat("<%s.%s>",
modname,
fn->m_ml->ml_name);
else
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);
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;
}
}
PyErr_Clear();
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*) malloc(sizeof(ProfilerEntry));
if (self == NULL) {
pObj->flags |= POF_NOMEMORY;
return NULL;
}
userObj = normalizeUserObj(userObj);
if (userObj == NULL) {
PyErr_Clear();
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*) 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;
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);
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 free list of ProfilerContexts */
while (pObj->freelistProfilerContext) {
ProfilerContext *c = pObj->freelistProfilerContext;
pObj->freelistProfilerContext = c->previous;
free(c);
}
}
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)
{
PY_LONG_LONG tt = CALL_TIMER(pObj) - self->t0;
PY_LONG_LONG 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 *last_type, *last_value, *last_tb;
PyErr_Fetch(&last_type, &last_value, &last_tb);
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*)
malloc(sizeof(ProfilerContext));
if (pContext == NULL) {
pObj->flags |= POF_NOMEMORY;
goto restorePyerr;
}
}
initContext(pObj, pContext, profEntry);
restorePyerr:
PyErr_Restore(last_type, last_value, last_tb);
}
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
profiler_callback(PyObject *self, PyFrameObject *frame, int what,
PyObject *arg)
{
switch (what) {
/* the 'frame' of a called function is about to start its execution */
case PyTrace_CALL:
ptrace_enter_call(self, (void *)frame->f_code,
(PyObject *)frame->f_code);
break;
/* the 'frame' of a called function is about to finish
(either normally or with an exception) */
case PyTrace_RETURN:
ptrace_leave_call(self, (void *)frame->f_code);
break;
/* case PyTrace_EXCEPTION:
If the exception results in the function exiting, a
PyTrace_RETURN event will be generated, so we don't need to
handle it. */
#ifdef PyTrace_C_CALL /* not defined in Python <= 2.3 */
/* the Python function 'frame' is issuing a call to the built-in
function 'arg' */
case PyTrace_C_CALL:
if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
&& PyCFunction_Check(arg)) {
ptrace_enter_call(self,
((PyCFunctionObject *)arg)->m_ml,
arg);
}
break;
/* the call to the built-in function 'arg' is returning into its
caller 'frame' */
case PyTrace_C_RETURN: /* ...normally */
case PyTrace_C_EXCEPTION: /* ...with an exception set */
if ((((ProfilerObject *)self)->flags & POF_BUILTINS)
&& PyCFunction_Check(arg)) {
ptrace_leave_call(self,
((PyCFunctionObject *)arg)->m_ml);
}
break;
#endif
default:
break;
}
return 0;
}
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 = {
"_lsprof.profiler_entry", /* name */
NULL, /* doc */
profiler_entry_fields,
6
};
static PyStructSequence_Desc profiler_subentry_desc = {
"_lsprof.profiler_subentry", /* name */
NULL, /* doc */
profiler_subentry_fields,
5
};
static int initialized;
static PyTypeObject StatsEntryType;
static PyTypeObject StatsSubEntryType;
typedef struct {
PyObject *list;
PyObject *sublist;
double factor;
} 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*) &StatsSubEntryType,
"((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 {
Py_INCREF(Py_None);
collect->sublist = Py_None;
}
info = PyObject_CallFunction((PyObject*) &StatsEntryType,
"((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;
}
PyDoc_STRVAR(getstats_doc, "\
getstats() -> list of profiler_entry objects\n\
\n\
Return all information collected by the profiler.\n\
Each profiler_entry is a tuple-like object with the\n\
following attributes:\n\
\n\
code code object\n\
callcount how many times this was called\n\
reccallcount how many times called recursively\n\
totaltime total time in this entry\n\
inlinetime inline time in this entry (not in subcalls)\n\
calls details of the calls\n\
\n\
The calls attribute is either None or a list of\n\
profiler_subentry objects:\n\
\n\
code called code object\n\
callcount how many times this is called\n\
reccallcount how many times this is called recursively\n\
totaltime total time spent in this call\n\
inlinetime inline time (not in further subcalls)\n\
");
static PyObject*
profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
{
statscollector_t collect;
if (pending_exception(pObj))
return NULL;
if (!pObj->externalTimer)
collect.factor = hpTimerUnit();
else if (pObj->externalTimerUnit > 0.0)
collect.factor = pObj->externalTimerUnit;
else
collect.factor = 1.0 / DOUBLE_TIMER_PRECISION;
collect.list = PyList_New(0);
if (collect.list == NULL)
return NULL;
if (RotatingTree_Enum(pObj->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) {
#ifndef PyTrace_C_CALL
PyErr_SetString(PyExc_ValueError,
"builtins=True requires Python >= 2.4");
return -1;
#else
pObj->flags |= POF_BUILTINS;
#endif
}
return 0;
}
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};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
kwlist, &subcalls, &builtins))
return NULL;
if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0)
return NULL;
PyEval_SetProfile(profiler_callback, (PyObject*)self);
self->flags |= POF_ENABLED;
Py_INCREF(Py_None);
return Py_None;
}
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)
free(pContext);
}
}
PyDoc_STRVAR(disable_doc, "\
disable()\n\
\n\
Stop collecting profiling information.\n\
");
static PyObject*
profiler_disable(ProfilerObject *self, PyObject* noarg)
{
self->flags &= ~POF_ENABLED;
PyEval_SetProfile(NULL, NULL);
flush_unmatched(self);
if (pending_exception(self))
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(clear_doc, "\
clear()\n\
\n\
Clear all profiling information collected so far.\n\
");
static PyObject*
profiler_clear(ProfilerObject *pObj, PyObject* noarg)
{
clearEntries(pObj);
Py_INCREF(Py_None);
return Py_None;
}
static void
profiler_dealloc(ProfilerObject *op)
{
if (op->flags & POF_ENABLED)
PyEval_SetProfile(NULL, NULL);
flush_unmatched(op);
clearEntries(op);
Py_XDECREF(op->externalTimer);
Py_Type(op)->tp_free(op);
}
static int
profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
{
PyObject *o;
PyObject *timer = NULL;
double timeunit = 0.0;
int subcalls = 1;
#ifdef PyTrace_C_CALL
int builtins = 1;
#else
int builtins = 0;
#endif
static char *kwlist[] = {"timer", "timeunit",
"subcalls", "builtins", 0};
if (!PyArg_ParseTupleAndKeywords(args, kw, "|Odii:Profiler", kwlist,
&timer, &timeunit,
&subcalls, &builtins))
return -1;
if (setSubcalls(pObj, subcalls) < 0 || setBuiltins(pObj, builtins) < 0)
return -1;
o = pObj->externalTimer;
pObj->externalTimer = timer;
Py_XINCREF(timer);
Py_XDECREF(o);
pObj->externalTimerUnit = timeunit;
return 0;
}
static PyMethodDef profiler_methods[] = {
{"getstats", (PyCFunction)profiler_getstats,
METH_NOARGS, getstats_doc},
{"enable", (PyCFunction)profiler_enable,
METH_VARARGS | METH_KEYWORDS, enable_doc},
{"disable", (PyCFunction)profiler_disable,
METH_NOARGS, disable_doc},
{"clear", (PyCFunction)profiler_clear,
METH_NOARGS, clear_doc},
{NULL, NULL}
};
PyDoc_STRVAR(profiler_doc, "\
Profiler(custom_timer=None, time_unit=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, time_unit can\n\
be a float specifying a scale (i.e. how long each integer unit\n\
is, in seconds).\n\
");
static PyTypeObject PyProfiler_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_lsprof.Profiler", /* tp_name */
sizeof(ProfilerObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)profiler_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
profiler_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
profiler_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)profiler_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
PyObject_Del, /* tp_free */
};
static PyMethodDef moduleMethods[] = {
{NULL, NULL}
};
PyMODINIT_FUNC
init_lsprof(void)
{
PyObject *module, *d;
module = Py_InitModule3("_lsprof", moduleMethods, "Fast profiler");
if (module == NULL)
return;
d = PyModule_GetDict(module);
if (PyType_Ready(&PyProfiler_Type) < 0)
return;
PyDict_SetItemString(d, "Profiler", (PyObject *)&PyProfiler_Type);
if (!initialized) {
PyStructSequence_InitType(&StatsEntryType,
&profiler_entry_desc);
PyStructSequence_InitType(&StatsSubEntryType,
&profiler_subentry_desc);
}
Py_INCREF((PyObject*) &StatsEntryType);
Py_INCREF((PyObject*) &StatsSubEntryType);
PyModule_AddObject(module, "profiler_entry",
(PyObject*) &StatsEntryType);
PyModule_AddObject(module, "profiler_subentry",
(PyObject*) &StatsSubEntryType);
empty_tuple = PyTuple_New(0);
initialized = 1;
}