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