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