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