Commit | Line | Data |
---|---|---|
2a24676e DF |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
4298caf5 | 6 | |
2a24676e DF |
7 | #ifndef lint |
8 | static char sccsid[] = "@(#)events.c 5.1 (Berkeley) %G%"; | |
9 | #endif not lint | |
0022c355 ML |
10 | |
11 | static char rcsid[] = "$Header: events.c,v 1.5 84/12/26 10:39:26 linton Exp $"; | |
4298caf5 ML |
12 | |
13 | /* | |
14 | * Event/breakpoint managment. | |
15 | */ | |
16 | ||
17 | #include "defs.h" | |
18 | #include "events.h" | |
19 | #include "main.h" | |
20 | #include "symbols.h" | |
21 | #include "tree.h" | |
22 | #include "eval.h" | |
23 | #include "source.h" | |
24 | #include "mappings.h" | |
2fd0f574 | 25 | #include "runtime.h" |
4298caf5 ML |
26 | #include "process.h" |
27 | #include "machine.h" | |
28 | #include "lists.h" | |
29 | ||
30 | #ifndef public | |
31 | typedef struct Event *Event; | |
32 | typedef struct Breakpoint *Breakpoint; | |
33 | ||
0022c355 ML |
34 | boolean inst_tracing; |
35 | boolean single_stepping; | |
36 | boolean isstopped; | |
4298caf5 ML |
37 | |
38 | #include "symbols.h" | |
39 | ||
40 | Symbol linesym; | |
41 | Symbol procsym; | |
42 | Symbol pcsym; | |
43 | Symbol retaddrsym; | |
44 | ||
45 | #define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist) | |
46 | #define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist) | |
47 | ||
48 | #endif | |
49 | ||
50 | struct Event { | |
51 | unsigned int id; | |
0022c355 | 52 | boolean temporary; |
4298caf5 ML |
53 | Node condition; |
54 | Cmdlist actions; | |
55 | }; | |
56 | ||
57 | struct Breakpoint { | |
58 | Event event; | |
59 | Address bpaddr; | |
60 | Lineno bpline; | |
61 | Cmdlist actions; | |
0022c355 | 62 | boolean temporary; |
4298caf5 ML |
63 | }; |
64 | ||
65 | typedef List Eventlist; | |
66 | typedef List Bplist; | |
67 | ||
68 | #define eventlist_append(event, el) list_append(list_item(event), nil, el) | |
69 | #define bplist_append(bp, bl) list_append(list_item(bp), nil, bl) | |
70 | ||
71 | private Eventlist eventlist; /* list of active events */ | |
72 | private Bplist bplist; /* list of active breakpoints */ | |
0022c355 ML |
73 | private Event curevent; /* most recently created event */ |
74 | private integer eventid; /* id number of current event */ | |
75 | private integer trid; /* id number of current trace */ | |
4298caf5 ML |
76 | |
77 | typedef struct Trcmd { | |
78 | Integer trid; | |
79 | Event event; | |
80 | Cmdlist cmdlist; | |
81 | } *Trcmd; | |
82 | ||
83 | private List eachline; /* commands to execute after each line */ | |
84 | private List eachinst; /* commands to execute after each instruction */ | |
85 | ||
86 | private Breakpoint bp_alloc(); | |
87 | ||
88 | /* | |
89 | * Initialize breakpoint information. | |
90 | */ | |
91 | ||
92 | private Symbol builtinsym(str, class, type) | |
93 | String str; | |
94 | Symclass class; | |
95 | Symbol type; | |
96 | { | |
97 | Symbol s; | |
98 | ||
99 | s = insert(identname(str, true)); | |
100 | s->language = findlanguage(".s"); | |
101 | s->class = class; | |
102 | s->type = type; | |
103 | return s; | |
104 | } | |
105 | ||
106 | public bpinit() | |
107 | { | |
108 | linesym = builtinsym("$line", VAR, t_int); | |
109 | procsym = builtinsym("$proc", PROC, nil); | |
110 | pcsym = lookup(identname("$pc", true)); | |
111 | if (pcsym == nil) { | |
112 | panic("can't find $pc"); | |
113 | } | |
114 | retaddrsym = builtinsym("$retaddr", VAR, t_int); | |
115 | eventlist = list_alloc(); | |
116 | bplist = list_alloc(); | |
117 | eachline = list_alloc(); | |
118 | eachinst = list_alloc(); | |
119 | } | |
120 | ||
121 | /* | |
122 | * Trap an event and do the associated commands when it occurs. | |
123 | */ | |
124 | ||
125 | public Event event_alloc(istmp, econd, cmdlist) | |
0022c355 | 126 | boolean istmp; |
4298caf5 ML |
127 | Node econd; |
128 | Cmdlist cmdlist; | |
129 | { | |
130 | register Event e; | |
131 | ||
132 | e = new(Event); | |
0022c355 ML |
133 | ++eventid; |
134 | e->id = eventid; | |
4298caf5 ML |
135 | e->temporary = istmp; |
136 | e->condition = econd; | |
137 | e->actions = cmdlist; | |
138 | eventlist_append(e, eventlist); | |
0022c355 | 139 | curevent = e; |
4298caf5 ML |
140 | translate(e); |
141 | return e; | |
142 | } | |
143 | ||
144 | /* | |
145 | * Delete the event with the given id. | |
2fd0f574 | 146 | * Returns whether it's successful or not. |
4298caf5 ML |
147 | */ |
148 | ||
2fd0f574 | 149 | public boolean delevent (id) |
4298caf5 ML |
150 | unsigned int id; |
151 | { | |
152 | Event e; | |
153 | Breakpoint bp; | |
154 | Trcmd t; | |
2fd0f574 | 155 | boolean found; |
4298caf5 | 156 | |
2fd0f574 | 157 | found = false; |
4298caf5 ML |
158 | foreach (Event, e, eventlist) |
159 | if (e->id == id) { | |
2fd0f574 | 160 | found = true; |
4298caf5 ML |
161 | foreach (Breakpoint, bp, bplist) |
162 | if (bp->event == e) { | |
0022c355 ML |
163 | if (tracebpts) { |
164 | printf("deleting breakpoint at 0x%x\n", bp->bpaddr); | |
165 | fflush(stdout); | |
166 | } | |
4298caf5 ML |
167 | list_delete(list_curitem(bplist), bplist); |
168 | } | |
169 | endfor | |
2fd0f574 | 170 | list_delete(list_curitem(eventlist), eventlist); |
4298caf5 ML |
171 | break; |
172 | } | |
173 | endfor | |
174 | foreach (Trcmd, t, eachline) | |
175 | if (t->event->id == id) { | |
2fd0f574 | 176 | found = true; |
4298caf5 ML |
177 | printrmtr(t); |
178 | list_delete(list_curitem(eachline), eachline); | |
179 | } | |
180 | endfor | |
181 | foreach (Trcmd, t, eachinst) | |
182 | if (t->event->id == id) { | |
2fd0f574 | 183 | found = true; |
4298caf5 ML |
184 | printrmtr(t); |
185 | list_delete(list_curitem(eachinst), eachinst); | |
186 | } | |
187 | endfor | |
188 | if (list_size(eachinst) == 0) { | |
189 | inst_tracing = false; | |
190 | if (list_size(eachline) == 0) { | |
191 | single_stepping = false; | |
192 | } | |
193 | } | |
2fd0f574 | 194 | return found; |
4298caf5 ML |
195 | } |
196 | ||
197 | /* | |
198 | * Translate an event into the appropriate breakpoints and actions. | |
199 | * While we're at it, turn on the breakpoints if the condition is true. | |
200 | */ | |
201 | ||
202 | private translate(e) | |
203 | Event e; | |
204 | { | |
205 | Breakpoint bp; | |
206 | Symbol s; | |
207 | Node place; | |
208 | Lineno line; | |
209 | Address addr; | |
210 | ||
211 | checkref(e->condition); | |
212 | switch (e->condition->op) { | |
213 | case O_EQ: | |
214 | if (e->condition->value.arg[0]->op == O_SYM) { | |
215 | s = e->condition->value.arg[0]->value.sym; | |
216 | place = e->condition->value.arg[1]; | |
217 | if (s == linesym) { | |
218 | if (place->op == O_QLINE) { | |
219 | line = place->value.arg[1]->value.lcon; | |
2fd0f574 | 220 | addr = objaddr(line, place->value.arg[0]->value.scon); |
4298caf5 ML |
221 | } else { |
222 | eval(place); | |
223 | line = pop(long); | |
224 | addr = objaddr(line, cursource); | |
225 | } | |
226 | if (addr == NOADDR) { | |
2fd0f574 SL |
227 | if (not delevent(e->id)) { |
228 | printf("!! dbx.translate: can't undo event %d?\n", | |
229 | e->id); | |
230 | } | |
4298caf5 ML |
231 | beginerrmsg(); |
232 | fprintf(stderr, "no executable code at line "); | |
233 | prtree(stderr, place); | |
234 | enderrmsg(); | |
235 | } | |
236 | bp = bp_alloc(e, addr, line, e->actions); | |
237 | } else if (s == procsym) { | |
238 | eval(place); | |
239 | s = pop(Symbol); | |
240 | bp = bp_alloc(e, codeloc(s), 0, e->actions); | |
241 | if (isactive(s) and pc != codeloc(program)) { | |
242 | evalcmdlist(e->actions); | |
243 | } | |
244 | } else if (s == pcsym) { | |
245 | eval(place); | |
246 | bp = bp_alloc(e, pop(Address), 0, e->actions); | |
247 | } else { | |
248 | condbp(e); | |
249 | } | |
250 | } else { | |
251 | condbp(e); | |
252 | } | |
253 | break; | |
254 | ||
255 | /* | |
256 | * These should be handled specially. | |
257 | * But for now I'm ignoring the problem. | |
258 | */ | |
259 | case O_AND: | |
260 | case O_OR: | |
261 | default: | |
262 | condbp(e); | |
263 | break; | |
264 | } | |
265 | } | |
266 | ||
267 | /* | |
268 | * Create a breakpoint for a condition that cannot be pinpointed | |
269 | * to happening at a particular address, but one for which we | |
270 | * must single step and check the condition after each statement. | |
271 | */ | |
272 | ||
273 | private condbp(e) | |
274 | Event e; | |
275 | { | |
276 | Symbol p; | |
277 | Breakpoint bp; | |
278 | Cmdlist actions; | |
279 | ||
280 | p = tcontainer(e->condition); | |
281 | if (p == nil) { | |
282 | p = program; | |
283 | } | |
284 | actions = buildcmdlist(build(O_IF, e->condition, e->actions)); | |
285 | actions = buildcmdlist(build(O_TRACEON, false, actions)); | |
286 | bp = bp_alloc(e, codeloc(p), 0, actions); | |
287 | } | |
288 | ||
289 | /* | |
290 | * Determine the deepest nested subprogram that still contains | |
291 | * all elements in the given expression. | |
292 | */ | |
293 | ||
294 | public Symbol tcontainer(exp) | |
295 | Node exp; | |
296 | { | |
297 | Integer i; | |
298 | Symbol s, t, u, v; | |
299 | ||
300 | checkref(exp); | |
301 | s = nil; | |
302 | if (exp->op == O_SYM) { | |
303 | s = container(exp->value.sym); | |
304 | } else if (not isleaf(exp->op)) { | |
305 | for (i = 0; i < nargs(exp->op); i++) { | |
306 | t = tcontainer(exp->value.arg[i]); | |
307 | if (t != nil) { | |
308 | if (s == nil) { | |
309 | s = t; | |
310 | } else { | |
311 | u = s; | |
312 | v = t; | |
313 | while (u != v and u != nil) { | |
314 | u = container(u); | |
315 | v = container(v); | |
316 | } | |
317 | if (u == nil) { | |
318 | panic("bad ancestry for \"%s\"", symname(s)); | |
319 | } else { | |
320 | s = u; | |
321 | } | |
322 | } | |
323 | } | |
324 | } | |
325 | } | |
326 | return s; | |
327 | } | |
328 | ||
e1bc702e ML |
329 | /* |
330 | * Determine if the given function can be executed at full speed. | |
331 | * This can only be done if there are no breakpoints within the function. | |
332 | */ | |
333 | ||
0022c355 | 334 | public boolean canskip(f) |
e1bc702e ML |
335 | Symbol f; |
336 | { | |
337 | Breakpoint p; | |
0022c355 | 338 | boolean ok; |
e1bc702e ML |
339 | |
340 | ok = true; | |
341 | foreach (Breakpoint, p, bplist) | |
342 | if (whatblock(p->bpaddr) == f) { | |
343 | ok = false; | |
344 | break; | |
345 | } | |
346 | endfor | |
347 | return ok; | |
348 | } | |
349 | ||
4298caf5 ML |
350 | /* |
351 | * Print out what's currently being traced by looking at | |
352 | * the currently active events. | |
353 | * | |
354 | * Some convolution here to translate internal representation | |
355 | * of events back into something more palatable. | |
356 | */ | |
357 | ||
358 | public status() | |
359 | { | |
360 | Event e; | |
4298caf5 ML |
361 | |
362 | foreach (Event, e, eventlist) | |
363 | if (not e->temporary) { | |
e1bc702e | 364 | printevent(e); |
4298caf5 ML |
365 | } |
366 | endfor | |
367 | } | |
368 | ||
e1bc702e ML |
369 | public printevent(e) |
370 | Event e; | |
371 | { | |
372 | Command cmd; | |
373 | ||
374 | if (not isredirected()) { | |
2fd0f574 | 375 | printeventid(e->id); |
e1bc702e ML |
376 | } |
377 | cmd = list_element(Command, list_head(e->actions)); | |
378 | if (cmd->op == O_PRINTCALL) { | |
379 | printf("trace "); | |
380 | printname(stdout, cmd->value.sym); | |
381 | } else { | |
382 | if (list_size(e->actions) > 1) { | |
383 | printf("{ "); | |
384 | } | |
385 | foreach (Command, cmd, e->actions) | |
386 | printcmd(stdout, cmd); | |
387 | if (not list_islast()) { | |
388 | printf("; "); | |
389 | } | |
390 | endfor | |
391 | if (list_size(e->actions) > 1) { | |
392 | printf(" }"); | |
393 | } | |
394 | printcond(e->condition); | |
395 | } | |
396 | printf("\n"); | |
397 | } | |
398 | ||
2fd0f574 SL |
399 | private printeventid (id) |
400 | integer id; | |
401 | { | |
402 | printf("[%d] ", id); | |
403 | } | |
404 | ||
4298caf5 ML |
405 | /* |
406 | * Print out a condition. | |
407 | */ | |
408 | ||
409 | private printcond(cond) | |
410 | Node cond; | |
411 | { | |
412 | Symbol s; | |
413 | Node place; | |
414 | ||
415 | if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) { | |
416 | s = cond->value.arg[0]->value.sym; | |
417 | place = cond->value.arg[1]; | |
418 | if (s == procsym) { | |
419 | if (place->value.sym != program) { | |
420 | printf(" in "); | |
421 | printname(stdout, place->value.sym); | |
422 | } | |
423 | } else if (s == linesym) { | |
424 | printf(" at "); | |
425 | prtree(stdout, place); | |
426 | } else if (s == pcsym or s == retaddrsym) { | |
427 | printf("i at "); | |
428 | prtree(stdout, place); | |
429 | } else { | |
430 | printf(" when "); | |
431 | prtree(stdout, cond); | |
432 | } | |
433 | } else { | |
434 | printf(" when "); | |
435 | prtree(stdout, cond); | |
436 | } | |
437 | } | |
438 | ||
439 | /* | |
440 | * Add a breakpoint to the list and return it. | |
441 | */ | |
442 | ||
443 | private Breakpoint bp_alloc(e, addr, line, actions) | |
444 | Event e; | |
445 | Address addr; | |
446 | Lineno line; | |
447 | Cmdlist actions; | |
448 | { | |
449 | register Breakpoint p; | |
450 | ||
451 | p = new(Breakpoint); | |
452 | p->event = e; | |
453 | p->bpaddr = addr; | |
454 | p->bpline = line; | |
455 | p->actions = actions; | |
0022c355 ML |
456 | p->temporary = false; |
457 | if (tracebpts) { | |
458 | if (e == nil) { | |
459 | printf("new bp at 0x%x for event ??\n", addr, e->id); | |
460 | } else { | |
461 | printf("new bp at 0x%x for event %d\n", addr, e->id); | |
462 | } | |
463 | fflush(stdout); | |
464 | } | |
4298caf5 ML |
465 | bplist_append(p, bplist); |
466 | return p; | |
467 | } | |
468 | ||
469 | /* | |
470 | * Free all storage in the event and breakpoint tables. | |
471 | */ | |
472 | ||
473 | public bpfree() | |
474 | { | |
475 | register Event e; | |
476 | ||
477 | fixbps(); | |
478 | foreach (Event, e, eventlist) | |
2fd0f574 SL |
479 | if (not delevent(e->id)) { |
480 | printf("!! dbx.bpfree: can't delete event %d\n", e->id); | |
481 | } | |
4298caf5 ML |
482 | list_delete(list_curitem(eventlist), eventlist); |
483 | endfor | |
484 | } | |
485 | ||
486 | /* | |
487 | * Determine if the program stopped at a known breakpoint | |
488 | * and if so do the associated commands. | |
489 | */ | |
490 | ||
0022c355 | 491 | public boolean bpact() |
4298caf5 ML |
492 | { |
493 | register Breakpoint p; | |
0022c355 | 494 | boolean found; |
2fd0f574 | 495 | integer eventId; |
4298caf5 ML |
496 | |
497 | found = false; | |
498 | foreach (Breakpoint, p, bplist) | |
499 | if (p->bpaddr == pc) { | |
0022c355 ML |
500 | if (tracebpts) { |
501 | printf("breakpoint for event %d found at location 0x%x\n", | |
502 | p->event->id, pc); | |
503 | } | |
4298caf5 | 504 | found = true; |
0022c355 ML |
505 | if (p->event->temporary) { |
506 | if (not delevent(p->event->id)) { | |
507 | printf("!! dbx.bpact: can't find event %d\n", | |
508 | p->event->id); | |
509 | } | |
510 | } | |
4298caf5 | 511 | evalcmdlist(p->actions); |
0022c355 ML |
512 | if (isstopped) { |
513 | eventId = p->event->id; | |
514 | } | |
515 | if (p->temporary) { | |
516 | list_delete(list_curitem(bplist), bplist); | |
517 | } | |
4298caf5 ML |
518 | } |
519 | endfor | |
520 | if (isstopped) { | |
2fd0f574 SL |
521 | if (found) { |
522 | printeventid(eventId); | |
523 | } | |
4298caf5 ML |
524 | printstatus(); |
525 | } | |
526 | fflush(stdout); | |
527 | return found; | |
528 | } | |
529 | ||
530 | /* | |
531 | * Begin single stepping and executing the given commands after each step. | |
532 | * If the first argument is true step by instructions, otherwise | |
533 | * step by source lines. | |
534 | * | |
535 | * We automatically set a breakpoint at the end of the current procedure | |
536 | * to turn off the given tracing. | |
537 | */ | |
538 | ||
539 | public traceon(inst, event, cmdlist) | |
0022c355 | 540 | boolean inst; |
4298caf5 ML |
541 | Event event; |
542 | Cmdlist cmdlist; | |
543 | { | |
544 | register Trcmd trcmd; | |
0022c355 | 545 | Breakpoint bp; |
4298caf5 | 546 | Cmdlist actions; |
e1bc702e | 547 | Address ret; |
0022c355 | 548 | Event e; |
4298caf5 | 549 | |
0022c355 ML |
550 | if (event == nil) { |
551 | e = curevent; | |
552 | } else { | |
553 | e = event; | |
554 | } | |
4298caf5 | 555 | trcmd = new(Trcmd); |
0022c355 ML |
556 | ++trid; |
557 | trcmd->trid = trid; | |
558 | trcmd->event = e; | |
4298caf5 ML |
559 | trcmd->cmdlist = cmdlist; |
560 | single_stepping = true; | |
561 | if (inst) { | |
562 | inst_tracing = true; | |
563 | list_append(list_item(trcmd), nil, eachinst); | |
564 | } else { | |
565 | list_append(list_item(trcmd), nil, eachline); | |
566 | } | |
e1bc702e ML |
567 | ret = return_addr(); |
568 | if (ret != 0) { | |
0022c355 ML |
569 | actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid)); |
570 | bp = bp_alloc(e, (Address) ret, 0, actions); | |
571 | bp->temporary = true; | |
572 | } | |
573 | if (tracebpts) { | |
574 | printf("adding trace %d for event %d\n", trcmd->trid, e->id); | |
4298caf5 ML |
575 | } |
576 | } | |
577 | ||
578 | /* | |
579 | * Turn off some kind of tracing. | |
580 | * Strictly an internal command, this cannot be invoked by the user. | |
581 | */ | |
582 | ||
583 | public traceoff(id) | |
584 | Integer id; | |
585 | { | |
586 | register Trcmd t; | |
0022c355 | 587 | register boolean found; |
4298caf5 ML |
588 | |
589 | found = false; | |
590 | foreach (Trcmd, t, eachline) | |
591 | if (t->trid == id) { | |
592 | printrmtr(t); | |
593 | list_delete(list_curitem(eachline), eachline); | |
594 | found = true; | |
595 | break; | |
596 | } | |
597 | endfor | |
598 | if (not found) { | |
599 | foreach (Trcmd, t, eachinst) | |
600 | if (t->event->id == id) { | |
601 | printrmtr(t); | |
602 | list_delete(list_curitem(eachinst), eachinst); | |
603 | found = true; | |
604 | break; | |
605 | } | |
606 | endfor | |
607 | if (not found) { | |
0022c355 ML |
608 | beginerrmsg(); |
609 | fprintf(stderr, "[internal error: trace id %d not found]\n", id); | |
4298caf5 ML |
610 | } |
611 | } | |
612 | if (list_size(eachinst) == 0) { | |
613 | inst_tracing = false; | |
614 | if (list_size(eachline) == 0) { | |
615 | single_stepping = false; | |
616 | } | |
617 | } | |
618 | } | |
619 | ||
620 | /* | |
621 | * If breakpoints are being traced, note that a Trcmd is being deleted. | |
622 | */ | |
623 | ||
624 | private printrmtr(t) | |
625 | Trcmd t; | |
626 | { | |
0022c355 ML |
627 | if (tracebpts) { |
628 | printf("removing trace %d", t->trid); | |
629 | if (t->event != nil) { | |
630 | printf(" for event %d", t->event->id); | |
631 | } | |
632 | printf("\n"); | |
252b7653 | 633 | } |
4298caf5 ML |
634 | } |
635 | ||
636 | /* | |
637 | * Print out news during single step tracing. | |
638 | */ | |
639 | ||
640 | public printnews() | |
641 | { | |
642 | register Trcmd t; | |
643 | ||
644 | foreach (Trcmd, t, eachline) | |
645 | evalcmdlist(t->cmdlist); | |
646 | endfor | |
647 | foreach (Trcmd, t, eachinst) | |
648 | evalcmdlist(t->cmdlist); | |
649 | endfor | |
650 | bpact(); | |
651 | } | |
652 | ||
653 | /* | |
654 | * A procedure call/return has occurred while single-stepping, | |
655 | * note it if we're tracing lines. | |
656 | */ | |
657 | ||
0022c355 | 658 | private boolean chklist(); |
4298caf5 ML |
659 | |
660 | public callnews(iscall) | |
0022c355 | 661 | boolean iscall; |
4298caf5 ML |
662 | { |
663 | if (not chklist(eachline, iscall)) { | |
664 | chklist(eachinst, iscall); | |
665 | } | |
666 | } | |
667 | ||
0022c355 | 668 | private boolean chklist(list, iscall) |
4298caf5 | 669 | List list; |
0022c355 | 670 | boolean iscall; |
4298caf5 ML |
671 | { |
672 | register Trcmd t; | |
673 | register Command cmd; | |
674 | ||
2fd0f574 | 675 | setcurfunc(whatblock(pc)); |
4298caf5 ML |
676 | foreach (Trcmd, t, list) |
677 | foreach (Command, cmd, t->cmdlist) | |
678 | if (cmd->op == O_PRINTSRCPOS and | |
679 | (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) { | |
4298caf5 ML |
680 | if (iscall) { |
681 | printentry(curfunc); | |
682 | } else { | |
683 | printexit(curfunc); | |
684 | } | |
685 | return true; | |
686 | } | |
687 | endfor | |
688 | endfor | |
689 | return false; | |
690 | } | |
691 | ||
692 | /* | |
693 | * When tracing variables we keep a copy of their most recent value | |
694 | * and compare it to the current one each time a breakpoint occurs. | |
695 | * MAXTRSIZE is the maximum size variable we allow. | |
696 | */ | |
697 | ||
698 | #define MAXTRSIZE 512 | |
699 | ||
700 | /* | |
701 | * List of variables being watched. | |
702 | */ | |
703 | ||
704 | typedef struct Trinfo *Trinfo; | |
705 | ||
706 | struct Trinfo { | |
707 | Node variable; | |
708 | Address traddr; | |
709 | Symbol trblock; | |
710 | char *trvalue; | |
711 | }; | |
712 | ||
713 | private List trinfolist; | |
714 | ||
715 | /* | |
716 | * Find the trace information record associated with the given record. | |
717 | * If there isn't one then create it and add it to the list. | |
718 | */ | |
719 | ||
720 | private Trinfo findtrinfo(p) | |
721 | Node p; | |
722 | { | |
723 | register Trinfo tp; | |
0022c355 | 724 | boolean isnew; |
4298caf5 ML |
725 | |
726 | isnew = true; | |
727 | if (trinfolist == nil) { | |
728 | trinfolist = list_alloc(); | |
729 | } else { | |
730 | foreach (Trinfo, tp, trinfolist) | |
731 | if (tp->variable == p) { | |
732 | isnew = false; | |
733 | break; | |
734 | } | |
735 | endfor | |
736 | } | |
737 | if (isnew) { | |
738 | if (tracebpts) { | |
739 | printf("adding trinfo for \""); | |
740 | prtree(stdout, p); | |
741 | printf("\"\n"); | |
742 | } | |
743 | tp = new(Trinfo); | |
744 | tp->variable = p; | |
745 | tp->traddr = lval(p); | |
746 | tp->trvalue = nil; | |
747 | list_append(list_item(tp), nil, trinfolist); | |
748 | } | |
749 | return tp; | |
750 | } | |
751 | ||
752 | /* | |
753 | * Print out the value of a variable if it has changed since the | |
754 | * last time we checked. | |
755 | */ | |
756 | ||
757 | public printifchanged(p) | |
758 | Node p; | |
759 | { | |
760 | register Trinfo tp; | |
761 | register int n; | |
762 | char buff[MAXTRSIZE]; | |
0022c355 | 763 | Filename curfile; |
4298caf5 | 764 | static Lineno prevline; |
0022c355 | 765 | static Filename prevfile; |
4298caf5 ML |
766 | |
767 | tp = findtrinfo(p); | |
768 | n = size(p->nodetype); | |
0022c355 ML |
769 | dread(buff, tp->traddr, n); |
770 | curfile = srcfilename(pc); | |
4298caf5 ML |
771 | if (tp->trvalue == nil) { |
772 | tp->trvalue = newarr(char, n); | |
773 | mov(buff, tp->trvalue, n); | |
774 | mov(buff, sp, n); | |
775 | sp += n; | |
0022c355 | 776 | printf("initially (at line %d in \"%s\"):\t", curline, curfile); |
4298caf5 ML |
777 | prtree(stdout, p); |
778 | printf(" = "); | |
779 | printval(p->nodetype); | |
780 | putchar('\n'); | |
781 | } else if (cmp(tp->trvalue, buff, n) != 0) { | |
782 | mov(buff, tp->trvalue, n); | |
783 | mov(buff, sp, n); | |
784 | sp += n; | |
0022c355 | 785 | printf("after line %d in \"%s\":\t", prevline, prevfile); |
4298caf5 ML |
786 | prtree(stdout, p); |
787 | printf(" = "); | |
788 | printval(p->nodetype); | |
789 | putchar('\n'); | |
790 | } | |
791 | prevline = curline; | |
0022c355 | 792 | prevfile = curfile; |
4298caf5 ML |
793 | } |
794 | ||
795 | /* | |
796 | * Stop if the value of the given expression has changed. | |
797 | */ | |
798 | ||
799 | public stopifchanged(p) | |
800 | Node p; | |
801 | { | |
802 | register Trinfo tp; | |
803 | register int n; | |
804 | char buff[MAXTRSIZE]; | |
805 | static Lineno prevline; | |
806 | ||
807 | tp = findtrinfo(p); | |
808 | n = size(p->nodetype); | |
809 | dread(buff, tp->traddr, n); | |
810 | if (tp->trvalue == nil) { | |
811 | tp->trvalue = newarr(char, n); | |
812 | mov(buff, tp->trvalue, n); | |
813 | isstopped = true; | |
814 | } else if (cmp(tp->trvalue, buff, n) != 0) { | |
815 | mov(buff, tp->trvalue, n); | |
2fd0f574 SL |
816 | mov(buff, sp, n); |
817 | sp += n; | |
818 | printf("after line %d:\t", prevline); | |
819 | prtree(stdout, p); | |
820 | printf(" = "); | |
821 | printval(p->nodetype); | |
822 | putchar('\n'); | |
4298caf5 ML |
823 | isstopped = true; |
824 | } | |
825 | prevline = curline; | |
826 | } | |
827 | ||
828 | /* | |
829 | * Free the tracing table. | |
830 | */ | |
831 | ||
832 | public trfree() | |
833 | { | |
834 | register Trinfo tp; | |
835 | ||
836 | foreach (Trinfo, tp, trinfolist) | |
837 | dispose(tp->trvalue); | |
838 | dispose(tp); | |
839 | list_delete(list_curitem(trinfolist), trinfolist); | |
840 | endfor | |
841 | } | |
842 | ||
843 | /* | |
844 | * Fix up breakpoint information before continuing execution. | |
845 | * | |
846 | * It's necessary to destroy events and breakpoints that were created | |
847 | * temporarily and still exist because the program terminated abnormally. | |
848 | */ | |
849 | ||
850 | public fixbps() | |
851 | { | |
852 | register Event e; | |
853 | register Trcmd t; | |
854 | ||
855 | single_stepping = false; | |
856 | inst_tracing = false; | |
857 | trfree(); | |
858 | foreach (Event, e, eventlist) | |
859 | if (e->temporary) { | |
2fd0f574 SL |
860 | if (not delevent(e->id)) { |
861 | printf("!! dbx.fixbps: can't find event %d\n", e->id); | |
862 | } | |
4298caf5 ML |
863 | } |
864 | endfor | |
865 | foreach (Trcmd, t, eachline) | |
866 | printrmtr(t); | |
867 | list_delete(list_curitem(eachline), eachline); | |
868 | endfor | |
869 | foreach (Trcmd, t, eachinst) | |
870 | printrmtr(t); | |
871 | list_delete(list_curitem(eachinst), eachinst); | |
872 | endfor | |
873 | } | |
874 | ||
875 | /* | |
876 | * Set all breakpoints in object code. | |
877 | */ | |
878 | ||
879 | public setallbps() | |
880 | { | |
881 | register Breakpoint p; | |
882 | ||
883 | foreach (Breakpoint, p, bplist) | |
884 | setbp(p->bpaddr); | |
885 | endfor | |
886 | } | |
887 | ||
888 | /* | |
889 | * Undo damage done by "setallbps". | |
890 | */ | |
891 | ||
892 | public unsetallbps() | |
893 | { | |
894 | register Breakpoint p; | |
895 | ||
896 | foreach (Breakpoint, p, bplist) | |
897 | unsetbp(p->bpaddr); | |
898 | endfor | |
899 | } |