1*5546Slinton /* Copyright (c) 1982 Regents of the University of California */
2*5546Slinton 
3*5546Slinton static char sccsid[] = "@(#)tracestop.c 1.1 01/18/82";
4*5546Slinton 
5*5546Slinton /*
6*5546Slinton  * Handle trace and stop commands.
7*5546Slinton  */
8*5546Slinton 
9*5546Slinton #include "defs.h"
10*5546Slinton #include "breakpoint.h"
11*5546Slinton #include "sym.h"
12*5546Slinton #include "tree.h"
13*5546Slinton #include "runtime.h"
14*5546Slinton #include "source.h"
15*5546Slinton #include "object.h"
16*5546Slinton #include "mappings.h"
17*5546Slinton #include "machine.h"
18*5546Slinton #include "tree.rep"
19*5546Slinton 
20*5546Slinton LOCAL SYM *tcontainer();
21*5546Slinton 
22*5546Slinton /*
23*5546Slinton  * Process a trace/untrace command, basically checking arguments
24*5546Slinton  * and translate to a call of the appropriate routine.
25*5546Slinton  */
26*5546Slinton 
27*5546Slinton trace(cmd, exp, where, cond)
28*5546Slinton int cmd;
29*5546Slinton NODE *exp;
30*5546Slinton NODE *where;
31*5546Slinton NODE *cond;
32*5546Slinton {
33*5546Slinton 	if (exp == NIL) {
34*5546Slinton 		traceall(cmd, where, cond);
35*5546Slinton 	} else if (exp->op == O_LCON || exp->op == O_QLINE) {
36*5546Slinton 		traceinst(cmd, exp, where, cond);
37*5546Slinton 	} else if (where!=NIL && (where->op==O_QLINE || where->op==O_LCON)) {
38*5546Slinton 		traceat(cmd, exp, where, cond);
39*5546Slinton 	} else {
40*5546Slinton 		tracedata(cmd, exp, where, cond);
41*5546Slinton 	}
42*5546Slinton 	if (where != NIL) {
43*5546Slinton 		tfree(where);
44*5546Slinton 	}
45*5546Slinton }
46*5546Slinton 
47*5546Slinton /*
48*5546Slinton  * Set a breakpoint that will turn on tracing.
49*5546Slinton  *
50*5546Slinton  * A line number of 0 in the breakpoint information structure
51*5546Slinton  * means it's a normal trace.
52*5546Slinton  *
53*5546Slinton  * A line number of -1 indicates that we want to trace at the instruction
54*5546Slinton  * rather than source line level.
55*5546Slinton  *
56*5546Slinton  * If location is NIL, turn on tracing because if the user
57*5546Slinton  * has the program stopped somewhere and says "trace",
58*5546Slinton  * he/she wants to see tracing after continuing execution.
59*5546Slinton  */
60*5546Slinton 
61*5546Slinton LOCAL traceall(cmd, where, cond)
62*5546Slinton int cmd;
63*5546Slinton NODE *where;
64*5546Slinton NODE *cond;
65*5546Slinton {
66*5546Slinton 	SYM *s;
67*5546Slinton 	LINENO line;
68*5546Slinton 
69*5546Slinton 	if (where != NIL && where->op != O_NAME) {
70*5546Slinton 		error("bad location for trace");
71*5546Slinton 	}
72*5546Slinton 	if (cmd == O_TRACE) {
73*5546Slinton 		line = 0;
74*5546Slinton 	} else {
75*5546Slinton 		line = -1;
76*5546Slinton 	}
77*5546Slinton 	if (where == NIL) {
78*5546Slinton 		switch (cmd) {
79*5546Slinton 			case O_TRACE:
80*5546Slinton 				if (tracing != 0) {
81*5546Slinton 					error("already tracing lines");
82*5546Slinton 				}
83*5546Slinton 				tracing++;
84*5546Slinton 				addcond(TRPRINT, cond);
85*5546Slinton 				break;
86*5546Slinton 
87*5546Slinton 			case O_TRACEI:
88*5546Slinton 				if (inst_tracing != 0) {
89*5546Slinton 					error("already tracing instructions");
90*5546Slinton 				}
91*5546Slinton 				inst_tracing++;
92*5546Slinton 				addcond(TRPRINT, cond);
93*5546Slinton 				break;
94*5546Slinton 
95*5546Slinton 			default:
96*5546Slinton 				panic("bad cmd in traceall");
97*5546Slinton 				break;
98*5546Slinton 		}
99*5546Slinton 		s = program;
100*5546Slinton 	} else if (where->op != O_NAME) {
101*5546Slinton 		trerror("found %t, expected procedure or function", where);
102*5546Slinton 	} else {
103*5546Slinton 		s = where->nameval;
104*5546Slinton 		if (!isblock(s)) {
105*5546Slinton 			error("\"%s\" is not a procedure or function", name(s));
106*5546Slinton 		}
107*5546Slinton 	}
108*5546Slinton 	addbp(codeloc(s), ALL_ON, s, cond, NIL, line);
109*5546Slinton }
110*5546Slinton 
111*5546Slinton /*
112*5546Slinton  * Set up the appropriate breakpoint for tracing an instruction.
113*5546Slinton  */
114*5546Slinton 
115*5546Slinton LOCAL traceinst(cmd, exp, where, cond)
116*5546Slinton int cmd;
117*5546Slinton NODE *exp;
118*5546Slinton NODE *where;
119*5546Slinton NODE *cond;
120*5546Slinton {
121*5546Slinton 	LINENO line;
122*5546Slinton 	ADDRESS addr;
123*5546Slinton 
124*5546Slinton 	if (where != NIL) {
125*5546Slinton 		error("unexpected \"at\" or \"in\"");
126*5546Slinton 	}
127*5546Slinton 	if (cmd == O_TRACEI) {
128*5546Slinton 		if (exp->op == O_QLINE) {
129*5546Slinton 			addr = (ADDRESS) exp->right->lconval;
130*5546Slinton 		} else if (exp->op == O_LCON) {
131*5546Slinton 			addr = (ADDRESS) exp->lconval;
132*5546Slinton 		} else {
133*5546Slinton 			trerror("expected integer constant, found %t", exp);
134*5546Slinton 		}
135*5546Slinton 		line = -1;
136*5546Slinton 	} else {
137*5546Slinton 		if (exp->op == O_QLINE) {
138*5546Slinton 			line = (LINENO) exp->right->lconval;
139*5546Slinton 			addr = objaddr(line, exp->left->sconval);
140*5546Slinton 		} else {
141*5546Slinton 			line = (LINENO) exp->lconval;
142*5546Slinton 			addr = objaddr(line, cursource);
143*5546Slinton 		}
144*5546Slinton 		if (addr == (ADDRESS) -1) {
145*5546Slinton 			error("can't trace line %d", line);
146*5546Slinton 		}
147*5546Slinton 	}
148*5546Slinton 	tfree(exp);
149*5546Slinton 	addbp(addr, INST, NIL, cond, NIL, line);
150*5546Slinton }
151*5546Slinton 
152*5546Slinton /*
153*5546Slinton  * set a breakpoint to print an expression at a given line or address
154*5546Slinton  */
155*5546Slinton 
156*5546Slinton LOCAL traceat(cmd, exp, where, cond)
157*5546Slinton int cmd;
158*5546Slinton NODE *exp;
159*5546Slinton NODE *where;
160*5546Slinton NODE *cond;
161*5546Slinton {
162*5546Slinton 	LINENO line;
163*5546Slinton 	ADDRESS addr;
164*5546Slinton 
165*5546Slinton 	if (cmd == O_TRACEI) {
166*5546Slinton 		if (where->op != O_LCON) {
167*5546Slinton 			trerror("expected integer constant, found %t", where);
168*5546Slinton 		}
169*5546Slinton 		line = -1;
170*5546Slinton 		addr = (ADDRESS) where->lconval;
171*5546Slinton 	} else {
172*5546Slinton 		line = (LINENO) where->right->lconval;
173*5546Slinton 		addr = objaddr(line, where->left->sconval);
174*5546Slinton 		if (addr == (ADDRESS) -1) {
175*5546Slinton 			error("can't trace at line %d", line);
176*5546Slinton 		}
177*5546Slinton 	}
178*5546Slinton 	addbp(addr, AT_BP, NIL, cond, exp, line);
179*5546Slinton }
180*5546Slinton 
181*5546Slinton /*
182*5546Slinton  * Set up breakpoint for tracing data.
183*5546Slinton  *
184*5546Slinton  * The tracing of blocks lies somewhere between instruction and data;
185*5546Slinton  * it's here since a block cannot be distinguished from other terms.
186*5546Slinton  *
187*5546Slinton  * As in "traceall", if the "block" is the main program then the
188*5546Slinton  * user didn't actually specify a block.  This means we want to
189*5546Slinton  * turn tracing on ourselves because if the program is stopped
190*5546Slinton  * we want to be on regardless of whether they say "cont" or "run".
191*5546Slinton  */
192*5546Slinton 
193*5546Slinton LOCAL tracedata(cmd, exp, block, cond)
194*5546Slinton int cmd;
195*5546Slinton NODE *exp;
196*5546Slinton NODE *block;
197*5546Slinton NODE *cond;
198*5546Slinton {
199*5546Slinton 	SYM *s, *t;
200*5546Slinton 
201*5546Slinton 	if (exp->op != O_RVAL && exp->op != O_CALL) {
202*5546Slinton 		error("can't trace expressions");
203*5546Slinton 	}
204*5546Slinton 	if (block == NIL) {
205*5546Slinton 		t = tcontainer(exp->left);
206*5546Slinton 	} else if (block->op == O_NAME) {
207*5546Slinton 		t = block->nameval;
208*5546Slinton 	} else {
209*5546Slinton 		trerror("found %t, expected procedure or function", block);
210*5546Slinton 	}
211*5546Slinton 	if (exp->left->op == O_NAME) {
212*5546Slinton 		s = exp->left->nameval;
213*5546Slinton 		if (isblock(s)) {
214*5546Slinton 			addbp(codeloc(t), BLOCK_ON, t, cond, exp->left, 0);
215*5546Slinton 			if (t == program) {
216*5546Slinton 				addbp(codeloc(s), CALL, s, cond, NIL, 0);
217*5546Slinton 			}
218*5546Slinton 			return;
219*5546Slinton 		}
220*5546Slinton 	}
221*5546Slinton 	addbp(codeloc(t), TERM_ON, t, cond, exp, 0);
222*5546Slinton 	if (curfunc == t) {
223*5546Slinton 		var_tracing++;
224*5546Slinton 		addvar(TRPRINT, exp, cond);
225*5546Slinton 		addbp(return_addr(), TERM_OFF, t, cond, exp, 0);
226*5546Slinton 	}
227*5546Slinton }
228*5546Slinton 
229*5546Slinton /*
230*5546Slinton  * Setting and unsetting of stops.
231*5546Slinton  */
232*5546Slinton 
233*5546Slinton stop(cmd, exp, where, cond)
234*5546Slinton int cmd;
235*5546Slinton NODE *exp;
236*5546Slinton NODE *where;
237*5546Slinton NODE *cond;
238*5546Slinton {
239*5546Slinton 	SYM *s;
240*5546Slinton 	LINENO n;
241*5546Slinton 
242*5546Slinton 	if (exp != NIL) {
243*5546Slinton 		stopvar(cmd, exp, where, cond);
244*5546Slinton 	} else if (cond != NIL) {
245*5546Slinton 		if (where == NIL) {
246*5546Slinton 			s = program;
247*5546Slinton 		} else if (where->op == O_NAME) {
248*5546Slinton 			s = where->nameval;
249*5546Slinton 		} else {
250*5546Slinton 			error("bad location for stop");
251*5546Slinton 		}
252*5546Slinton 		n = codeloc(s);
253*5546Slinton 		addbp(n, STOP_ON, s, cond, NIL, n);
254*5546Slinton 		addcond(TRSTOP, cond);
255*5546Slinton 		var_tracing++;
256*5546Slinton 	} else if (where->op == O_NAME) {
257*5546Slinton 		s = where->nameval;
258*5546Slinton 		if (!isblock(s)) {
259*5546Slinton 			error("\"%s\" is not a procedure or function", name(s));
260*5546Slinton 		}
261*5546Slinton 		n = codeloc(s);
262*5546Slinton 		addbp(n, STOP_BP, s, cond, NIL, srcline(firstline(s)));
263*5546Slinton 	} else {
264*5546Slinton 		stopinst(cmd, where, cond);
265*5546Slinton 	}
266*5546Slinton 	if (where != NIL) {
267*5546Slinton 		tfree(where);
268*5546Slinton 	}
269*5546Slinton }
270*5546Slinton 
271*5546Slinton LOCAL stopinst(cmd, where, cond)
272*5546Slinton int cmd;
273*5546Slinton NODE *where;
274*5546Slinton NODE *cond;
275*5546Slinton {
276*5546Slinton 	LINENO line;
277*5546Slinton 	ADDRESS addr;
278*5546Slinton 
279*5546Slinton 	if (where->op != O_QLINE) {
280*5546Slinton 		error("expected line number");
281*5546Slinton 	}
282*5546Slinton 	if (cmd == O_STOP) {
283*5546Slinton 		line = (LINENO) where->right->lconval;
284*5546Slinton 		addr = objaddr(line, where->left->sconval);
285*5546Slinton 		if (addr == (ADDRESS) -1) {
286*5546Slinton 			error("can't stop at that line");
287*5546Slinton 		}
288*5546Slinton 	} else {
289*5546Slinton 		line = -1;
290*5546Slinton 		addr = (ADDRESS) where->right->lconval;
291*5546Slinton 	}
292*5546Slinton 	addbp(addr, STOP_BP, NIL, cond, NIL, line);
293*5546Slinton }
294*5546Slinton 
295*5546Slinton /*
296*5546Slinton  * Implement stopping on assignment to a variable by adding it to
297*5546Slinton  * the variable list.
298*5546Slinton  */
299*5546Slinton 
300*5546Slinton LOCAL stopvar(cmd, exp, where, cond)
301*5546Slinton int cmd;
302*5546Slinton NODE *exp;
303*5546Slinton NODE *where;
304*5546Slinton NODE *cond;
305*5546Slinton {
306*5546Slinton 	SYM *s;
307*5546Slinton 
308*5546Slinton 	if (exp->op != O_RVAL) {
309*5546Slinton 		trerror("found %t, expected variable", exp);
310*5546Slinton 	}
311*5546Slinton 	if (cmd == O_STOPI) {
312*5546Slinton 		inst_tracing++;
313*5546Slinton 	}
314*5546Slinton 	var_tracing++;
315*5546Slinton 	addvar(TRSTOP, exp, cond);
316*5546Slinton 	if (where == NIL) {
317*5546Slinton 		s = program;
318*5546Slinton 	} else if (where->op == O_NAME) {
319*5546Slinton 		s = where->nameval;
320*5546Slinton 	} else {
321*5546Slinton 		error("bad location for stop");
322*5546Slinton 	}
323*5546Slinton 	addbp(codeloc(s), STOP_ON, s, cond, exp, 0);
324*5546Slinton }
325*5546Slinton 
326*5546Slinton /*
327*5546Slinton  * Figure out the block that contains the symbols
328*5546Slinton  * in the given variable expression.
329*5546Slinton  */
330*5546Slinton 
331*5546Slinton LOCAL SYM *tcontainer(var)
332*5546Slinton NODE *var;
333*5546Slinton {
334*5546Slinton 	NODE *p;
335*5546Slinton 
336*5546Slinton 	p = var;
337*5546Slinton 	while (p->op != O_NAME) {
338*5546Slinton 		if (isleaf(p->op)) {
339*5546Slinton 			panic("unexpected op %d in tcontainer", p->op);
340*5546Slinton 			/* NOTREACHED */
341*5546Slinton 		}
342*5546Slinton 		p = p->left;
343*5546Slinton 	}
344*5546Slinton 	return container(p->nameval);
345*5546Slinton }
346