/* Copyright (c) 1982 Regents of the University of California */
static char sccsid
[] = "@(#)eval.c 1.3 %G%";
#define push(type, value) { \
((type *) (sp += sizeof(type)))[-1] = (value); \
(*((type *) (sp -= sizeof(type)))) \
sp = (Stack *) (( ((int) sp) + sizeof(int) - 1)&~(sizeof(int) - 1)); \
public Stack stack
[STACKSIZE
];
public Stack
*sp
= &stack
[0];
panic("stack underflow"); \
#define poparg(n, r, fr) { \
} else if (isint(p->op)) { \
r = popsmall(p->value.arg[n]->nodetype); \
#define Boolrep char /* underlying representation type for booleans */
* Evaluate a parse tree leaving the value on the top of the stack.
push(long, return_addr());
if (s
!= program
and not isactive(container(s
))) {
error("\"%s\" is not active", symname(s
));
push(long, address(s
, nil
));
error("can't evaluate a %s", classname(s
));
pushsmall(p
->nodetype
, r0
);
push(double, p
->value
.fcon
);
mov(p
->value
.scon
, sp
, len
);
i
= evalindex(p
->value
.arg
[0]->nodetype
,
popsmall(p
->value
.arg
[1]->nodetype
));
push(long, n
+ i
*size(p
->nodetype
));
s
= p
->value
.arg
[1]->value
.sym
;
n
= lval(p
->value
.arg
[0]);
push(long, n
+ (s
->symvalue
.field
.offset div
8));
* Get the value of the expression addressed by the top of the stack.
* Push the result back on the stack.
error("reference through nil pointer");
push(double, (double) r0
);
error("error: division by 0");
error("error: div by 0");
error("error: mod by 0");
push(Boolrep
, fr0
< fr1
);
push(Boolrep
, fr0
<= fr1
);
push(Boolrep
, fr0
> fr1
);
push(Boolrep
, fr0
== fr1
);
push(Boolrep
, fr0
!= fr1
);
push(Boolrep
, r0
and r1
);
assign(p
->value
.arg
[0], p
->value
.arg
[1]);
if (p
->value
.scon
== nil
) {
printf("%s\n", cursource
);
file
= opensource(p
->value
.scon
);
error("can't read \"%s\"", p
->value
.scon
);
setsource(p
->value
.scon
);
if (p
->value
.arg
[0]->op
== O_SYM
) {
f
= p
->value
.arg
[0]->value
.sym
;
error("no source lines for \"%s\"", symname(f
));
setsource(srcfilename(addr
));
printlines((Lineno
) r0
, (Lineno
) r1
);
if (p
->value
.arg
[0] == nil
) {
printname(stdout
, curfunc
);
curfunc
= p
->value
.arg
[0]->value
.sym
;
setsource(srcfilename(addr
));
cursrcline
= srcline(addr
) - 5;
eval(p
->value
.examine
.beginaddr
);
if (p
->value
.examine
.endaddr
== nil
) {
n
= p
->value
.examine
.count
;
if (streq(p
->value
.examine
.mode
, "i")) {
printninst(n
, (Address
) r0
);
printndata(n
, (Address
) r0
, p
->value
.examine
.mode
);
eval(p
->value
.examine
.endaddr
);
if (streq(p
->value
.examine
.mode
, "i")) {
printinst((Address
)r0
, (Address
)r1
);
printdata((Address
)r0
, (Address
)r1
, p
->value
.examine
.mode
);
for (n1
= p
->value
.arg
[0]; n1
!= nil
; n1
= n1
->value
.arg
[1]) {
printval(n1
->value
.arg
[0]->nodetype
);
if (p
->value
.arg
[0]->op
== O_SYM
) {
psym(p
->value
.arg
[0]->value
.sym
);
psym(p
->value
.arg
[0]->nodetype
);
inst_tracing
= (Boolean
) (not p
->value
.step
.source
);
if (p
->value
.step
.skipcalls
) {
if (p
->value
.arg
[0]->op
== O_SYM
) {
printdecl(p
->value
.arg
[0]->value
.sym
);
printdecl(p
->value
.arg
[0]->nodetype
);
printwhereis(stdout
, p
->value
.arg
[0]->value
.sym
);
printwhich(stdout
, p
->value
.arg
[0]->value
.sym
);
print_alias(n1
->value
.name
);
enter_alias(n1
->value
.name
, n2
->value
.name
);
callproc(p
->value
.arg
[0], p
->value
.arg
[1]);
psigtrace(process
, p
->value
.lcon
, true);
psigtrace(process
, p
->value
.lcon
, false);
addevent(p
->value
.event
.cond
, p
->value
.event
.actions
);
delevent((unsigned int) p
->value
.lcon
);
if (cond(p
->value
.event
.cond
)) {
evalcmdlist(p
->value
.event
.actions
);
event_once(p
->value
.event
.cond
, p
->value
.event
.actions
);
printcall(p
->value
.sym
, whatblock(return_addr()));
printifchanged(p
->value
.arg
[0]);
if (p
->value
.arg
[0] == nil
) {
printlines(curline
, curline
);
} else if (p
->value
.arg
[0]->op
== O_QLINE
) {
if (p
->value
.arg
[0]->value
.arg
[1]->value
.lcon
== 0) {
printlines(curline
, curline
);
prtree(stdout
, p
->value
.arg
[0]);
printval(p
->value
.arg
[0]->nodetype
);
procreturn(p
->value
.sym
);
stopifchanged(p
->value
.arg
[0]);
traceon(p
->value
.trace
.inst
, p
->value
.trace
.event
,
panic("eval: bad op %d", p
->op
);
* Evaluate a list of commands.
* Push "len" bytes onto the expression stack from address "addr"
* in the process. If there isn't room on the stack, print an error message.
error("expression too large to evaluate");
* Check if the stack has n bytes available.
public Boolean
canpush(n
)
return (Boolean
) (sp
+ n
< &stack
[STACKSIZE
]);
* Push a small scalar of the given type onto the stack.
panic("bad size %d in popsmall", s
);
* Pop an item of the given type which is assumed to be no larger
* than a long and return it expanded into a long.
panic("popsmall: size is %d", size(t
));
* Evaluate a conditional expression.
* Return the address corresponding to a given tree.
return (Address
) (pop(long));
* Process a trace command, translating into the appropriate events
* and associated actions.
traceall(p
->op
, place
, cond
);
} else if (exp
->op
== O_QLINE
) {
traceinst(p
->op
, exp
, cond
);
} else if (place
!= nil
and place
->op
== O_QLINE
) {
traceat(p
->op
, exp
, place
, cond
);
if (left
->op
== O_RVAL
or left
->op
== O_CALL
) {
left
= left
->value
.arg
[0];
if (left
->op
== O_SYM
and isblock(left
->value
.sym
)) {
traceproc(p
->op
, left
->value
.sym
, place
, cond
);
tracedata(p
->op
, exp
, place
, cond
);
* Set a breakpoint that will turn on tracing.
private traceall(op
, place
, cond
)
event
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, s
));
action
= build(O_PRINTSRCPOS
,
build(O_QLINE
, nil
, build(O_LCON
, (op
== O_TRACE
) ? 1 : 0)));
action
= build(O_IF
, cond
, buildcmdlist(action
));
action
= build(O_TRACEON
, (op
== O_TRACEI
), buildcmdlist(action
));
action
->value
.trace
.event
= addevent(event
, buildcmdlist(action
));
* Set up the appropriate breakpoint for tracing an instruction.
private traceinst(op
, exp
, cond
)
event
= build(O_EQ
, build(O_SYM
, pcsym
), exp
);
event
= build(O_EQ
, build(O_SYM
, linesym
), exp
);
action
= build(O_PRINTSRCPOS
, exp
);
action
= build(O_IF
, cond
, buildcmdlist(action
));
addevent(event
, buildcmdlist(action
));
* Set a breakpoint to print an expression at a given line or address.
private traceat(op
, exp
, place
, cond
)
event
= build(O_EQ
, build(O_SYM
, pcsym
), place
);
event
= build(O_EQ
, build(O_SYM
, linesym
), place
);
action
= build(O_PRINTSRCPOS
, exp
);
action
= build(O_IF
, cond
, buildcmdlist(action
));
addevent(event
, buildcmdlist(action
));
* Construct event for tracing a procedure.
* Note that "once" is like "when" except that the event
* deletes itself as part of its associated action.
private traceproc(op
, p
, place
, cond
)
action
= build(O_PRINTCALL
, p
);
actionlist
= list_alloc();
cmdlist_append(action
, actionlist
);
event
= build(O_EQ
, build(O_SYM
, pcsym
), build(O_SYM
, retaddrsym
));
action
= build(O_ONCE
, event
, buildcmdlist(build(O_PRINTRTN
, p
)));
cmdlist_append(action
, actionlist
);
actionlist
= buildcmdlist(build(O_IF
, cond
, actionlist
));
event
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, p
));
addevent(event
, actionlist
);
* Set up breakpoint for tracing data.
private tracedata(op
, exp
, place
, cond
)
p
= (place
== nil
) ? tcontainer(exp
) : place
->value
.sym
;
action
= build(O_PRINTIFCHANGED
, exp
);
action
= build(O_IF
, cond
, buildcmdlist(action
));
action
= build(O_TRACEON
, (op
== O_TRACEI
), buildcmdlist(action
));
event
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, p
));
action
->value
.trace
.event
= addevent(event
, buildcmdlist(action
));
* Setting and unsetting of stops.
stopvar(p
->op
, exp
, place
, cond
);
} else if (cond
!= nil
) {
s
= (place
== nil
) ? program
: place
->value
.sym
;
action
= build(O_IF
, cond
, buildcmdlist(build(O_STOPX
)));
action
= build(O_TRACEON
, (p
->op
== O_STOPI
), buildcmdlist(action
));
cond
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, s
));
action
->value
.trace
.event
= addevent(cond
, buildcmdlist(action
));
} else if (place
->op
== O_SYM
) {
cond
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, s
));
addevent(cond
, buildcmdlist(build(O_STOPX
)));
stopinst(p
->op
, place
, cond
);
private stopinst(op
, place
, cond
)
event
= build(O_EQ
, build(O_SYM
, linesym
), place
);
event
= build(O_EQ
, build(O_SYM
, pcsym
), place
);
addevent(event
, buildcmdlist(build(O_STOPX
)));
* Implement stopping on assignment to a variable by adding it to
private stopvar(op
, exp
, place
, cond
)
p
= (place
== nil
) ? tcontainer(exp
) : place
->value
.sym
;
action
= build(O_IF
, cond
, buildcmdlist(build(O_STOPIFCHANGED
, exp
)));
action
= build(O_TRACEON
, (op
== O_STOPI
), buildcmdlist(action
));
event
= build(O_EQ
, build(O_SYM
, procsym
), build(O_SYM
, p
));
action
->value
.trace
.event
= addevent(event
, buildcmdlist(action
));
* Assign the value of an expression to a variable (or term).
if (not compatible(var
->nodetype
, exp
->nodetype
)) {
error("incompatible types");
varsize
= size(var
->nodetype
);
if (varsize
< sizeof(long)) {
dwrite(&cvalue
, addr
, varsize
);
dwrite(&svalue
, addr
, varsize
);
panic("bad size %d", varsize
);
dwrite(sp
, addr
, varsize
);
* Invoke an editor on the given file. Which editor to use might change
* installation to installation. For now, we use "vi". In any event,
* the environment variable "EDITOR" overrides any default.
call(ed
, stdin
, stdout
, cursource
, nil
);
f
= fopen(filename
, "r");
s
= which(identname(filename
, true));
error("can't read \"%s\"", filename
);
error("no source for \"%s\"", filename
);
sprintf(buff
, "+%d", srcline(addr
));
call(ed
, stdin
, stdout
, buff
, src
, nil
);
call(ed
, stdin
, stdout
, filename
, nil
);
* Send some nasty mail to the current support person.
char *maintainer
= "linton@ucbarpa";
puts("Type control-D to end your message. Be sure to include");
puts("your name and the name of the file you are debugging.");
old
= signal(SIGINT
, SIG_DFL
);
call("Mail", stdin
, stdout
, maintainer
, nil
);
* Give the user some help.
puts("run - begin execution of the program");
puts("cont - continue execution");
puts("step - single step one line");
puts("next - step to next line (skip over calls)");
puts("trace <line#> - trace execution of the line");
puts("trace <proc> - trace calls to the procedure");
puts("trace <var> - trace changes to the variable");
puts("trace <exp> at <line#> - print <exp> when <line> is reached");
puts("stop at <line> - suspend execution at the line");
puts("stop in <proc> - suspend execution when <proc> is called");
puts("status - print trace/stop's in effect");
puts("delete <number> - remove trace or stop of given number");
puts("call <proc> - call the procedure");
puts("where - print currently active procedures");
puts("print <exp> - print the value of the expression");
puts("whatis <name> - print the declaration of the name");
puts("list <line>, <line> - list source lines");
puts("edit <proc> - edit file containing <proc>");
puts("gripe - send mail to the person in charge of dbx");
* Divert output to the given file name.
* Cannot redirect to an existing file.
private Boolean notstdout
;
f
= fopen(filename
, "r");
error("%s: file already exists", filename
);
if (creat(filename
, 0666) == nil
) {
error("can't create %s", filename
);
* Revert output to standard output.
panic("standard out dup failed");
* Determine is standard output is currently being redirected
* to a file (as far as we know).
public Boolean
isredirected()