[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