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