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.1 (Berkeley) 06/06/85";
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 	if (exp->op != O_RVAL && exp->op != O_CALL) {
208 		error("can't trace expressions");
209 	}
210 	if (block == NIL) {
211 		t = tcontainer(exp->left);
212 	} else if (block->op == O_NAME) {
213 		t = block->nameval;
214 	} else {
215 		trerror("found %t, expected procedure or function", block);
216 	}
217 	if (exp->left->op == O_NAME) {
218 		s = exp->left->nameval;
219 		if (isblock(s)) {
220 			addbp(codeloc(t), BLOCK_ON, t, cond, exp->left, 0);
221 			if (t == program) {
222 				addbp(codeloc(s), CALL, s, cond, NIL, 0);
223 			}
224 			return;
225 		}
226 	}
227 	addbp(codeloc(t), TERM_ON, t, cond, exp, 0);
228 	if (curfunc == t) {
229 		var_tracing++;
230 		addvar(TRPRINT, exp, cond);
231 		addbp(return_addr(), TERM_OFF, t, cond, exp, 0);
232 	}
233 }
234 
235 /*
236  * Setting and unsetting of stops.
237  */
238 
239 stop(cmd, exp, where, cond)
240 int cmd;
241 NODE *exp;
242 NODE *where;
243 NODE *cond;
244 {
245 	SYM *s;
246 	LINENO n;
247 
248 	if (exp != NIL) {
249 		stopvar(cmd, exp, where, cond);
250 	} else if (cond != NIL) {
251 		if (where == NIL) {
252 			s = program;
253 		} else if (where->op == O_NAME) {
254 			s = where->nameval;
255 		} else {
256 			error("bad location for stop");
257 		}
258 		n = codeloc(s);
259 		addbp(n, STOP_ON, s, cond, NIL, n);
260 		addcond(TRSTOP, cond);
261 		var_tracing++;
262 	} else if (where->op == O_NAME) {
263 		s = where->nameval;
264 		if (!isblock(s)) {
265 			error("\"%s\" is not a procedure or function", name(s));
266 		}
267 		n = codeloc(s);
268 		addbp(n, STOP_BP, s, cond, NIL, srcline(firstline(s)));
269 	} else {
270 		stopinst(cmd, where, cond);
271 	}
272 	if (where != NIL) {
273 		tfree(where);
274 	}
275 }
276 
277 LOCAL stopinst(cmd, where, cond)
278 int cmd;
279 NODE *where;
280 NODE *cond;
281 {
282 	LINENO line;
283 	ADDRESS addr;
284 
285 	if (where->op != O_QLINE) {
286 		error("expected line number");
287 	}
288 	if (cmd == O_STOP) {
289 		line = (LINENO) where->right->lconval;
290 		addr = objaddr(line, where->left->sconval);
291 		if (addr == (ADDRESS) -1) {
292 			error("can't stop at that line");
293 		}
294 	} else {
295 		line = -1;
296 		addr = (ADDRESS) where->right->lconval;
297 	}
298 	addbp(addr, STOP_BP, NIL, cond, NIL, line);
299 }
300 
301 /*
302  * Implement stopping on assignment to a variable by adding it to
303  * the variable list.
304  */
305 
306 LOCAL stopvar(cmd, exp, where, cond)
307 int cmd;
308 NODE *exp;
309 NODE *where;
310 NODE *cond;
311 {
312 	SYM *s;
313 
314 	if (exp->op != O_RVAL) {
315 		trerror("found %t, expected variable", exp);
316 	}
317 	if (cmd == O_STOPI) {
318 		inst_tracing++;
319 	}
320 	var_tracing++;
321 	addvar(TRSTOP, exp, cond);
322 	if (where == NIL) {
323 		s = program;
324 	} else if (where->op == O_NAME) {
325 		s = where->nameval;
326 	} else {
327 		error("bad location for stop");
328 	}
329 	addbp(codeloc(s), STOP_ON, s, cond, exp, 0);
330 }
331 
332 /*
333  * Figure out the block that contains the symbols
334  * in the given variable expression.
335  */
336 
337 LOCAL SYM *tcontainer(var)
338 NODE *var;
339 {
340 	NODE *p;
341 
342 	p = var;
343 	while (p->op != O_NAME) {
344 		if (isleaf(p->op)) {
345 			panic("unexpected op %d in tcontainer", p->op);
346 			/* NOTREACHED */
347 		}
348 		p = p->left;
349 	}
350 	return container(p->nameval);
351 }
352