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