xref: /original-bsd/old/dbx/events.c (revision f0fd5f8a)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)events.c 1.2 12/15/82";
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  * Print out what's currently being traced by looking at
306  * the currently active events.
307  *
308  * Some convolution here to translate internal representation
309  * of events back into something more palatable.
310  */
311 
312 public status()
313 {
314     Event e;
315     Command cmd;
316 
317     foreach (Event, e, eventlist)
318 	if (not e->temporary) {
319 	    if (not isredirected()) {
320 		printf("(%d) ", e->id);
321 	    }
322 	    cmd = list_element(Command, list_head(e->actions));
323 	    if (cmd->op == O_PRINTCALL) {
324 		printf("trace ");
325 		printname(stdout, cmd->value.sym);
326 	    } else {
327 		if (list_size(e->actions) > 1) {
328 		    printf("{ ");
329 		}
330 		foreach (Command, cmd, e->actions)
331 		    printcmd(stdout, cmd);
332 		    if (not list_islast()) {
333 			printf("; ");
334 		    }
335 		endfor
336 		if (list_size(e->actions) > 1) {
337 		    printf(" }");
338 		}
339 		printcond(e->condition);
340 	    }
341 	    printf("\n");
342 	}
343     endfor
344 }
345 
346 /*
347  * Print out a condition.
348  */
349 
350 private printcond(cond)
351 Node cond;
352 {
353     Symbol s;
354     Node place;
355 
356     if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) {
357 	s = cond->value.arg[0]->value.sym;
358 	place = cond->value.arg[1];
359 	if (s == procsym) {
360 	    if (place->value.sym != program) {
361 		printf(" in ");
362 		printname(stdout, place->value.sym);
363 	    }
364 	} else if (s == linesym) {
365 	    printf(" at ");
366 	    prtree(stdout, place);
367 	} else if (s == pcsym or s == retaddrsym) {
368 	    printf("i at ");
369 	    prtree(stdout, place);
370 	} else {
371 	    printf(" when ");
372 	    prtree(stdout, cond);
373 	}
374     } else {
375 	printf(" when ");
376 	prtree(stdout, cond);
377     }
378 }
379 
380 /*
381  * Add a breakpoint to the list and return it.
382  */
383 
384 private Breakpoint bp_alloc(e, addr, line, actions)
385 Event e;
386 Address addr;
387 Lineno line;
388 Cmdlist actions;
389 {
390     register Breakpoint p;
391 
392     p = new(Breakpoint);
393     p->event = e;
394     p->bpaddr = addr;
395     p->bpline = line;
396     p->actions = actions;
397     if (tracebpts) {
398 	printf("new bp at 0x%x\n", addr);
399 	fflush(stdout);
400     }
401     bplist_append(p, bplist);
402     return p;
403 }
404 
405 /*
406  * Free all storage in the event and breakpoint tables.
407  */
408 
409 public bpfree()
410 {
411     register Event e;
412 
413     fixbps();
414     foreach (Event, e, eventlist)
415 	delevent(e->id);
416 	list_delete(list_curitem(eventlist), eventlist);
417     endfor
418 }
419 
420 /*
421  * Determine if the program stopped at a known breakpoint
422  * and if so do the associated commands.
423  */
424 
425 public Boolean bpact()
426 {
427     register Breakpoint p;
428     Boolean found;
429 
430     found = false;
431     foreach (Breakpoint, p, bplist)
432 	if (p->bpaddr == pc) {
433 	    if (tracebpts) {
434 		printf("breakpoint found at location 0x%x\n", pc);
435 	    }
436 	    found = true;
437 	    if (p->event->temporary) {
438 		delevent(p->event->id);
439 	    }
440 	    evalcmdlist(p->actions);
441 	}
442     endfor
443     if (isstopped) {
444 	printstatus();
445     }
446     fflush(stdout);
447     return found;
448 }
449 
450 /*
451  * Begin single stepping and executing the given commands after each step.
452  * If the first argument is true step by instructions, otherwise
453  * step by source lines.
454  *
455  * We automatically set a breakpoint at the end of the current procedure
456  * to turn off the given tracing.
457  */
458 
459 public traceon(inst, event, cmdlist)
460 Boolean inst;
461 Event event;
462 Cmdlist cmdlist;
463 {
464     register Trcmd trcmd;
465     Breakpoint bp;
466     Node until;
467     Cmdlist actions;
468 
469     trcmd = new(Trcmd);
470     ++trid;
471     trcmd->trid = trid;
472     trcmd->event = event;
473     trcmd->cmdlist = cmdlist;
474     single_stepping = true;
475     if (inst) {
476 	inst_tracing = true;
477 	list_append(list_item(trcmd), nil, eachinst);
478     } else {
479 	list_append(list_item(trcmd), nil, eachline);
480     }
481     until = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, return_addr()));
482     actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid));
483     event_once(until, actions);
484     if (tracebpts) {
485 	printf("adding trace %d for event %d\n", trcmd->trid, event->id);
486     }
487 }
488 
489 /*
490  * Turn off some kind of tracing.
491  * Strictly an internal command, this cannot be invoked by the user.
492  */
493 
494 public traceoff(id)
495 Integer id;
496 {
497     register Trcmd t;
498     register Boolean found;
499 
500     found = false;
501     foreach (Trcmd, t, eachline)
502 	if (t->trid == id) {
503 	    printrmtr(t);
504 	    list_delete(list_curitem(eachline), eachline);
505 	    found = true;
506 	    break;
507 	}
508     endfor
509     if (not found) {
510 	foreach (Trcmd, t, eachinst)
511 	    if (t->event->id == id) {
512 		printrmtr(t);
513 		list_delete(list_curitem(eachinst), eachinst);
514 		found = true;
515 		break;
516 	    }
517 	endfor
518 	if (not found) {
519 	    panic("missing trid %d", id);
520 	}
521     }
522     if (list_size(eachinst) == 0) {
523 	inst_tracing = false;
524 	if (list_size(eachline) == 0) {
525 	    single_stepping = false;
526 	}
527     }
528 }
529 
530 /*
531  * If breakpoints are being traced, note that a Trcmd is being deleted.
532  */
533 
534 private printrmtr(t)
535 Trcmd t;
536 {
537     if (tracebpts) {
538 	printf("removing trace %d for event %d\n", t->trid, t->event->id);
539     }
540 }
541 
542 /*
543  * Print out news during single step tracing.
544  */
545 
546 public printnews()
547 {
548     register Trcmd t;
549 
550     foreach (Trcmd, t, eachline)
551 	evalcmdlist(t->cmdlist);
552     endfor
553     foreach (Trcmd, t, eachinst)
554 	evalcmdlist(t->cmdlist);
555     endfor
556     bpact();
557 }
558 
559 /*
560  * A procedure call/return has occurred while single-stepping,
561  * note it if we're tracing lines.
562  */
563 
564 private Boolean chklist();
565 
566 public callnews(iscall)
567 Boolean iscall;
568 {
569     if (not chklist(eachline, iscall)) {
570 	chklist(eachinst, iscall);
571     }
572 }
573 
574 private Boolean chklist(list, iscall)
575 List list;
576 Boolean iscall;
577 {
578     register Trcmd t;
579     register Command cmd;
580 
581     foreach (Trcmd, t, list)
582 	foreach (Command, cmd, t->cmdlist)
583 	    if (cmd->op == O_PRINTSRCPOS and
584 	      (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) {
585 		curfunc = whatblock(pc);
586 		if (iscall) {
587 		    printentry(curfunc);
588 		} else {
589 		    printexit(curfunc);
590 		}
591 		return true;
592 	    }
593 	endfor
594     endfor
595     return false;
596 }
597 
598 /*
599  * When tracing variables we keep a copy of their most recent value
600  * and compare it to the current one each time a breakpoint occurs.
601  * MAXTRSIZE is the maximum size variable we allow.
602  */
603 
604 #define MAXTRSIZE 512
605 
606 /*
607  * List of variables being watched.
608  */
609 
610 typedef struct Trinfo *Trinfo;
611 
612 struct Trinfo {
613     Node variable;
614     Address traddr;
615     Symbol trblock;
616     char *trvalue;
617 };
618 
619 private List trinfolist;
620 
621 /*
622  * Find the trace information record associated with the given record.
623  * If there isn't one then create it and add it to the list.
624  */
625 
626 private Trinfo findtrinfo(p)
627 Node p;
628 {
629     register Trinfo tp;
630     Boolean isnew;
631 
632     isnew = true;
633     if (trinfolist == nil) {
634 	trinfolist = list_alloc();
635     } else {
636 	foreach (Trinfo, tp, trinfolist)
637 	    if (tp->variable == p) {
638 		isnew = false;
639 		break;
640 	    }
641 	endfor
642     }
643     if (isnew) {
644 	if (tracebpts) {
645 	    printf("adding trinfo for \"");
646 	    prtree(stdout, p);
647 	    printf("\"\n");
648 	}
649 	tp = new(Trinfo);
650 	tp->variable = p;
651 	tp->traddr = lval(p);
652 	tp->trvalue = nil;
653 	list_append(list_item(tp), nil, trinfolist);
654     }
655     return tp;
656 }
657 
658 /*
659  * Print out the value of a variable if it has changed since the
660  * last time we checked.
661  */
662 
663 public printifchanged(p)
664 Node p;
665 {
666     register Trinfo tp;
667     register int n;
668     char buff[MAXTRSIZE];
669     static Lineno prevline;
670 
671     tp = findtrinfo(p);
672     n = size(p->nodetype);
673     dread(buff, tp->traddr, n);
674     if (tp->trvalue == nil) {
675 	tp->trvalue = newarr(char, n);
676 	mov(buff, tp->trvalue, n);
677 	mov(buff, sp, n);
678 	sp += n;
679 	printf("initially (at line %d):\t", curline);
680 	prtree(stdout, p);
681 	printf(" = ");
682 	printval(p->nodetype);
683 	putchar('\n');
684     } else if (cmp(tp->trvalue, buff, n) != 0) {
685 	mov(buff, tp->trvalue, n);
686 	mov(buff, sp, n);
687 	sp += n;
688 	printf("after line %d:\t", prevline);
689 	prtree(stdout, p);
690 	printf(" = ");
691 	printval(p->nodetype);
692 	putchar('\n');
693     }
694     prevline = curline;
695 }
696 
697 /*
698  * Stop if the value of the given expression has changed.
699  */
700 
701 public stopifchanged(p)
702 Node p;
703 {
704     register Trinfo tp;
705     register int n;
706     char buff[MAXTRSIZE];
707     static Lineno prevline;
708 
709     tp = findtrinfo(p);
710     n = size(p->nodetype);
711     dread(buff, tp->traddr, n);
712     if (tp->trvalue == nil) {
713 	tp->trvalue = newarr(char, n);
714 	mov(buff, tp->trvalue, n);
715 	isstopped = true;
716     } else if (cmp(tp->trvalue, buff, n) != 0) {
717 	mov(buff, tp->trvalue, n);
718 	isstopped = true;
719     }
720     prevline = curline;
721 }
722 
723 /*
724  * Free the tracing table.
725  */
726 
727 public trfree()
728 {
729     register Trinfo tp;
730 
731     foreach (Trinfo, tp, trinfolist)
732 	dispose(tp->trvalue);
733 	dispose(tp);
734 	list_delete(list_curitem(trinfolist), trinfolist);
735     endfor
736 }
737 
738 /*
739  * Fix up breakpoint information before continuing execution.
740  *
741  * It's necessary to destroy events and breakpoints that were created
742  * temporarily and still exist because the program terminated abnormally.
743  */
744 
745 public fixbps()
746 {
747     register Event e;
748     register Trcmd t;
749 
750     single_stepping = false;
751     inst_tracing = false;
752     trfree();
753     foreach (Event, e, eventlist)
754 	if (e->temporary) {
755 	    delevent(e->id);
756 	}
757     endfor
758     foreach (Trcmd, t, eachline)
759 	printrmtr(t);
760 	list_delete(list_curitem(eachline), eachline);
761     endfor
762     foreach (Trcmd, t, eachinst)
763 	printrmtr(t);
764 	list_delete(list_curitem(eachinst), eachinst);
765     endfor
766 }
767 
768 /*
769  * Set all breakpoints in object code.
770  */
771 
772 public setallbps()
773 {
774     register Breakpoint p;
775 
776     foreach (Breakpoint, p, bplist)
777 	setbp(p->bpaddr);
778     endfor
779 }
780 
781 /*
782  * Undo damage done by "setallbps".
783  */
784 
785 public unsetallbps()
786 {
787     register Breakpoint p;
788 
789     foreach (Breakpoint, p, bplist)
790 	unsetbp(p->bpaddr);
791     endfor
792 }
793