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