User trace feature.
This commit is contained in:
parent
921c82401b
commit
96a42c85bc
189
Python/ceval.c
189
Python/ceval.c
|
@ -36,7 +36,9 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#include "traceback.h"
|
#include "traceback.h"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define TRACE
|
/* For debugging the interpreter: */
|
||||||
|
#define LLTRACE 1 /* Low-level trace feature */
|
||||||
|
#define CHECKEXC 1 /* Double-check exception checking */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
|
@ -69,6 +71,7 @@ static int testbool();
|
||||||
static int assign_subscript PROTO((object *, object *, object *));
|
static int assign_subscript PROTO((object *, object *, object *));
|
||||||
static int assign_slice PROTO((object *, object *, object *, object *));
|
static int assign_slice PROTO((object *, object *, object *, object *));
|
||||||
static int import_from PROTO((object *, object *, object *));
|
static int import_from PROTO((object *, object *, object *));
|
||||||
|
static object *call_trace PROTO((object *, frameobject *, char *, object *));
|
||||||
|
|
||||||
|
|
||||||
static frameobject *current_frame;
|
static frameobject *current_frame;
|
||||||
|
@ -106,12 +109,12 @@ eval_code(co, globals, locals, arg)
|
||||||
register object *u;
|
register object *u;
|
||||||
register object *t;
|
register object *t;
|
||||||
register frameobject *f; /* Current frame */
|
register frameobject *f; /* Current frame */
|
||||||
int lineno; /* Current line number */
|
object *trace; /* Trace function or NULL */
|
||||||
object *retval; /* Return value iff why == WHY_RETURN */
|
object *retval; /* Return value iff why == WHY_RETURN */
|
||||||
char *name; /* Name used by some instructions */
|
char *name; /* Name used by some instructions */
|
||||||
FILE *fp; /* Used by print operations */
|
FILE *fp; /* Used by print operations */
|
||||||
#ifdef TRACE
|
#ifdef LLTRACE
|
||||||
int trace = dictlookup(globals, "__trace__") != NULL;
|
int lltrace = dictlookup(globals, "__lltrace__") != NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Code access macros */
|
/* Code access macros */
|
||||||
|
@ -134,9 +137,9 @@ eval_code(co, globals, locals, arg)
|
||||||
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
|
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
|
||||||
#define BASIC_POP() (*--stack_pointer)
|
#define BASIC_POP() (*--stack_pointer)
|
||||||
|
|
||||||
#ifdef TRACE
|
#ifdef LLTRACE
|
||||||
#define PUSH(v) (BASIC_PUSH(v), trace && prtrace(TOP(), "push"))
|
#define PUSH(v) (BASIC_PUSH(v), lltrace && prtrace(TOP(), "push"))
|
||||||
#define POP() (trace && prtrace(TOP(), "pop"), BASIC_POP())
|
#define POP() (lltrace && prtrace(TOP(), "pop"), BASIC_POP())
|
||||||
#else
|
#else
|
||||||
#define PUSH(v) BASIC_PUSH(v)
|
#define PUSH(v) BASIC_PUSH(v)
|
||||||
#define POP() BASIC_POP()
|
#define POP() BASIC_POP()
|
||||||
|
@ -154,8 +157,36 @@ eval_code(co, globals, locals, arg)
|
||||||
|
|
||||||
current_frame = f;
|
current_frame = f;
|
||||||
|
|
||||||
next_instr = GETUSTRINGVALUE(f->f_code->co_code);
|
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;
|
stack_pointer = f->f_valuestack;
|
||||||
|
|
||||||
if (arg != NULL) {
|
if (arg != NULL) {
|
||||||
|
@ -166,7 +197,6 @@ eval_code(co, globals, locals, arg)
|
||||||
why = WHY_NOT;
|
why = WHY_NOT;
|
||||||
err = 0;
|
err = 0;
|
||||||
x = None; /* Not a reference, just anything non-NULL */
|
x = None; /* Not a reference, just anything non-NULL */
|
||||||
lineno = -1;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
static int ticker;
|
static int ticker;
|
||||||
|
@ -178,7 +208,6 @@ eval_code(co, globals, locals, arg)
|
||||||
if (intrcheck()) {
|
if (intrcheck()) {
|
||||||
err_set(KeyboardInterrupt);
|
err_set(KeyboardInterrupt);
|
||||||
why = WHY_EXCEPTION;
|
why = WHY_EXCEPTION;
|
||||||
tb_here(f, INSTR_OFFSET(), lineno);
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,10 +218,10 @@ eval_code(co, globals, locals, arg)
|
||||||
if (HAS_ARG(opcode))
|
if (HAS_ARG(opcode))
|
||||||
oparg = NEXTARG();
|
oparg = NEXTARG();
|
||||||
|
|
||||||
#ifdef TRACE
|
#ifdef LLTRACE
|
||||||
/* Instruction tracing */
|
/* Instruction tracing */
|
||||||
|
|
||||||
if (trace) {
|
if (lltrace) {
|
||||||
if (HAS_ARG(opcode)) {
|
if (HAS_ARG(opcode)) {
|
||||||
printf("%d: %d, %d\n",
|
printf("%d: %d, %d\n",
|
||||||
(int) (INSTR_OFFSET() - 3),
|
(int) (INSTR_OFFSET() - 3),
|
||||||
|
@ -273,6 +302,7 @@ eval_code(co, globals, locals, arg)
|
||||||
|
|
||||||
case UNARY_CALL:
|
case UNARY_CALL:
|
||||||
v = POP();
|
v = POP();
|
||||||
|
f->f_lasti = INSTR_OFFSET() - 1; /* For tracing */
|
||||||
x = call_object(v, (object *)NULL);
|
x = call_object(v, (object *)NULL);
|
||||||
DECREF(v);
|
DECREF(v);
|
||||||
PUSH(x);
|
PUSH(x);
|
||||||
|
@ -342,6 +372,7 @@ eval_code(co, globals, locals, arg)
|
||||||
case BINARY_CALL:
|
case BINARY_CALL:
|
||||||
w = POP();
|
w = POP();
|
||||||
v = POP();
|
v = POP();
|
||||||
|
f->f_lasti = INSTR_OFFSET() - 1; /* For tracing */
|
||||||
x = call_object(v, w);
|
x = call_object(v, w);
|
||||||
DECREF(v);
|
DECREF(v);
|
||||||
DECREF(w);
|
DECREF(w);
|
||||||
|
@ -921,17 +952,31 @@ eval_code(co, globals, locals, arg)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SET_LINENO:
|
case SET_LINENO:
|
||||||
#ifdef TRACE
|
#ifdef LLTRACE
|
||||||
if (trace)
|
if (lltrace)
|
||||||
printf("--- Line %d ---\n", oparg);
|
printf("--- Line %d ---\n", oparg);
|
||||||
#endif
|
#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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"XXX lineno: %d, opcode: %d\n",
|
"XXX lineno: %d, opcode: %d\n",
|
||||||
lineno, opcode);
|
f->f_lineno, opcode);
|
||||||
err_setstr(SystemError, "eval_code: unknown opcode");
|
err_setstr(SystemError, "eval_code: unknown opcode");
|
||||||
why = WHY_EXCEPTION;
|
why = WHY_EXCEPTION;
|
||||||
break;
|
break;
|
||||||
|
@ -950,7 +995,7 @@ eval_code(co, globals, locals, arg)
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef CHECKEXC
|
||||||
/* Double-check exception status */
|
/* Double-check exception status */
|
||||||
|
|
||||||
if (why == WHY_EXCEPTION || why == WHY_RERAISE) {
|
if (why == WHY_EXCEPTION || why == WHY_RERAISE) {
|
||||||
|
@ -971,10 +1016,49 @@ eval_code(co, globals, locals, arg)
|
||||||
/* Log traceback info if this is a real exception */
|
/* Log traceback info if this is a real exception */
|
||||||
|
|
||||||
if (why == WHY_EXCEPTION) {
|
if (why == WHY_EXCEPTION) {
|
||||||
int lasti = INSTR_OFFSET() - 1;
|
f->f_lasti = INSTR_OFFSET() - 1;
|
||||||
if (HAS_ARG(opcode))
|
if (HAS_ARG(opcode))
|
||||||
lasti -= 2;
|
f->f_lasti -= 2;
|
||||||
tb_here(f, lasti, lineno);
|
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 */
|
/* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */
|
||||||
|
@ -1012,9 +1096,7 @@ eval_code(co, globals, locals, arg)
|
||||||
Python main loop. Don't do
|
Python main loop. Don't do
|
||||||
this for 'finally'. */
|
this for 'finally'. */
|
||||||
if (b->b_type == SETUP_EXCEPT) {
|
if (b->b_type == SETUP_EXCEPT) {
|
||||||
#if 1 /* Oops, this breaks too many things */
|
|
||||||
sysset("exc_traceback", v);
|
sysset("exc_traceback", v);
|
||||||
#endif
|
|
||||||
sysset("exc_value", val);
|
sysset("exc_value", val);
|
||||||
sysset("exc_type", exc);
|
sysset("exc_type", exc);
|
||||||
err_clear();
|
err_clear();
|
||||||
|
@ -1049,18 +1131,31 @@ eval_code(co, globals, locals, arg)
|
||||||
XDECREF(v);
|
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 */
|
/* Restore previous frame and release the current one */
|
||||||
|
|
||||||
current_frame = f->f_back;
|
current_frame = f->f_back;
|
||||||
DECREF(f);
|
DECREF(f);
|
||||||
|
|
||||||
if (why == WHY_RETURN)
|
return retval;
|
||||||
return retval;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TRACE
|
#ifdef LLTRACE
|
||||||
static int
|
static int
|
||||||
prtrace(v, str)
|
prtrace(v, str)
|
||||||
object *v;
|
object *v;
|
||||||
|
@ -1073,6 +1168,46 @@ prtrace(v, str)
|
||||||
}
|
}
|
||||||
#endif
|
#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 *
|
object *
|
||||||
getlocals()
|
getlocals()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue