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