]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Revise the interface to the profiling and tracing support for the
authorFred Drake <fdrake@acm.org>
Wed, 27 Jun 2001 19:19:46 +0000 (19:19 +0000)
committerFred Drake <fdrake@acm.org>
Wed, 27 Jun 2001 19:19:46 +0000 (19:19 +0000)
Python interpreter.

This change adds two new C-level APIs:  PyEval_SetProfile() and
PyEval_SetTrace().  These can be used to install profile and trace
functions implemented in C, which can operate at much higher speeds
than Python-based functions.  The overhead for calling a C-based
profile function is a very small fraction of a percent of the overhead
involved in calling a Python-based function.

The machinery required to call a Python-based profile or trace
function been moved to sysmodule.c, where sys.setprofile() and
sys.setprofile() simply become users of the new interface.

As a side effect, SF bug #436058 is fixed; there is no longer a
_PyTrace_Init() function to declare.

Python/ceval.c
Python/pystate.c
Python/sysmodule.c

index a73e4d06a72446bd265c66ffaeb8d1a9872f145f..702380d75338f6a5e87ea2501ff7ddd568211045 100644 (file)
@@ -2,7 +2,7 @@
 /* Execute compiled code */
 
 /* XXX TO DO:
-   XXX how to pass arguments to call_trace?
+   XXX how to pass arguments to profile and trace functions?
    XXX speed up searching for keywords by using a dictionary
    XXX document it!
    */
@@ -61,9 +61,9 @@ static PyObject *load_args(PyObject ***, int);
 #ifdef LLTRACE
 static int prtrace(PyObject *, char *);
 #endif
-static void call_exc_trace(PyObject **, PyObject**, PyFrameObject *);
-static int call_trace(PyObject **, PyObject **,
-                     PyFrameObject *, PyObject *, PyObject *);
+static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
+                     int, PyObject *);
+static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *);
 static PyObject *loop_subscript(PyObject *, PyObject *);
 static PyObject *apply_slice(PyObject *, PyObject *, PyObject *);
 static int assign_slice(PyObject *, PyObject *,
@@ -98,14 +98,6 @@ static long dxp[256];
 #endif
 #endif
 
-/* Cached interned string objects used for calling the profile and
- * trace functions.
- */
-static PyObject *str_call = NULL;
-static PyObject *str_exception = NULL;
-static PyObject *str_line = NULL;
-static PyObject *str_return = NULL;
-
 
 staticforward PyTypeObject gentype;
 
@@ -1892,12 +1884,15 @@ eval_frame(PyFrameObject *f)
                                printf("--- %s:%d \n", filename, oparg);
 #endif
                        f->f_lineno = oparg;
-                       if (f->f_trace == NULL)
+                       if (tstate->c_tracefunc == NULL || tstate->tracing)
                                continue;
                        /* Trace each line of code reached */
                        f->f_lasti = INSTR_OFFSET();
-                       err = call_trace(&f->f_trace, &f->f_trace,
-                                        f, str_line, Py_None);
+                       /* Inline call_trace() for performance: */
+                       tstate->tracing++;
+                       err = (tstate->c_tracefunc)(tstate->c_traceobj, f,
+                                                   PyTrace_LINE, Py_None);
+                       tstate->tracing--;
                        break;
 
                case CALL_FUNCTION:
@@ -2147,11 +2142,12 @@ eval_frame(PyFrameObject *f)
                                f->f_lasti -= 2;
                        PyTraceBack_Here(f);
 
-                       if (f->f_trace)
-                               call_exc_trace(&f->f_trace, &f->f_trace, f);
-                       if (tstate->sys_profilefunc)
-                               call_exc_trace(&tstate->sys_profilefunc,
-                                              (PyObject**)0, f);
+                       if (tstate->c_tracefunc)
+                               call_exc_trace(tstate->c_tracefunc,
+                                              tstate->c_traceobj, f);
+                       if (tstate->c_profilefunc)
+                               call_exc_trace(tstate->c_profilefunc,
+                                              tstate->c_profileobj, f);
                }
 
                /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */
@@ -2232,10 +2228,10 @@ eval_frame(PyFrameObject *f)
        if (why != WHY_RETURN && why != WHY_YIELD)
                retval = NULL;
 
-       if (f->f_trace) {
+       if (tstate->c_tracefunc && !tstate->tracing) {
                if (why == WHY_RETURN || why == WHY_YIELD) {
-                       if (call_trace(&f->f_trace, &f->f_trace, f,
-                                      str_return, retval)) {
+                       if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
+                                      f, PyTrace_RETURN, retval)) {
                                Py_XDECREF(retval);
                                retval = NULL;
                                why = WHY_EXCEPTION;
@@ -2243,10 +2239,10 @@ eval_frame(PyFrameObject *f)
                }
        }
 
-       if (tstate->sys_profilefunc &&
-                       (why == WHY_RETURN || why == WHY_YIELD)) {
-               if (call_trace(&tstate->sys_profilefunc, (PyObject**)0,
-                              f, str_return, retval)) {
+       if (tstate->c_profilefunc && !tstate->tracing
+           && (why == WHY_RETURN || why == WHY_YIELD)) {
+               if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
+                              f, PyTrace_RETURN, retval)) {
                        Py_XDECREF(retval);
                        retval = NULL;
                        why = WHY_EXCEPTION;
@@ -2475,7 +2471,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
                }
        }
 
-       if (tstate->sys_tracefunc != NULL) {
+       if (tstate->c_tracefunc != NULL && !tstate->tracing) {
                /* tstate->sys_tracefunc, if defined, is a function that
                   will be called  on *every* entry to a code block.
                   Its return value, if not None, is a function that
@@ -2488,20 +2484,21 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
                   depends on the situation.  The global trace function
                   (sys.trace) is also called whenever an exception
                   is detected. */
-               if (call_trace(&tstate->sys_tracefunc,
-                              &f->f_trace, f, str_call,
-                              Py_None/*XXX how to compute arguments now?*/)) {
+               if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
+                              f, PyTrace_CALL, Py_None)) {
+                       /* XXX Need way to compute arguments?? */
                        /* Trace function raised an error */
                        goto fail;
                }
        }
 
-       if (tstate->sys_profilefunc != NULL) {
+       if (tstate->c_profilefunc != NULL) {
                /* Similar for sys_profilefunc, except it needn't return
                   itself and isn't called for "line" events */
-               if (call_trace(&tstate->sys_profilefunc,
-                              (PyObject**)0, f, str_call,
-                              Py_None/*XXX*/)) {
+               if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
+                              f, PyTrace_CALL, Py_None)) {
+                       /* XXX Need way to compute arguments?? */
+                       /* Profile function raised an error */
                        goto fail;
                }
        }
@@ -2772,7 +2769,7 @@ prtrace(PyObject *v, char *str)
 #endif
 
 static void
-call_exc_trace(PyObject **p_trace, PyObject **p_newtrace, PyFrameObject *f)
+call_exc_trace(Py_tracefunc func, PyObject *self, PyFrameObject *f)
 {
        PyObject *type, *value, *traceback, *arg;
        int err;
@@ -2786,7 +2783,7 @@ call_exc_trace(PyObject **p_trace, PyObject **p_newtrace, PyFrameObject *f)
                PyErr_Restore(type, value, traceback);
                return;
        }
-       err = call_trace(p_trace, p_newtrace, f, str_exception, arg);
+       err = call_trace(func, self, f, PyTrace_EXCEPTION, arg);
        Py_DECREF(arg);
        if (err == 0)
                PyErr_Restore(type, value, traceback);
@@ -2797,110 +2794,44 @@ call_exc_trace(PyObject **p_trace, PyObject **p_newtrace, PyFrameObject *f)
        }
 }
 
-/* PyObject **p_trace: in/out; may not be NULL;
-                               may not point to NULL variable initially
-   PyObject **p_newtrace: in/out; may be NULL;
-                               may point to NULL variable;
-                               may be same variable as p_newtrace
-   PyObject *msg: in; must not be NULL
-*/
-
 static int
-call_trace(PyObject **p_trace, PyObject **p_newtrace, PyFrameObject *f,
-          PyObject *msg, PyObject *arg)
-{
-       PyThreadState *tstate = f->f_tstate;
-       PyObject *args;
-       PyObject *res = NULL;
-
-       if (tstate->tracing) {
-               /* Don't do recursive traces */
-               if (p_newtrace) {
-                       Py_XDECREF(*p_newtrace);
-                       *p_newtrace = NULL;
-               }
+call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
+          int what, PyObject *arg)
+{
+       register PyThreadState *tstate = frame->f_tstate;
+       int result;
+       if (tstate->tracing)
                return 0;
-       }
-
-       args = PyTuple_New(3);
-       if (args == NULL)
-               goto cleanup;
-       Py_INCREF(msg);
-       Py_INCREF(f);
-       PyTuple_SET_ITEM(args, 0, (PyObject *)f);
-       PyTuple_SET_ITEM(args, 1, msg);
-       if (arg == NULL)
-               arg = Py_None;
-       Py_INCREF(arg);
-       PyTuple_SET_ITEM(args, 2, arg);
        tstate->tracing++;
-       PyFrame_FastToLocals(f);
-       res = PyEval_CallObject(*p_trace, args); /* May clear *p_trace! */
-       PyFrame_LocalsToFast(f, 1);
+       result = func(obj, frame, what, arg);
        tstate->tracing--;
- cleanup:
-       Py_XDECREF(args);
-       if (res == NULL) {
-               /* The trace proc raised an exception */
-               PyTraceBack_Here(f);
-               Py_XDECREF(*p_trace);
-               *p_trace = NULL;
-               if (p_newtrace) {
-                       Py_XDECREF(*p_newtrace);
-                       *p_newtrace = NULL;
-               }
-               /* to be extra double plus sure we don't get recursive
-                * calls inf either tracefunc or profilefunc gets an
-                * exception, zap the global variables.
-                */
-               Py_XDECREF(tstate->sys_tracefunc);
-               tstate->sys_tracefunc = NULL;
-               Py_XDECREF(tstate->sys_profilefunc);
-               tstate->sys_profilefunc = NULL;
-               return -1;
-       }
-       else {
-               if (p_newtrace) {
-                       Py_XDECREF(*p_newtrace);
-                       if (res == Py_None)
-                               *p_newtrace = NULL;
-                       else {
-                               Py_INCREF(res);
-                               *p_newtrace = res;
-                       }
-               }
-               Py_DECREF(res);
-               return 0;
-       }
+       return result;
 }
 
-/* Initialize the strings that get passed to the profile and trace functions;
- * this avoids doing this while we're actually profiling/tracing.
- */
-int
-_PyTrace_Init(void)
+void
+PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
 {
-       if (str_call == NULL) {
-               str_call = PyString_InternFromString("call");
-               if (str_call == NULL)
-                       return -1;
-       }
-       if (str_exception == NULL) {
-               str_exception = PyString_InternFromString("exception");
-               if (str_exception == NULL)
-                       return -1;
-       }
-       if (str_line == NULL) {
-               str_line = PyString_InternFromString("line");
-               if (str_line == NULL)
-                       return -1;
-       }
-       if (str_return == NULL) {
-               str_return = PyString_InternFromString("return");
-               if (str_return == NULL)
-                       return -1;
-       }
-       return 0;
+       PyThreadState *tstate = PyThreadState_Get();
+       PyObject *temp = tstate->c_profileobj;
+       Py_XINCREF(arg);
+       tstate->c_profilefunc = NULL;
+       tstate->c_profileobj = NULL;
+       Py_XDECREF(temp);
+       tstate->c_profilefunc = func;
+       tstate->c_profileobj = arg;
+}
+
+void
+PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
+{
+       PyThreadState *tstate = PyThreadState_Get();
+       PyObject *temp = tstate->c_traceobj;
+       Py_XINCREF(arg);
+       tstate->c_tracefunc = NULL;
+       tstate->c_traceobj = NULL;
+       Py_XDECREF(temp);
+       tstate->c_tracefunc = func;
+       tstate->c_traceobj = arg;
 }
 
 PyObject *
index 8e5896a44fd1ea27f207557ae0aa2d508a5e5415..192c4c14f6263698f09cc255758e7830bd52dda2 100644 (file)
@@ -120,8 +120,10 @@ PyThreadState_New(PyInterpreterState *interp)
                tstate->exc_value = NULL;
                tstate->exc_traceback = NULL;
 
-               tstate->sys_profilefunc = NULL;
-               tstate->sys_tracefunc = NULL;
+               tstate->c_profilefunc = NULL;
+               tstate->c_tracefunc = NULL;
+               tstate->c_profileobj = NULL;
+               tstate->c_traceobj = NULL;
 
                HEAD_LOCK();
                tstate->next = interp->tstate_head;
@@ -152,8 +154,10 @@ PyThreadState_Clear(PyThreadState *tstate)
        ZAP(tstate->exc_value);
        ZAP(tstate->exc_traceback);
 
-       ZAP(tstate->sys_profilefunc);
-       ZAP(tstate->sys_tracefunc);
+       tstate->c_profilefunc = NULL;
+       tstate->c_tracefunc = NULL;
+       ZAP(tstate->c_profileobj);
+       ZAP(tstate->c_traceobj);
 }
 
 
index fe880d5443ca4b04dc9d83252f7a20d6f4894039..76d40bfd0badcd293f919960996014babb986a5a 100644 (file)
@@ -196,20 +196,120 @@ static char setdefaultencoding_doc[] =
 \n\
 Set the current default string encoding used by the Unicode implementation.";
 
-extern int _PyTrace_Init(void);
+/*
+ * Cached interned string objects used for calling the profile and
+ * trace functions.  Initialized by trace_init().
+ */
+static PyObject *whatstrings[4] = {NULL, NULL, NULL, NULL};
+
+static int
+trace_init(void)
+{
+       static char *whatnames[4] = {"call", "exception", "line", "return"};
+       PyObject *name;
+       int i;
+       for (i = 0; i < 4; ++i) {
+               if (whatstrings[i] == NULL) {
+                       name = PyString_InternFromString(whatnames[i]);
+                       if (name == NULL)
+                               return -1;
+                       whatstrings[i] = name;
+                }
+       }
+       return 0;
+}
+
+
+static PyObject *
+call_trampoline(PyThreadState *tstate, PyObject* callback,
+               PyFrameObject *frame, int what, PyObject *arg)
+{
+       PyObject *args = PyTuple_New(3);
+       PyObject *whatstr;
+       PyObject *result;
+
+       if (args == NULL)
+               return NULL;
+       Py_INCREF(frame);
+       whatstr = whatstrings[what];
+       Py_INCREF(whatstr);
+       if (arg == NULL)
+               arg = Py_None;
+       Py_INCREF(arg);
+       PyTuple_SET_ITEM(args, 0, (PyObject *)frame);
+       PyTuple_SET_ITEM(args, 1, whatstr);
+       PyTuple_SET_ITEM(args, 2, arg);
+
+       /* call the Python-level function */
+       PyFrame_FastToLocals(frame);
+       result = PyEval_CallObject(callback, args);
+       PyFrame_LocalsToFast(frame, 1);
+       if (result == NULL)
+               PyTraceBack_Here(frame);
+
+       /* cleanup */
+       Py_DECREF(args);
+       return result;
+}
+
+static int
+profile_trampoline(PyObject *self, PyFrameObject *frame,
+                  int what, PyObject *arg)
+{
+       PyThreadState *tstate = frame->f_tstate;
+       PyObject *result;
+
+       result = call_trampoline(tstate, self, frame, what, arg);
+       if (result == NULL) {
+               PyEval_SetProfile(NULL, NULL);
+               return -1;
+       }
+       Py_DECREF(result);
+       return 0;
+}
+
+static int
+trace_trampoline(PyObject *self, PyFrameObject *frame,
+                int what, PyObject *arg)
+{
+       PyThreadState *tstate = frame->f_tstate;
+       PyObject *callback;
+       PyObject *result;
+
+       if (what == PyTrace_CALL)
+               callback = self;
+       else
+               callback = frame->f_trace;
+       if (callback == NULL)
+               return 0;
+       result = call_trampoline(tstate, callback, frame, what, arg);
+       if (result == NULL) {
+               PyEval_SetTrace(NULL, NULL);
+               Py_XDECREF(frame->f_trace);
+               frame->f_trace = NULL;
+               return -1;
+       }
+       if (result != Py_None) {
+               PyObject *temp = frame->f_trace;
+               frame->f_trace = NULL;
+               Py_XDECREF(temp);
+               frame->f_trace = result;
+       }
+       else {
+               Py_DECREF(result);
+       }
+       return 0;
+}
 
 static PyObject *
 sys_settrace(PyObject *self, PyObject *args)
 {
-       PyThreadState *tstate = PyThreadState_Get();
-       if (_PyTrace_Init() == -1)
+       if (trace_init() == -1)
                return NULL;
        if (args == Py_None)
-               args = NULL;
+               PyEval_SetTrace(NULL, NULL);
        else
-               Py_XINCREF(args);
-       Py_XDECREF(tstate->sys_tracefunc);
-       tstate->sys_tracefunc = args;
+               PyEval_SetTrace(trace_trampoline, args);
        Py_INCREF(Py_None);
        return Py_None;
 }
@@ -223,15 +323,12 @@ function call.  See the debugger chapter in the library manual.";
 static PyObject *
 sys_setprofile(PyObject *self, PyObject *args)
 {
-       PyThreadState *tstate = PyThreadState_Get();
-       if (_PyTrace_Init() == -1)
+       if (trace_init() == -1)
                return NULL;
        if (args == Py_None)
-               args = NULL;
+               PyEval_SetProfile(NULL, NULL);
        else
-               Py_XINCREF(args);
-       Py_XDECREF(tstate->sys_profilefunc);
-       tstate->sys_profilefunc = args;
+               PyEval_SetProfile(profile_trampoline, args);
        Py_INCREF(Py_None);
        return Py_None;
 }