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[] = "@(#)bpact.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 /*
13  * Routines for doing the right thing when a breakpoint is reached.
14  */
15 
16 #include "defs.h"
17 #include "breakpoint.h"
18 #include "sym.h"
19 #include "tree.h"
20 #include "source.h"
21 #include "mappings.h"
22 #include "runtime.h"
23 #include "process.h"
24 #include "machine.h"
25 #include "main.h"
26 #include "bp.rep"
27 #include "tree/tree.rep"
28 
29 typedef enum { SAVE, NOSAVE } SAVEBP;
30 
31 LOCAL SAVEBP handlebp();
32 
33 /*
34  * A "delayed" breakpoint is one that has an action involving execution
35  * of code, e.g. at a CALL we want to step from the beginning of the
36  * procedure to the first line before printing parameters.
37  */
38 
39 LOCAL short delayed;
40 
41 #define NONE 0
42 #define DELAY_CALL 1
43 #define DELAY_STOP 2
44 
45 /*
46  * Take action at a breakpoint; if it's not a breakpoint return FALSE.
47  *
48  * As we go through the list of breakpoints, we have to remember
49  * the previous one so that "handlebp" can delete breakpoints on
50  * the fly if necessary.
51  *
52  * If the breakpoint is a STOP_BP, handlebp will set "isstopped".  After
53  * going through the loop, bpact checks if "isstopped" is set and calls
54  * printstatus if it is.  This is so multiple breakpoints at the same
55  * address, one of which is a STOP_BP, still work.
56  */
57 
58 #define isswitch(bptype) ( \
59 	bptype == ALL_ON || bptype == ALL_OFF || \
60 	bptype == TERM_ON || bptype == TERM_OFF || \
61 	bptype == BLOCK_ON || bptype == BLOCK_OFF || \
62 	bptype == STOP_ON || bptype == STOP_OFF \
63 )
64 
65 BOOLEAN bpact()
66 {
67 	register BPINFO *p;
68 	BPINFO *prev, *next;
69 	BOOLEAN found;
70 	ADDRESS oldpc;
71 
72 	delayed = NONE;
73 	found = FALSE;
74 	prev = NIL;
75 	for (p = bphead; p != NIL; p = next) {
76 		next = p->bpnext;
77 		if (p->bpaddr == pc) {
78 			prbpfound(p);
79 			found = TRUE;
80 			if (p->bpcond == NIL || isswitch(p->bptype) || cond(p->bpcond)) {
81 				prbphandled();
82 				if (handlebp(p) == NOSAVE) {
83 					prbpnosave();
84 					if (prev == NIL) {
85 						bphead = next;
86 					} else {
87 						prev->bpnext = next;
88 					}
89 					dispose(p);
90 				} else {
91 					prbpsave();
92 					prev = p;
93 				}
94 			} else {
95 				prev = p;
96 			}
97 		} else {
98 			prev = p;
99 		}
100 	}
101 	if (delayed != NONE) {
102 		oldpc = pc;
103 		runtofirst();
104 		if ((delayed&DELAY_CALL) == DELAY_CALL) {
105 			SYM *s, *t;
106 
107 			s = curfunc;
108 			t = whatblock(return_addr());
109 			if (t == NIL) {
110 				panic("can't find block for caller addr %d", caller_addr());
111 			}
112 			printcall(s, t);
113 			addbp(return_addr(), RETURN, s, NIL, NIL, 0);
114 		}
115 		if (pc != oldpc) {
116 			bpact();
117 		}
118 		if (isstopped) {
119 			printstatus();
120 		}
121 	} else {
122 		if (isstopped) {
123 			printstatus();
124 		}
125 	}
126 	fflush(stdout);
127 	return(found);
128 }
129 
130 /*
131  * Handle an expected breakpoint appropriately, return whether
132  * or not to save the breakpoint.
133  */
134 
135 LOCAL SAVEBP handlebp(p)
136 BPINFO *p;
137 {
138 	register SYM *s, *t;
139 	SAVEBP r;
140 
141 	r = SAVE;
142 	switch(p->bptype) {
143 		case ALL_ON:
144 			curfunc = p->bpblock;
145 			addcond(TRPRINT, p->bpcond);
146 			if (p->bpline >= 0) {
147 				tracing++;
148 			} else {
149 				inst_tracing++;
150 			}
151 			addbp(return_addr(), ALL_OFF, curfunc, p->bpcond, NIL, 0);
152 			break;
153 
154 		case ALL_OFF:
155 			r = NOSAVE;
156 			if (p->bpline >= 0) {
157 				tracing--;
158 			} else {
159 				inst_tracing--;
160 			}
161 			delcond(TRPRINT, p->bpcond);
162 			curfunc = p->bpblock;
163 			break;
164 
165 		case STOP_ON:
166 			var_tracing++;
167 			curfunc = p->bpblock;
168 			if (p->bpnode != NIL) {
169 				addvar(TRSTOP, p->bpnode, p->bpcond);
170 			} else if (p->bpcond != NIL) {
171 				addcond(TRSTOP, p->bpcond);
172 			}
173 			addbp(return_addr(), STOP_OFF, curfunc, p->bpcond, p->bpnode, 0);
174 			break;
175 
176 		case STOP_OFF:
177 			r = NOSAVE;
178 			delcond(TRSTOP, p->bpcond);
179 			var_tracing--;
180 			curfunc = p->bpblock;
181 			break;
182 
183 		case INST:
184 			curline = p->bpline;
185 			if (curline > 0) {
186 				printf("trace:  ");
187 				printlines(curline, curline);
188 			} else {
189 				printf("inst trace:	");
190 				printinst(pc, pc);
191 			}
192 			break;
193 
194 		case STOP_BP:
195 			if (p->bpblock != NIL) {
196 				delayed |= DELAY_STOP;
197 				curfunc = p->bpblock;
198 			}
199 			curline = p->bpline;
200 			isstopped = TRUE;
201 			break;
202 
203 		case BLOCK_ON: {
204 			BPINFO *nbp;
205 
206 			s = p->bpblock;
207 			t = p->bpnode->nameval;
208 			nbp = newbp(codeloc(t), CALL, t, p->bpcond, NIL, 0);
209 			addbp(return_addr(), BLOCK_OFF, (SYM *) nbp, NIL, NIL, 0);
210 			break;
211 		}
212 
213 		case BLOCK_OFF: {
214 			BPINFO *oldbp;
215 
216 			r = NOSAVE;
217 			oldbp = (BPINFO *) p->bpblock;
218 			delbp(oldbp->bpid);
219 			break;
220 		}
221 
222 		case CALL:
223 			delayed |= DELAY_CALL;
224 			curfunc = p->bpblock;
225 			break;
226 
227 		case RETURN:
228 			r = NOSAVE;
229 			s = p->bpblock;
230 			printrtn(s);
231 			break;
232 
233 		case TERM_ON: {
234 			ADDRESS addr;
235 
236 			curfunc = p->bpblock;
237 			addvar(TRPRINT, p->bpnode, p->bpcond);
238 			addr = return_addr();
239 			addbp(addr, TERM_OFF, curfunc, p->bpcond, p->bpnode, 0);
240 			var_tracing++;
241 			break;
242 		}
243 
244 		case TERM_OFF:
245 			r = NOSAVE;
246 			var_tracing--;
247 			delvar(TRPRINT, p->bpnode, p->bpcond);
248 			curfunc = p->bpblock;
249 			break;
250 
251 		case AT_BP:
252 			printf("at line %d: ", p->bpline);
253 			eval(p->bpnode);
254 			prtree(p->bpnode);
255 			printf(" = ");
256 			printval(p->bpnode->nodetype);
257 			putchar('\n');
258 			break;
259 
260 		/*
261 		 * Returning from a called procedure.
262 		 * Further breakpoint processing is not done, since if
263 		 * there were any it wouldn't be associated with the call.
264 		 */
265 		case CALLPROC:
266 			procreturn(p->bpblock);
267 			delbp(p->bpid);
268 			erecover();
269 			/* NOTREACHED */
270 
271 		case END_BP:
272 			r = NOSAVE;
273 			endprogram();
274 
275 		default:
276 			panic("unknown bptype %d in cont", p->bptype);
277 			/* NOTREACHED */
278 	}
279 	return(r);
280 }
281 
282 /*
283  * Internal trace routines.
284  */
285 
286 LOCAL char *prbptype[] ={
287 	"ALL_ON", "ALL_OFF", "INST", "CALL", "RETURN", "BLOCK_ON", "BLOCK_OFF",
288 	"TERM_ON", "TERM_OFF", "AT_BP", "STOP_BP", "CALLPROC", "END_BP",
289 	"STOP_ON", "STOP_OFF",
290 };
291 
292 LOCAL prbpfound(p)
293 BPINFO *p;
294 {
295 	if (option('b')) {
296 		printf("%s breakpoint found at pc %d, line %d -- ",
297 			prbptype[(int) p->bptype], p->bpaddr, p->bpline);
298 	}
299 }
300 
301 LOCAL prbphandled()
302 {
303 	if (option('b')) {
304 		printf("handled, ");
305 	}
306 }
307 
308 LOCAL prbpnosave()
309 {
310 	if (option('b')) {
311 		printf("not saved\n");
312 		fflush(stdout);
313 	}
314 }
315 
316 LOCAL prbpsave()
317 {
318 	if (option('b')) {
319 		printf("saved\n");
320 		fflush(stdout);
321 	}
322 }
323