[Stackless] Fwd: [Stackless-checkins] r84071 - stackless/trunk/Modules/_lsprof.c
Richard Tew
richard.m.tew at gmail.com
Mon Aug 16 02:24:27 CEST 2010
Isn't the trunk supposed to be frozen now? :) To mirror what happens
with the mainline?
---------- Forwarded message ----------
From: kristjan.jonsson <python-checkins at python.org>
Date: Mon, Aug 16, 2010 at 1:23 AM
Subject: [Stackless-checkins] r84071 - stackless/trunk/Modules/_lsprof.c
To: stackless-checkins at stackless.com
Author: kristjan.jonsson
Date: Sun Aug 15 19:23:00 2010
New Revision: 84071
Log:
the _lsprof.c module is now stackless aware. This allows the same
cProfile.Profile() instance to be used by multiple tasklets, for whole
program profiling.
A patch with this new _lsprof.c has also been submitted to bug.python.org.
What remains now is the ability to set tracing/profiling for all
tasklets globally.
Modified:
stackless/trunk/Modules/_lsprof.c
Modified: stackless/trunk/Modules/_lsprof.c
==============================================================================
--- stackless/trunk/Modules/_lsprof.c (original)
+++ stackless/trunk/Modules/_lsprof.c Sun Aug 15 19:23:00 2010
@@ -3,6 +3,9 @@
#include "frameobject.h"
#include "structseq.h"
#include "rotatingtree.h"
+#ifdef STACKLESS
+#include "stackless_api.h"
+#endif
#if !defined(HAVE_LONG_LONG)
#error "This module requires long longs!"
@@ -98,20 +101,35 @@
typedef struct _ProfilerContext {
PY_LONG_LONG t0;
PY_LONG_LONG subt;
+ PY_LONG_LONG paused;
struct _ProfilerContext *previous;
ProfilerEntry *ctxEntry;
+ char is_recursion;
+ char is_subcall_recursion;
} ProfilerContext;
+typedef struct _ProfilerStack {
+ rotating_node_t header;
+ ProfilerContext *currentProfilerContext;
+ PY_LONG_LONG t0; /* When did stack become non-current? */
+} ProfilerStack;
+
typedef struct {
PyObject_HEAD
+ rotating_node_t *profilerStacks;
rotating_node_t *profilerEntries;
- ProfilerContext *currentProfilerContext;
+ ProfilerStack *currentProfilerStack;
ProfilerContext *freelistProfilerContext;
- int flags;
PyObject *externalTimer;
double externalTimerUnit;
+ PY_LONG_LONG currentTime;
+ int flags;
+ int nProfilerStacks;
} ProfilerObject;
+#define CURRENT_CONTEXT(pObj) \
+ ((pObj)->currentProfilerStack->currentProfilerContext)
+
#define POF_ENABLED 0x001
#define POF_SUBCALLS 0x002
#define POF_BUILTINS 0x004
@@ -303,26 +321,61 @@
{
RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
pObj->profilerEntries = EMPTY_ROTATING_TREE;
- /* release the memory hold by the ProfilerContexts */
- if (pObj->currentProfilerContext) {
- free(pObj->currentProfilerContext);
- pObj->currentProfilerContext = NULL;
- }
+ /* release the memory hold by the free list of ProfilerContexts */
while (pObj->freelistProfilerContext) {
ProfilerContext *c = pObj->freelistProfilerContext;
pObj->freelistProfilerContext = c->previous;
free(c);
}
- pObj->freelistProfilerContext = NULL;
+}
+
+static void
+checkRecursion(ProfilerContext *self, int nStacks, long
recursionLevel, long subcallRecursionLevel)
+{
+ ProfilerEntry *entry = self->ctxEntry;
+ ProfilerContext *p;
+ self->is_recursion = self->is_subcall_recursion = 0;
+ if (subcallRecursionLevel > 1) {
+ ProfilerEntry *previous_entry;
+ if (nStacks <= 1) {
+ self->is_recursion = self->is_subcall_recursion = 1;
+ return;
+ }
+ /* possible subcall recursion (and hence recursion) on this stack. */
+ previous_entry = self->previous->ctxEntry;
+ for(p = self->previous; p; p = p->previous) {
+ if (p->ctxEntry == entry) {
+ self->is_recursion = 1;
+ if (p->previous && p->previous->ctxEntry == previous_entry) {
+ self->is_subcall_recursion = 1;
+ return;
+ }
+ }
+ }
+ }
+ else if (recursionLevel > 1) {
+ if (nStacks <= 1) {
+ self->is_recursion = 1;
+ return;
+ }
+ /* possible regular recurion on this stack. Check it. */
+ for(p = self->previous; p; p = p->previous) {
+ if (p->ctxEntry == entry) {
+ self->is_recursion = 1;
+ return;
+ }
+ }
+ }
}
static void
initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
{
+ long subcallRecursionLevel = 0;
self->ctxEntry = entry;
- self->subt = 0;
- self->previous = pObj->currentProfilerContext;
- pObj->currentProfilerContext = self;
+ self->subt = self->paused = 0;
+ self->previous = CURRENT_CONTEXT(pObj);
+ CURRENT_CONTEXT(pObj) = self;
++entry->recursionLevel;
if ((pObj->flags & POF_SUBCALLS) && self->previous) {
/* find or create an entry for me in my caller's entry */
@@ -331,20 +384,29 @@
if (subentry == NULL)
subentry = newSubEntry(pObj, caller, entry);
if (subentry)
- ++subentry->recursionLevel;
+ subcallRecursionLevel = ++subentry->recursionLevel;
}
- self->t0 = CALL_TIMER(pObj);
+ checkRecursion(self, pObj->nProfilerStacks, entry->recursionLevel,
+ subcallRecursionLevel);
+ self->t0 = pObj->currentTime;
}
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)
+ PY_LONG_LONG tt, it;
+
+ tt = pObj->currentTime - self->t0;
+ tt -= self->paused;
+ it = tt - self->subt;
+
+ if (self->previous) {
self->previous->subt += tt;
- pObj->currentProfilerContext = self->previous;
- if (--entry->recursionLevel == 0)
+ self->previous->paused += self->paused;
+ }
+ CURRENT_CONTEXT(pObj) = self->previous;
+ --entry->recursionLevel;
+ if (!self->is_recursion)
entry->tt += tt;
else
++entry->recursivecallcount;
@@ -355,7 +417,8 @@
ProfilerEntry *caller = self->previous->ctxEntry;
ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
if (subentry) {
- if (--subentry->recursionLevel == 0)
+ --subentry->recursionLevel;
+ if (!self->is_subcall_recursion)
subentry->tt += tt;
else
++subentry->recursivecallcount;
@@ -417,7 +480,7 @@
ProfilerEntry *profEntry;
ProfilerContext *pContext;
- pContext = pObj->currentProfilerContext;
+ pContext = CURRENT_CONTEXT(pObj);
if (pContext == NULL)
return;
profEntry = getEntry(pObj, key);
@@ -425,17 +488,72 @@
Stop(pObj, pContext, profEntry);
}
else {
- pObj->currentProfilerContext = pContext->previous;
+ CURRENT_CONTEXT(pObj) = pContext->previous;
}
/* put pContext into the free list */
pContext->previous = pObj->freelistProfilerContext;
pObj->freelistProfilerContext = pContext;
}
+static void
+SelectStackByKey(ProfilerObject *pObj, void *key)
+{
+ ProfilerStack *old = pObj->currentProfilerStack;
+ ProfilerStack *stack;
+
+ if (old) {
+ if (old->header.key == key)
+ return;
+ old->t0 = pObj->currentTime;
+ }
+ stack = (ProfilerStack*)RotatingTree_Get(&pObj->profilerStacks, key);
+ if (stack) {
+ if (stack->currentProfilerContext)
+ stack->currentProfilerContext->paused +=
pObj->currentTime - stack->t0;
+ }
+ else {
+ stack = (ProfilerStack*) malloc(sizeof(ProfilerStack));
+ if (stack != NULL) {
+ stack->currentProfilerContext = NULL;
+ stack->header.key = key;
+ RotatingTree_Add(&pObj->profilerStacks, (rotating_node_t*)stack);
+ ++pObj->nProfilerStacks;
+ }
+ }
+ pObj->currentProfilerStack = stack;
+}
+
+static void
+SelectStack(ProfilerObject *pObj)
+{
+#ifdef STACKLESS
+ SelectStackByKey(pObj, PyStackless_GetCurrent());
+#else
+ PyThreadState *tstate = PyThreadState_GET();
+ SelectStackByKey(pObj, tstate);
+#endif
+}
+
static int
profiler_callback(PyObject *self, PyFrameObject *frame, int what,
PyObject *arg)
{
+ ProfilerObject *pObj = (ProfilerObject*)self;
+ {
+ /* keep error state, see ptrace_enter_call above.
+ * We could keep this more focused, only really needed
+ * when calling a user time function, and initializing
+ * a user object
+ */
+ PyObject *et, *ev, *tb;
+ PyErr_Fetch(&et, &ev, &tb);
+ pObj->currentTime = CALL_TIMER(pObj);
+ SelectStack(pObj);
+ PyErr_Restore(et, ev, tb);
+ }
+ if (pObj->currentProfilerStack == NULL)
+ return 0;
+
switch (what) {
/* the 'frame' of a called function is about to start its execution */
@@ -708,19 +826,41 @@
static void
flush_unmatched(ProfilerObject *pObj)
{
- while (pObj->currentProfilerContext) {
- ProfilerContext *pContext = pObj->currentProfilerContext;
+ if (!pObj->currentProfilerStack)
+ return;
+ while (CURRENT_CONTEXT(pObj)) {
+ ProfilerContext *pContext = CURRENT_CONTEXT(pObj);
ProfilerEntry *profEntry= pContext->ctxEntry;
if (profEntry)
Stop(pObj, pContext, profEntry);
else
- pObj->currentProfilerContext = pContext->previous;
+ CURRENT_CONTEXT(pObj) = pContext->previous;
if (pContext)
free(pContext);
}
}
+static int
+flush_unmatched_enum(rotating_node_t *n, void *arg)
+{
+ ProfilerStack *t = (ProfilerStack*)n;
+ ProfilerObject *pObj = (ProfilerObject*)arg;
+ pObj->currentProfilerStack = t;
+ flush_unmatched(pObj);
+ free(t);
+ return 0;
+}
+
+static void
+flush_unmatched_allstacks(ProfilerObject *pObj)
+{
+ RotatingTree_Enum(pObj->profilerStacks, &flush_unmatched_enum, pObj);
+ pObj->profilerStacks = EMPTY_ROTATING_TREE;
+ pObj->currentProfilerStack = NULL;
+ pObj->nProfilerStacks = 0;
+}
+
PyDoc_STRVAR(disable_doc, "\
disable()\n\
\n\
@@ -732,6 +872,7 @@
{
self->flags &= ~POF_ENABLED;
PyEval_SetProfile(NULL, NULL);
+ SelectStack(self);
flush_unmatched(self);
if (pending_exception(self))
return NULL;
@@ -748,6 +889,7 @@
static PyObject*
profiler_clear(ProfilerObject *pObj, PyObject* noarg)
{
+ flush_unmatched_allstacks(pObj);
clearEntries(pObj);
Py_INCREF(Py_None);
return Py_None;
@@ -758,7 +900,7 @@
{
if (op->flags & POF_ENABLED)
PyEval_SetProfile(NULL, NULL);
- flush_unmatched(op);
+ flush_unmatched_allstacks(op);
clearEntries(op);
Py_XDECREF(op->externalTimer);
Py_TYPE(op)->tp_free(op);
@@ -889,4 +1031,4 @@
(PyObject*) &StatsSubEntryType);
empty_tuple = PyTuple_New(0);
initialized = 1;
-}
+}
\ No newline at end of file
_______________________________________________
Stackless-checkins mailing list
Stackless-checkins at stackless.com
http://www.stackless.com/mailman/listinfo/stackless-checkins
More information about the Stackless
mailing list