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