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