148069Sbostic /*-
2*62163Sbostic  * Copyright (c) 1980, 1993
3*62163Sbostic  *	The Regents of the University of California.  All rights reserved.
448069Sbostic  *
548069Sbostic  * %sccs.include.redist.c%
622565Sdist  */
75546Slinton 
822565Sdist #ifndef lint
9*62163Sbostic static char sccsid[] = "@(#)tracestop.c	8.1 (Berkeley) 06/06/93";
1048069Sbostic #endif /* not lint */
115546Slinton 
125546Slinton /*
135546Slinton  * Handle trace and stop commands.
145546Slinton  */
155546Slinton 
165546Slinton #include "defs.h"
175546Slinton #include "breakpoint.h"
185546Slinton #include "sym.h"
195546Slinton #include "tree.h"
205546Slinton #include "runtime.h"
215546Slinton #include "source.h"
225546Slinton #include "object.h"
235546Slinton #include "mappings.h"
245546Slinton #include "machine.h"
255546Slinton #include "tree.rep"
265546Slinton 
275546Slinton LOCAL SYM *tcontainer();
285546Slinton 
295546Slinton /*
305546Slinton  * Process a trace/untrace command, basically checking arguments
315546Slinton  * and translate to a call of the appropriate routine.
325546Slinton  */
335546Slinton 
trace(cmd,exp,where,cond)345546Slinton trace(cmd, exp, where, cond)
355546Slinton int cmd;
365546Slinton NODE *exp;
375546Slinton NODE *where;
385546Slinton NODE *cond;
395546Slinton {
405546Slinton 	if (exp == NIL) {
415546Slinton 		traceall(cmd, where, cond);
425546Slinton 	} else if (exp->op == O_LCON || exp->op == O_QLINE) {
435546Slinton 		traceinst(cmd, exp, where, cond);
445546Slinton 	} else if (where!=NIL && (where->op==O_QLINE || where->op==O_LCON)) {
455546Slinton 		traceat(cmd, exp, where, cond);
465546Slinton 	} else {
475546Slinton 		tracedata(cmd, exp, where, cond);
485546Slinton 	}
495546Slinton 	if (where != NIL) {
505546Slinton 		tfree(where);
515546Slinton 	}
525546Slinton }
535546Slinton 
545546Slinton /*
555546Slinton  * Set a breakpoint that will turn on tracing.
565546Slinton  *
575546Slinton  * A line number of 0 in the breakpoint information structure
585546Slinton  * means it's a normal trace.
595546Slinton  *
605546Slinton  * A line number of -1 indicates that we want to trace at the instruction
615546Slinton  * rather than source line level.
625546Slinton  *
635546Slinton  * If location is NIL, turn on tracing because if the user
645546Slinton  * has the program stopped somewhere and says "trace",
655546Slinton  * he/she wants to see tracing after continuing execution.
665546Slinton  */
675546Slinton 
traceall(cmd,where,cond)685546Slinton LOCAL traceall(cmd, where, cond)
695546Slinton int cmd;
705546Slinton NODE *where;
715546Slinton NODE *cond;
725546Slinton {
735546Slinton 	SYM *s;
745546Slinton 	LINENO line;
755546Slinton 
765546Slinton 	if (where != NIL && where->op != O_NAME) {
775546Slinton 		error("bad location for trace");
785546Slinton 	}
795546Slinton 	if (cmd == O_TRACE) {
805546Slinton 		line = 0;
815546Slinton 	} else {
825546Slinton 		line = -1;
835546Slinton 	}
845546Slinton 	if (where == NIL) {
855546Slinton 		switch (cmd) {
865546Slinton 			case O_TRACE:
875546Slinton 				if (tracing != 0) {
885546Slinton 					error("already tracing lines");
895546Slinton 				}
905546Slinton 				tracing++;
915546Slinton 				addcond(TRPRINT, cond);
925546Slinton 				break;
935546Slinton 
945546Slinton 			case O_TRACEI:
955546Slinton 				if (inst_tracing != 0) {
965546Slinton 					error("already tracing instructions");
975546Slinton 				}
985546Slinton 				inst_tracing++;
995546Slinton 				addcond(TRPRINT, cond);
1005546Slinton 				break;
1015546Slinton 
1025546Slinton 			default:
1035546Slinton 				panic("bad cmd in traceall");
1045546Slinton 				break;
1055546Slinton 		}
1065546Slinton 		s = program;
1075546Slinton 	} else if (where->op != O_NAME) {
1085546Slinton 		trerror("found %t, expected procedure or function", where);
1095546Slinton 	} else {
1105546Slinton 		s = where->nameval;
1115546Slinton 		if (!isblock(s)) {
1125546Slinton 			error("\"%s\" is not a procedure or function", name(s));
1135546Slinton 		}
1145546Slinton 	}
1155546Slinton 	addbp(codeloc(s), ALL_ON, s, cond, NIL, line);
1165546Slinton }
1175546Slinton 
1185546Slinton /*
1195546Slinton  * Set up the appropriate breakpoint for tracing an instruction.
1205546Slinton  */
1215546Slinton 
traceinst(cmd,exp,where,cond)1225546Slinton LOCAL traceinst(cmd, exp, where, cond)
1235546Slinton int cmd;
1245546Slinton NODE *exp;
1255546Slinton NODE *where;
1265546Slinton NODE *cond;
1275546Slinton {
1285546Slinton 	LINENO line;
1295546Slinton 	ADDRESS addr;
1305546Slinton 
1315546Slinton 	if (where != NIL) {
1325546Slinton 		error("unexpected \"at\" or \"in\"");
1335546Slinton 	}
1345546Slinton 	if (cmd == O_TRACEI) {
1355546Slinton 		if (exp->op == O_QLINE) {
1365546Slinton 			addr = (ADDRESS) exp->right->lconval;
1375546Slinton 		} else if (exp->op == O_LCON) {
1385546Slinton 			addr = (ADDRESS) exp->lconval;
1395546Slinton 		} else {
1405546Slinton 			trerror("expected integer constant, found %t", exp);
1415546Slinton 		}
1425546Slinton 		line = -1;
1435546Slinton 	} else {
1445546Slinton 		if (exp->op == O_QLINE) {
1455546Slinton 			line = (LINENO) exp->right->lconval;
1465546Slinton 			addr = objaddr(line, exp->left->sconval);
1475546Slinton 		} else {
1485546Slinton 			line = (LINENO) exp->lconval;
1495546Slinton 			addr = objaddr(line, cursource);
1505546Slinton 		}
1515546Slinton 		if (addr == (ADDRESS) -1) {
1525546Slinton 			error("can't trace line %d", line);
1535546Slinton 		}
1545546Slinton 	}
1555546Slinton 	tfree(exp);
1565546Slinton 	addbp(addr, INST, NIL, cond, NIL, line);
1575546Slinton }
1585546Slinton 
1595546Slinton /*
1605546Slinton  * set a breakpoint to print an expression at a given line or address
1615546Slinton  */
1625546Slinton 
traceat(cmd,exp,where,cond)1635546Slinton LOCAL traceat(cmd, exp, where, cond)
1645546Slinton int cmd;
1655546Slinton NODE *exp;
1665546Slinton NODE *where;
1675546Slinton NODE *cond;
1685546Slinton {
1695546Slinton 	LINENO line;
1705546Slinton 	ADDRESS addr;
1715546Slinton 
1725546Slinton 	if (cmd == O_TRACEI) {
1735546Slinton 		if (where->op != O_LCON) {
1745546Slinton 			trerror("expected integer constant, found %t", where);
1755546Slinton 		}
1765546Slinton 		line = -1;
1775546Slinton 		addr = (ADDRESS) where->lconval;
1785546Slinton 	} else {
1795546Slinton 		line = (LINENO) where->right->lconval;
1805546Slinton 		addr = objaddr(line, where->left->sconval);
1815546Slinton 		if (addr == (ADDRESS) -1) {
1825546Slinton 			error("can't trace at line %d", line);
1835546Slinton 		}
1845546Slinton 	}
1855546Slinton 	addbp(addr, AT_BP, NIL, cond, exp, line);
1865546Slinton }
1875546Slinton 
1885546Slinton /*
1895546Slinton  * Set up breakpoint for tracing data.
1905546Slinton  *
1915546Slinton  * The tracing of blocks lies somewhere between instruction and data;
1925546Slinton  * it's here since a block cannot be distinguished from other terms.
1935546Slinton  *
1945546Slinton  * As in "traceall", if the "block" is the main program then the
1955546Slinton  * user didn't actually specify a block.  This means we want to
1965546Slinton  * turn tracing on ourselves because if the program is stopped
1975546Slinton  * we want to be on regardless of whether they say "cont" or "run".
1985546Slinton  */
1995546Slinton 
tracedata(cmd,exp,block,cond)2005546Slinton LOCAL tracedata(cmd, exp, block, cond)
2015546Slinton int cmd;
2025546Slinton NODE *exp;
2035546Slinton NODE *block;
2045546Slinton NODE *cond;
2055546Slinton {
2065546Slinton 	SYM *s, *t;
2075546Slinton 
20830829Smckusick #ifdef lint
20930829Smckusick 	cmd = cmd;
21030829Smckusick #endif
2115546Slinton 	if (exp->op != O_RVAL && exp->op != O_CALL) {
2125546Slinton 		error("can't trace expressions");
2135546Slinton 	}
2145546Slinton 	if (block == NIL) {
2155546Slinton 		t = tcontainer(exp->left);
2165546Slinton 	} else if (block->op == O_NAME) {
2175546Slinton 		t = block->nameval;
2185546Slinton 	} else {
2195546Slinton 		trerror("found %t, expected procedure or function", block);
2205546Slinton 	}
2215546Slinton 	if (exp->left->op == O_NAME) {
2225546Slinton 		s = exp->left->nameval;
2235546Slinton 		if (isblock(s)) {
2245546Slinton 			addbp(codeloc(t), BLOCK_ON, t, cond, exp->left, 0);
2255546Slinton 			if (t == program) {
2265546Slinton 				addbp(codeloc(s), CALL, s, cond, NIL, 0);
2275546Slinton 			}
2285546Slinton 			return;
2295546Slinton 		}
2305546Slinton 	}
2315546Slinton 	addbp(codeloc(t), TERM_ON, t, cond, exp, 0);
2325546Slinton 	if (curfunc == t) {
2335546Slinton 		var_tracing++;
2345546Slinton 		addvar(TRPRINT, exp, cond);
2355546Slinton 		addbp(return_addr(), TERM_OFF, t, cond, exp, 0);
2365546Slinton 	}
2375546Slinton }
2385546Slinton 
2395546Slinton /*
2405546Slinton  * Setting and unsetting of stops.
2415546Slinton  */
2425546Slinton 
stop(cmd,exp,where,cond)2435546Slinton stop(cmd, exp, where, cond)
2445546Slinton int cmd;
2455546Slinton NODE *exp;
2465546Slinton NODE *where;
2475546Slinton NODE *cond;
2485546Slinton {
2495546Slinton 	SYM *s;
2505546Slinton 	LINENO n;
2515546Slinton 
2525546Slinton 	if (exp != NIL) {
2535546Slinton 		stopvar(cmd, exp, where, cond);
2545546Slinton 	} else if (cond != NIL) {
2555546Slinton 		if (where == NIL) {
2565546Slinton 			s = program;
2575546Slinton 		} else if (where->op == O_NAME) {
2585546Slinton 			s = where->nameval;
2595546Slinton 		} else {
2605546Slinton 			error("bad location for stop");
2615546Slinton 		}
2625546Slinton 		n = codeloc(s);
2635546Slinton 		addbp(n, STOP_ON, s, cond, NIL, n);
2645546Slinton 		addcond(TRSTOP, cond);
2655546Slinton 		var_tracing++;
2665546Slinton 	} else if (where->op == O_NAME) {
2675546Slinton 		s = where->nameval;
2685546Slinton 		if (!isblock(s)) {
2695546Slinton 			error("\"%s\" is not a procedure or function", name(s));
2705546Slinton 		}
2715546Slinton 		n = codeloc(s);
2725546Slinton 		addbp(n, STOP_BP, s, cond, NIL, srcline(firstline(s)));
2735546Slinton 	} else {
2745546Slinton 		stopinst(cmd, where, cond);
2755546Slinton 	}
2765546Slinton 	if (where != NIL) {
2775546Slinton 		tfree(where);
2785546Slinton 	}
2795546Slinton }
2805546Slinton 
stopinst(cmd,where,cond)2815546Slinton LOCAL stopinst(cmd, where, cond)
2825546Slinton int cmd;
2835546Slinton NODE *where;
2845546Slinton NODE *cond;
2855546Slinton {
2865546Slinton 	LINENO line;
2875546Slinton 	ADDRESS addr;
2885546Slinton 
2895546Slinton 	if (where->op != O_QLINE) {
2905546Slinton 		error("expected line number");
2915546Slinton 	}
2925546Slinton 	if (cmd == O_STOP) {
2935546Slinton 		line = (LINENO) where->right->lconval;
2945546Slinton 		addr = objaddr(line, where->left->sconval);
2955546Slinton 		if (addr == (ADDRESS) -1) {
2965546Slinton 			error("can't stop at that line");
2975546Slinton 		}
2985546Slinton 	} else {
2995546Slinton 		line = -1;
3005546Slinton 		addr = (ADDRESS) where->right->lconval;
3015546Slinton 	}
3025546Slinton 	addbp(addr, STOP_BP, NIL, cond, NIL, line);
3035546Slinton }
3045546Slinton 
3055546Slinton /*
3065546Slinton  * Implement stopping on assignment to a variable by adding it to
3075546Slinton  * the variable list.
3085546Slinton  */
3095546Slinton 
stopvar(cmd,exp,where,cond)3105546Slinton LOCAL stopvar(cmd, exp, where, cond)
3115546Slinton int cmd;
3125546Slinton NODE *exp;
3135546Slinton NODE *where;
3145546Slinton NODE *cond;
3155546Slinton {
3165546Slinton 	SYM *s;
3175546Slinton 
3185546Slinton 	if (exp->op != O_RVAL) {
3195546Slinton 		trerror("found %t, expected variable", exp);
3205546Slinton 	}
3215546Slinton 	if (cmd == O_STOPI) {
3225546Slinton 		inst_tracing++;
3235546Slinton 	}
3245546Slinton 	var_tracing++;
3255546Slinton 	addvar(TRSTOP, exp, cond);
3265546Slinton 	if (where == NIL) {
3275546Slinton 		s = program;
3285546Slinton 	} else if (where->op == O_NAME) {
3295546Slinton 		s = where->nameval;
3305546Slinton 	} else {
3315546Slinton 		error("bad location for stop");
3325546Slinton 	}
3335546Slinton 	addbp(codeloc(s), STOP_ON, s, cond, exp, 0);
3345546Slinton }
3355546Slinton 
3365546Slinton /*
3375546Slinton  * Figure out the block that contains the symbols
3385546Slinton  * in the given variable expression.
3395546Slinton  */
3405546Slinton 
tcontainer(var)3415546Slinton LOCAL SYM *tcontainer(var)
3425546Slinton NODE *var;
3435546Slinton {
3445546Slinton 	NODE *p;
3455546Slinton 
3465546Slinton 	p = var;
3475546Slinton 	while (p->op != O_NAME) {
3485546Slinton 		if (isleaf(p->op)) {
3495546Slinton 			panic("unexpected op %d in tcontainer", p->op);
3505546Slinton 			/* NOTREACHED */
3515546Slinton 		}
3525546Slinton 		p = p->left;
3535546Slinton 	}
3545546Slinton 	return container(p->nameval);
3555546Slinton }
356