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