User trace feature.
diff --git a/Python/ceval.c b/Python/ceval.c
index 1d0cdb4..129d743 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -36,7 +36,9 @@
 #include "traceback.h"
 
 #ifndef NDEBUG
-#define TRACE
+/* For debugging the interpreter: */
+#define LLTRACE  1	/* Low-level trace feature */
+#define CHECKEXC 1	/* Double-check exception checking */
 #endif
 
 /* Forward declarations */
@@ -69,6 +71,7 @@
 static int assign_subscript PROTO((object *, object *, object *));
 static int assign_slice PROTO((object *, object *, object *, object *));
 static int import_from PROTO((object *, object *, object *));
+static object *call_trace PROTO((object *, frameobject *, char *, object *));
 
 
 static frameobject *current_frame;
@@ -106,12 +109,12 @@
 	register object *u;
 	register object *t;
 	register frameobject *f; /* Current frame */
-	int lineno;		/* Current line number */
+	object *trace;		/* Trace function or NULL */
 	object *retval;		/* Return value iff why == WHY_RETURN */
 	char *name;		/* Name used by some instructions */
 	FILE *fp;		/* Used by print operations */
-#ifdef TRACE
-	int trace = dictlookup(globals, "__trace__") != NULL;
+#ifdef LLTRACE
+	int lltrace = dictlookup(globals, "__lltrace__") != NULL;
 #endif
 
 /* Code access macros */
@@ -134,9 +137,9 @@
 #define BASIC_PUSH(v)	(*stack_pointer++ = (v))
 #define BASIC_POP()	(*--stack_pointer)
 
-#ifdef TRACE
-#define PUSH(v)		(BASIC_PUSH(v), trace && prtrace(TOP(), "push"))
-#define POP()		(trace && prtrace(TOP(), "pop"), BASIC_POP())
+#ifdef LLTRACE
+#define PUSH(v)		(BASIC_PUSH(v), lltrace && prtrace(TOP(), "push"))
+#define POP()		(lltrace && prtrace(TOP(), "pop"), BASIC_POP())
 #else
 #define PUSH(v)		BASIC_PUSH(v)
 #define POP()		BASIC_POP()
@@ -153,9 +156,37 @@
 		return NULL;
 	
 	current_frame = f;
+
+	trace = sysget("trace");
+	if (trace != NULL) {
+		/* sys.trace, 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
+		   will be called at the start of each executed line
+		   of code.  (Actually, the function must return
+		   itself in order to continue tracing.)
+		   The trace functions are called with three arguments:
+		   a pointer to the current frame, a string indicating
+		   why the function is called, and an argument which
+		   depends on the situation.  The global trace function
+		   (sys.trace) is also called whenever an exception
+		   is detected. */
+		trace = call_trace(trace, f, "call", arg);
+		if (trace == NULL) {
+			/* Trace function raised an error */
+			sysset("trace", (object *)NULL);
+			current_frame = f->f_back;
+			DECREF(f);
+			return NULL;
+		}
+		if (trace == None) {
+			/* No need to trace this code block */
+			DECREF(trace);
+			trace = NULL;
+		}
+	}
 	
 	next_instr = GETUSTRINGVALUE(f->f_code->co_code);
-	
 	stack_pointer = f->f_valuestack;
 	
 	if (arg != NULL) {
@@ -166,7 +197,6 @@
 	why = WHY_NOT;
 	err = 0;
 	x = None;	/* Not a reference, just anything non-NULL */
-	lineno = -1;
 	
 	for (;;) {
 		static int ticker;
@@ -178,7 +208,6 @@
 			if (intrcheck()) {
 				err_set(KeyboardInterrupt);
 				why = WHY_EXCEPTION;
-				tb_here(f, INSTR_OFFSET(), lineno);
 				goto on_error;
 			}
 		}
@@ -189,10 +218,10 @@
 		if (HAS_ARG(opcode))
 			oparg = NEXTARG();
 
-#ifdef TRACE
+#ifdef LLTRACE
 		/* Instruction tracing */
 		
-		if (trace) {
+		if (lltrace) {
 			if (HAS_ARG(opcode)) {
 				printf("%d: %d, %d\n",
 					(int) (INSTR_OFFSET() - 3),
@@ -273,6 +302,7 @@
 		
 		case UNARY_CALL:
 			v = POP();
+			f->f_lasti = INSTR_OFFSET() - 1; /* For tracing */
 			x = call_object(v, (object *)NULL);
 			DECREF(v);
 			PUSH(x);
@@ -342,6 +372,7 @@
 		case BINARY_CALL:
 			w = POP();
 			v = POP();
+			f->f_lasti = INSTR_OFFSET() - 1; /* For tracing */
 			x = call_object(v, w);
 			DECREF(v);
 			DECREF(w);
@@ -921,17 +952,31 @@
 			break;
 		
 		case SET_LINENO:
-#ifdef TRACE
-			if (trace)
+#ifdef LLTRACE
+			if (lltrace)
 				printf("--- Line %d ---\n", oparg);
 #endif
-			lineno = oparg;
+			f->f_lineno = oparg;
+			if (trace != NULL) {
+				/* Trace each line of code reached */
+				f->f_lasti = INSTR_OFFSET();
+				x = call_trace(trace, f, "line", None);
+				/* The trace function must return itself
+				   in order to continue tracing */
+				DECREF(trace);
+				if (x == None) {
+					DECREF(x);
+					trace = NULL;
+				}
+				else
+					trace = x;
+			}
 			break;
 		
 		default:
 			fprintf(stderr,
 				"XXX lineno: %d, opcode: %d\n",
-				lineno, opcode);
+				f->f_lineno, opcode);
 			err_setstr(SystemError, "eval_code: unknown opcode");
 			why = WHY_EXCEPTION;
 			break;
@@ -950,7 +995,7 @@
 			err = 0;
 		}
 
-#ifndef NDEBUG
+#ifndef CHECKEXC
 		/* Double-check exception status */
 		
 		if (why == WHY_EXCEPTION || why == WHY_RERAISE) {
@@ -971,10 +1016,49 @@
 		/* Log traceback info if this is a real exception */
 		
 		if (why == WHY_EXCEPTION) {
-			int lasti = INSTR_OFFSET() - 1;
+			f->f_lasti = INSTR_OFFSET() - 1;
 			if (HAS_ARG(opcode))
-				lasti -= 2;
-			tb_here(f, lasti, lineno);
+				f->f_lasti -= 2;
+			tb_here(f);
+
+			if (trace)
+				v = trace;
+			else
+				v = sysget("trace");
+			if (v) {
+				object *type, *value, *traceback, *arg;
+				err_get(&type, &value);
+				traceback = tb_fetch();
+				arg = newtupleobject(3);
+				if (arg == NULL)
+					err_clear();
+				else {
+					settupleitem(arg, 0, type);
+					settupleitem(arg, 1, value);
+					settupleitem(arg, 2, traceback);
+				}
+				v = call_trace(v, f, "exception", arg);
+				if (v == NULL) {
+					/* Trace function raised error */
+					tb_here(f);
+					sysset("trace", (object *)NULL);
+					XDECREF(trace);
+					trace = NULL;
+				}
+				else {
+					/* Restore original exception */
+					err_setval(type, value);
+					tb_store(traceback);
+					if (v == None)
+						DECREF(v);
+					else {
+						/* Set trace function */
+						XDECREF(trace);
+						trace = v;
+					}
+				}
+				XDECREF(arg);
+			}
 		}
 		
 		/* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */
@@ -1012,9 +1096,7 @@
 					   Python main loop.  Don't do
 					   this for 'finally'. */
 					if (b->b_type == SETUP_EXCEPT) {
-#if 1 /* Oops, this breaks too many things */
 						sysset("exc_traceback", v);
-#endif
 						sysset("exc_value", val);
 						sysset("exc_type", exc);
 						err_clear();
@@ -1049,18 +1131,31 @@
 		XDECREF(v);
 	}
 	
+	if (why != WHY_RETURN)
+		retval = NULL;
+	
+	if (trace) {
+		if (why == WHY_RETURN) {
+			x = call_trace(trace, f, "return", retval);
+			if (x == NULL) {
+				XDECREF(retval);
+				retval = NULL;
+			}
+			else
+				DECREF(x);
+		}
+		DECREF(trace);
+	}
+	
 	/* Restore previous frame and release the current one */
 	
 	current_frame = f->f_back;
 	DECREF(f);
 	
-	if (why == WHY_RETURN)
-		return retval;
-	else
-		return NULL;
+	return retval;
 }
 
-#ifdef TRACE
+#ifdef LLTRACE
 static int
 prtrace(v, str)
 	object *v;
@@ -1073,6 +1168,46 @@
 }
 #endif
 
+static object *
+call_trace(trace, f, msg, arg)
+	object *trace;
+	frameobject *f;
+	char *msg;
+	object *arg;
+{
+	object *arglist, *what, *res;
+	static int tracing = 0;
+	
+	if (tracing) {
+		/* Don't trace the trace code! */
+		INCREF(None);
+		return None;
+	}
+	
+	arglist = newtupleobject(3);
+	if (arglist == NULL)
+		return NULL;
+	what = newstringobject(msg);
+	if (what == NULL) {
+		DECREF(arglist);
+		return NULL;
+	}
+	INCREF(f);
+	if (arg == NULL)
+		arg = None;
+	INCREF(arg);
+	settupleitem(arglist, 0, (object *)f);
+	settupleitem(arglist, 1, what);
+	settupleitem(arglist, 2, arg);
+	tracing++;
+	res = call_object(trace, arglist);
+	tracing--;
+	if (res == NULL)
+		tb_here(f);
+	DECREF(arglist);
+	return res;
+}
+
 object *
 getlocals()
 {