[Python-checkins] CVS: python/dist/src/Python ceval.c,2.258,2.259 pystate.c,2.16,2.17 sysmodule.c,2.87,2.88

Fred L. Drake fdrake@users.sourceforge.net
Wed, 27 Jun 2001 12:19:48 -0700


Update of /cvsroot/python/python/dist/src/Python
In directory usw-pr-cvs1:/tmp/cvs-serv18718/Python

Modified Files:
	ceval.c pystate.c sysmodule.c 
Log Message:
Revise the interface to the profiling and tracing support for the
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.


Index: ceval.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v
retrieving revision 2.258
retrieving revision 2.259
diff -C2 -r2.258 -r2.259
*** ceval.c	2001/06/26 22:24:51	2.258
--- ceval.c	2001/06/27 19:19:46	2.259
***************
*** 3,7 ****
  
  /* XXX TO DO:
!    XXX how to pass arguments to call_trace?
     XXX speed up searching for keywords by using a dictionary
     XXX document it!
--- 3,7 ----
  
  /* XXX TO DO:
!    XXX how to pass arguments to profile and trace functions?
     XXX speed up searching for keywords by using a dictionary
     XXX document it!
***************
*** 62,68 ****
  static int prtrace(PyObject *, char *);
  #endif
! static void call_exc_trace(PyObject **, PyObject**, PyFrameObject *);
! static int call_trace(PyObject **, PyObject **,
! 		      PyFrameObject *, PyObject *, PyObject *);
  static PyObject *loop_subscript(PyObject *, PyObject *);
  static PyObject *apply_slice(PyObject *, PyObject *, PyObject *);
--- 62,68 ----
  static int prtrace(PyObject *, char *);
  #endif
! 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 *);
***************
*** 99,111 ****
  #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;
  
--- 99,103 ----
***************
*** 1893,1902 ****
  #endif
  			f->f_lineno = oparg;
! 			if (f->f_trace == NULL)
  				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);
  			break;
  
--- 1885,1897 ----
  #endif
  			f->f_lineno = oparg;
! 			if (tstate->c_tracefunc == NULL || tstate->tracing)
  				continue;
  			/* Trace each line of code reached */
  			f->f_lasti = INSTR_OFFSET();
! 			/* Inline call_trace() for performance: */
! 			tstate->tracing++;
! 			err = (tstate->c_tracefunc)(tstate->c_traceobj, f,
! 						    PyTrace_LINE, Py_None);
! 			tstate->tracing--;
  			break;
  
***************
*** 2148,2156 ****
  			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);
  		}
  
--- 2143,2152 ----
  			PyTraceBack_Here(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);
  		}
  
***************
*** 2233,2240 ****
  		retval = NULL;
  
! 	if (f->f_trace) {
  		if (why == WHY_RETURN || why == WHY_YIELD) {
! 			if (call_trace(&f->f_trace, &f->f_trace, f,
! 				       str_return, retval)) {
  				Py_XDECREF(retval);
  				retval = NULL;
--- 2229,2236 ----
  		retval = NULL;
  
! 	if (tstate->c_tracefunc && !tstate->tracing) {
  		if (why == WHY_RETURN || why == WHY_YIELD) {
! 			if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
! 				       f, PyTrace_RETURN, retval)) {
  				Py_XDECREF(retval);
  				retval = NULL;
***************
*** 2244,2251 ****
  	}
  
! 	if (tstate->sys_profilefunc &&
! 			(why == WHY_RETURN || why == WHY_YIELD)) {
! 		if (call_trace(&tstate->sys_profilefunc, (PyObject**)0,
! 			       f, str_return, retval)) {
  			Py_XDECREF(retval);
  			retval = NULL;
--- 2240,2247 ----
  	}
  
! 	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;
***************
*** 2476,2480 ****
  	}
  
! 	if (tstate->sys_tracefunc != NULL) {
  		/* tstate->sys_tracefunc, if defined, is a function that
  		   will be called  on *every* entry to a code block.
--- 2472,2476 ----
  	}
  
! 	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.
***************
*** 2489,2495 ****
  		   (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?*/)) {
  			/* Trace function raised an error */
  			goto fail;
--- 2485,2491 ----
  		   (sys.trace) is also called whenever an exception
  		   is detected. */
! 		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;
***************
*** 2497,2506 ****
  	}
  
! 	if (tstate->sys_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*/)) {
  			goto fail;
  		}
--- 2493,2503 ----
  	}
  
! 	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->c_profilefunc, tstate->c_profileobj,
! 			       f, PyTrace_CALL, Py_None)) {
! 			/* XXX Need way to compute arguments?? */
! 			/* Profile function raised an error */
  			goto fail;
  		}
***************
*** 2773,2777 ****
  
  static void
! call_exc_trace(PyObject **p_trace, PyObject **p_newtrace, PyFrameObject *f)
  {
  	PyObject *type, *value, *traceback, *arg;
--- 2770,2774 ----
  
  static void
! call_exc_trace(Py_tracefunc func, PyObject *self, PyFrameObject *f)
  {
  	PyObject *type, *value, *traceback, *arg;
***************
*** 2787,2791 ****
  		return;
  	}
! 	err = call_trace(p_trace, p_newtrace, f, str_exception, arg);
  	Py_DECREF(arg);
  	if (err == 0)
--- 2784,2788 ----
  		return;
  	}
! 	err = call_trace(func, self, f, PyTrace_EXCEPTION, arg);
  	Py_DECREF(arg);
  	if (err == 0)
***************
*** 2798,2905 ****
  }
  
- /* 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;
! 		}
  		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);
  	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;
! 	}
  }
  
! /* 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)
  {
! 	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;
  }
  
--- 2795,2836 ----
  }
  
  static int
! 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;
  	tstate->tracing++;
! 	result = func(obj, frame, what, arg);
  	tstate->tracing--;
! 	return result;
  }
  
! void
! PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
  {
! 	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;
  }
  

Index: pystate.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/pystate.c,v
retrieving revision 2.16
retrieving revision 2.17
diff -C2 -r2.16 -r2.17
*** pystate.c	2001/01/23 01:46:06	2.16
--- pystate.c	2001/06/27 19:19:46	2.17
***************
*** 121,126 ****
  		tstate->exc_traceback = NULL;
  
! 		tstate->sys_profilefunc = NULL;
! 		tstate->sys_tracefunc = NULL;
  
  		HEAD_LOCK();
--- 121,128 ----
  		tstate->exc_traceback = NULL;
  
! 		tstate->c_profilefunc = NULL;
! 		tstate->c_tracefunc = NULL;
! 		tstate->c_profileobj = NULL;
! 		tstate->c_traceobj = NULL;
  
  		HEAD_LOCK();
***************
*** 153,158 ****
  	ZAP(tstate->exc_traceback);
  
! 	ZAP(tstate->sys_profilefunc);
! 	ZAP(tstate->sys_tracefunc);
  }
  
--- 155,162 ----
  	ZAP(tstate->exc_traceback);
  
! 	tstate->c_profilefunc = NULL;
! 	tstate->c_tracefunc = NULL;
! 	ZAP(tstate->c_profileobj);
! 	ZAP(tstate->c_traceobj);
  }
  

Index: sysmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/sysmodule.c,v
retrieving revision 2.87
retrieving revision 2.88
diff -C2 -r2.87 -r2.88
*** sysmodule.c	2001/06/27 06:28:56	2.87
--- sysmodule.c	2001/06/27 19:19:46	2.88
***************
*** 197,214 ****
  Set the current default string encoding used by the Unicode implementation.";
  
! extern int _PyTrace_Init(void);
  
  static PyObject *
  sys_settrace(PyObject *self, PyObject *args)
  {
! 	PyThreadState *tstate = PyThreadState_Get();
! 	if (_PyTrace_Init() == -1)
  		return NULL;
  	if (args == Py_None)
! 		args = NULL;
  	else
! 		Py_XINCREF(args);
! 	Py_XDECREF(tstate->sys_tracefunc);
! 	tstate->sys_tracefunc = args;
  	Py_INCREF(Py_None);
  	return Py_None;
--- 197,314 ----
  Set the current default string encoding used by the Unicode implementation.";
  
! /*
!  * 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)
  {
! 	if (trace_init() == -1)
  		return NULL;
  	if (args == Py_None)
! 		PyEval_SetTrace(NULL, NULL);
  	else
! 		PyEval_SetTrace(trace_trampoline, args);
  	Py_INCREF(Py_None);
  	return Py_None;
***************
*** 224,236 ****
  sys_setprofile(PyObject *self, PyObject *args)
  {
! 	PyThreadState *tstate = PyThreadState_Get();
! 	if (_PyTrace_Init() == -1)
  		return NULL;
  	if (args == Py_None)
! 		args = NULL;
  	else
! 		Py_XINCREF(args);
! 	Py_XDECREF(tstate->sys_profilefunc);
! 	tstate->sys_profilefunc = args;
  	Py_INCREF(Py_None);
  	return Py_None;
--- 324,333 ----
  sys_setprofile(PyObject *self, PyObject *args)
  {
! 	if (trace_init() == -1)
  		return NULL;
  	if (args == Py_None)
! 		PyEval_SetProfile(NULL, NULL);
  	else
! 		PyEval_SetProfile(profile_trampoline, args);
  	Py_INCREF(Py_None);
  	return Py_None;