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