xref: /original-bsd/old/dbx/events.c (revision 5e36add1)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
407 private printeventid (id)
408 integer id;
409 {
410     printf("[%d] ", id);
411 }
412 
413 /*
414  * Print out a condition.
415  */
416 
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 
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 
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 
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 
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 
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 
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 
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 
668 public callnews(iscall)
669 boolean iscall;
670 {
671     if (not chklist(eachline, iscall)) {
672 	chklist(eachinst, iscall);
673     }
674 }
675 
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 
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 
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 
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 
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 
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 
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 
892 public unsetallbps()
893 {
894     register Breakpoint p;
895 
896     foreach (Breakpoint, p, bplist)
897 	unsetbp(p->bpaddr);
898     endfor
899 }
900