|
|
1.1 root 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.1 (Berkeley) 5/31/85";
9: #endif not lint
10:
11: static char rcsid[] = "$Header: events.c,v 1.5 84/12/26 10:39:26 linton 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: typedef struct Event *Event;
32: typedef struct Breakpoint *Breakpoint;
33:
34: boolean inst_tracing;
35: boolean single_stepping;
36: boolean isstopped;
37:
38: #include "symbols.h"
39:
40: Symbol linesym;
41: Symbol procsym;
42: Symbol pcsym;
43: Symbol retaddrsym;
44:
45: #define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist)
46: #define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist)
47:
48: #endif
49:
50: struct Event {
51: unsigned int id;
52: boolean temporary;
53: Node condition;
54: Cmdlist actions;
55: };
56:
57: struct Breakpoint {
58: Event event;
59: Address bpaddr;
60: Lineno bpline;
61: Cmdlist actions;
62: boolean temporary;
63: };
64:
65: typedef List Eventlist;
66: typedef List Bplist;
67:
68: #define eventlist_append(event, el) list_append(list_item(event), nil, el)
69: #define bplist_append(bp, bl) list_append(list_item(bp), nil, bl)
70:
71: private Eventlist eventlist; /* list of active events */
72: private Bplist bplist; /* list of active breakpoints */
73: private Event curevent; /* most recently created event */
74: private integer eventid; /* id number of current event */
75: private integer trid; /* id number of current trace */
76:
77: typedef struct Trcmd {
78: Integer trid;
79: Event event;
80: Cmdlist cmdlist;
81: } *Trcmd;
82:
83: private List eachline; /* commands to execute after each line */
84: private List eachinst; /* commands to execute after each instruction */
85:
86: private Breakpoint bp_alloc();
87:
88: /*
89: * Initialize breakpoint information.
90: */
91:
92: private Symbol builtinsym(str, class, type)
93: String str;
94: Symclass class;
95: Symbol type;
96: {
97: Symbol s;
98:
99: s = insert(identname(str, true));
100: s->language = findlanguage(".s");
101: s->class = class;
102: s->type = type;
103: return s;
104: }
105:
106: public bpinit()
107: {
108: linesym = builtinsym("$line", VAR, t_int);
109: procsym = builtinsym("$proc", PROC, nil);
110: pcsym = lookup(identname("$pc", true));
111: if (pcsym == nil) {
112: panic("can't find $pc");
113: }
114: retaddrsym = builtinsym("$retaddr", VAR, t_int);
115: eventlist = list_alloc();
116: bplist = list_alloc();
117: eachline = list_alloc();
118: eachinst = list_alloc();
119: }
120:
121: /*
122: * Trap an event and do the associated commands when it occurs.
123: */
124:
125: public Event event_alloc(istmp, econd, cmdlist)
126: boolean istmp;
127: Node econd;
128: Cmdlist cmdlist;
129: {
130: register Event e;
131:
132: e = new(Event);
133: ++eventid;
134: e->id = eventid;
135: e->temporary = istmp;
136: e->condition = econd;
137: e->actions = cmdlist;
138: eventlist_append(e, eventlist);
139: curevent = e;
140: translate(e);
141: return e;
142: }
143:
144: /*
145: * Delete the event with the given id.
146: * Returns whether it's successful or not.
147: */
148:
149: public boolean delevent (id)
150: unsigned int id;
151: {
152: Event e;
153: Breakpoint bp;
154: Trcmd t;
155: boolean found;
156:
157: found = false;
158: foreach (Event, e, eventlist)
159: if (e->id == id) {
160: found = true;
161: foreach (Breakpoint, bp, bplist)
162: if (bp->event == e) {
163: if (tracebpts) {
164: printf("deleting breakpoint at 0x%x\n", bp->bpaddr);
165: fflush(stdout);
166: }
167: list_delete(list_curitem(bplist), bplist);
168: }
169: endfor
170: list_delete(list_curitem(eventlist), eventlist);
171: break;
172: }
173: endfor
174: foreach (Trcmd, t, eachline)
175: if (t->event->id == id) {
176: found = true;
177: printrmtr(t);
178: list_delete(list_curitem(eachline), eachline);
179: }
180: endfor
181: foreach (Trcmd, t, eachinst)
182: if (t->event->id == id) {
183: found = true;
184: printrmtr(t);
185: list_delete(list_curitem(eachinst), eachinst);
186: }
187: endfor
188: if (list_size(eachinst) == 0) {
189: inst_tracing = false;
190: if (list_size(eachline) == 0) {
191: single_stepping = false;
192: }
193: }
194: return found;
195: }
196:
197: /*
198: * Translate an event into the appropriate breakpoints and actions.
199: * While we're at it, turn on the breakpoints if the condition is true.
200: */
201:
202: private translate(e)
203: Event e;
204: {
205: Breakpoint bp;
206: Symbol s;
207: Node place;
208: Lineno line;
209: Address addr;
210:
211: checkref(e->condition);
212: switch (e->condition->op) {
213: case O_EQ:
214: if (e->condition->value.arg[0]->op == O_SYM) {
215: s = e->condition->value.arg[0]->value.sym;
216: place = e->condition->value.arg[1];
217: if (s == linesym) {
218: if (place->op == O_QLINE) {
219: line = place->value.arg[1]->value.lcon;
220: addr = objaddr(line, place->value.arg[0]->value.scon);
221: } else {
222: eval(place);
223: line = pop(long);
224: addr = objaddr(line, cursource);
225: }
226: if (addr == NOADDR) {
227: if (not delevent(e->id)) {
228: printf("!! dbx.translate: can't undo event %d?\n",
229: e->id);
230: }
231: beginerrmsg();
232: fprintf(stderr, "no executable code at line ");
233: prtree(stderr, place);
234: enderrmsg();
235: }
236: bp = bp_alloc(e, addr, line, e->actions);
237: } else if (s == procsym) {
238: eval(place);
239: s = pop(Symbol);
240: bp = bp_alloc(e, codeloc(s), 0, e->actions);
241: if (isactive(s) and pc != codeloc(program)) {
242: evalcmdlist(e->actions);
243: }
244: } else if (s == pcsym) {
245: eval(place);
246: bp = bp_alloc(e, pop(Address), 0, e->actions);
247: } else {
248: condbp(e);
249: }
250: } else {
251: condbp(e);
252: }
253: break;
254:
255: /*
256: * These should be handled specially.
257: * But for now I'm ignoring the problem.
258: */
259: case O_AND:
260: case O_OR:
261: default:
262: condbp(e);
263: break;
264: }
265: }
266:
267: /*
268: * Create a breakpoint for a condition that cannot be pinpointed
269: * to happening at a particular address, but one for which we
270: * must single step and check the condition after each statement.
271: */
272:
273: private condbp(e)
274: Event e;
275: {
276: Symbol p;
277: Breakpoint bp;
278: Cmdlist actions;
279:
280: p = tcontainer(e->condition);
281: if (p == nil) {
282: p = program;
283: }
284: actions = buildcmdlist(build(O_IF, e->condition, e->actions));
285: actions = buildcmdlist(build(O_TRACEON, false, actions));
286: bp = bp_alloc(e, codeloc(p), 0, actions);
287: }
288:
289: /*
290: * Determine the deepest nested subprogram that still contains
291: * all elements in the given expression.
292: */
293:
294: public Symbol tcontainer(exp)
295: Node exp;
296: {
297: Integer i;
298: Symbol s, t, u, v;
299:
300: checkref(exp);
301: s = nil;
302: if (exp->op == O_SYM) {
303: s = container(exp->value.sym);
304: } else if (not isleaf(exp->op)) {
305: for (i = 0; i < nargs(exp->op); i++) {
306: t = tcontainer(exp->value.arg[i]);
307: if (t != nil) {
308: if (s == nil) {
309: s = t;
310: } else {
311: u = s;
312: v = t;
313: while (u != v and u != nil) {
314: u = container(u);
315: v = container(v);
316: }
317: if (u == nil) {
318: panic("bad ancestry for \"%s\"", symname(s));
319: } else {
320: s = u;
321: }
322: }
323: }
324: }
325: }
326: return s;
327: }
328:
329: /*
330: * Determine if the given function can be executed at full speed.
331: * This can only be done if there are no breakpoints within the function.
332: */
333:
334: public boolean canskip(f)
335: Symbol f;
336: {
337: Breakpoint p;
338: boolean ok;
339:
340: ok = true;
341: foreach (Breakpoint, p, bplist)
342: if (whatblock(p->bpaddr) == f) {
343: ok = false;
344: break;
345: }
346: endfor
347: return ok;
348: }
349:
350: /*
351: * Print out what's currently being traced by looking at
352: * the currently active events.
353: *
354: * Some convolution here to translate internal representation
355: * of events back into something more palatable.
356: */
357:
358: public status()
359: {
360: Event e;
361:
362: foreach (Event, e, eventlist)
363: if (not e->temporary) {
364: printevent(e);
365: }
366: endfor
367: }
368:
369: public printevent(e)
370: Event e;
371: {
372: Command cmd;
373:
374: if (not isredirected()) {
375: printeventid(e->id);
376: }
377: cmd = list_element(Command, list_head(e->actions));
378: if (cmd->op == O_PRINTCALL) {
379: printf("trace ");
380: printname(stdout, cmd->value.sym);
381: } else {
382: if (list_size(e->actions) > 1) {
383: printf("{ ");
384: }
385: foreach (Command, cmd, e->actions)
386: printcmd(stdout, cmd);
387: if (not list_islast()) {
388: printf("; ");
389: }
390: endfor
391: if (list_size(e->actions) > 1) {
392: printf(" }");
393: }
394: printcond(e->condition);
395: }
396: printf("\n");
397: }
398:
399: private printeventid (id)
400: integer id;
401: {
402: printf("[%d] ", id);
403: }
404:
405: /*
406: * Print out a condition.
407: */
408:
409: private printcond(cond)
410: Node cond;
411: {
412: Symbol s;
413: Node place;
414:
415: if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) {
416: s = cond->value.arg[0]->value.sym;
417: place = cond->value.arg[1];
418: if (s == procsym) {
419: if (place->value.sym != program) {
420: printf(" in ");
421: printname(stdout, place->value.sym);
422: }
423: } else if (s == linesym) {
424: printf(" at ");
425: prtree(stdout, place);
426: } else if (s == pcsym or s == retaddrsym) {
427: printf("i at ");
428: prtree(stdout, place);
429: } else {
430: printf(" when ");
431: prtree(stdout, cond);
432: }
433: } else {
434: printf(" when ");
435: prtree(stdout, cond);
436: }
437: }
438:
439: /*
440: * Add a breakpoint to the list and return it.
441: */
442:
443: private Breakpoint bp_alloc(e, addr, line, actions)
444: Event e;
445: Address addr;
446: Lineno line;
447: Cmdlist actions;
448: {
449: register Breakpoint p;
450:
451: p = new(Breakpoint);
452: p->event = e;
453: p->bpaddr = addr;
454: p->bpline = line;
455: p->actions = actions;
456: p->temporary = false;
457: if (tracebpts) {
458: if (e == nil) {
459: printf("new bp at 0x%x for event ??\n", addr, e->id);
460: } else {
461: printf("new bp at 0x%x for event %d\n", addr, e->id);
462: }
463: fflush(stdout);
464: }
465: bplist_append(p, bplist);
466: return p;
467: }
468:
469: /*
470: * Free all storage in the event and breakpoint tables.
471: */
472:
473: public bpfree()
474: {
475: register Event e;
476:
477: fixbps();
478: foreach (Event, e, eventlist)
479: if (not delevent(e->id)) {
480: printf("!! dbx.bpfree: can't delete event %d\n", e->id);
481: }
482: list_delete(list_curitem(eventlist), eventlist);
483: endfor
484: }
485:
486: /*
487: * Determine if the program stopped at a known breakpoint
488: * and if so do the associated commands.
489: */
490:
491: public boolean bpact()
492: {
493: register Breakpoint p;
494: boolean found;
495: integer eventId;
496:
497: found = false;
498: foreach (Breakpoint, p, bplist)
499: if (p->bpaddr == pc) {
500: if (tracebpts) {
501: printf("breakpoint for event %d found at location 0x%x\n",
502: p->event->id, pc);
503: }
504: found = true;
505: if (p->event->temporary) {
506: if (not delevent(p->event->id)) {
507: printf("!! dbx.bpact: can't find event %d\n",
508: p->event->id);
509: }
510: }
511: evalcmdlist(p->actions);
512: if (isstopped) {
513: eventId = p->event->id;
514: }
515: if (p->temporary) {
516: list_delete(list_curitem(bplist), bplist);
517: }
518: }
519: endfor
520: if (isstopped) {
521: if (found) {
522: printeventid(eventId);
523: }
524: printstatus();
525: }
526: fflush(stdout);
527: return found;
528: }
529:
530: /*
531: * Begin single stepping and executing the given commands after each step.
532: * If the first argument is true step by instructions, otherwise
533: * step by source lines.
534: *
535: * We automatically set a breakpoint at the end of the current procedure
536: * to turn off the given tracing.
537: */
538:
539: public traceon(inst, event, cmdlist)
540: boolean inst;
541: Event event;
542: Cmdlist cmdlist;
543: {
544: register Trcmd trcmd;
545: Breakpoint bp;
546: Cmdlist actions;
547: Address ret;
548: Event e;
549:
550: if (event == nil) {
551: e = curevent;
552: } else {
553: e = event;
554: }
555: trcmd = new(Trcmd);
556: ++trid;
557: trcmd->trid = trid;
558: trcmd->event = e;
559: trcmd->cmdlist = cmdlist;
560: single_stepping = true;
561: if (inst) {
562: inst_tracing = true;
563: list_append(list_item(trcmd), nil, eachinst);
564: } else {
565: list_append(list_item(trcmd), nil, eachline);
566: }
567: ret = return_addr();
568: if (ret != 0) {
569: actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid));
570: bp = bp_alloc(e, (Address) ret, 0, actions);
571: bp->temporary = true;
572: }
573: if (tracebpts) {
574: printf("adding trace %d for event %d\n", trcmd->trid, e->id);
575: }
576: }
577:
578: /*
579: * Turn off some kind of tracing.
580: * Strictly an internal command, this cannot be invoked by the user.
581: */
582:
583: public traceoff(id)
584: Integer id;
585: {
586: register Trcmd t;
587: register boolean found;
588:
589: found = false;
590: foreach (Trcmd, t, eachline)
591: if (t->trid == id) {
592: printrmtr(t);
593: list_delete(list_curitem(eachline), eachline);
594: found = true;
595: break;
596: }
597: endfor
598: if (not found) {
599: foreach (Trcmd, t, eachinst)
600: if (t->event->id == id) {
601: printrmtr(t);
602: list_delete(list_curitem(eachinst), eachinst);
603: found = true;
604: break;
605: }
606: endfor
607: if (not found) {
608: beginerrmsg();
609: fprintf(stderr, "[internal error: trace id %d not found]\n", id);
610: }
611: }
612: if (list_size(eachinst) == 0) {
613: inst_tracing = false;
614: if (list_size(eachline) == 0) {
615: single_stepping = false;
616: }
617: }
618: }
619:
620: /*
621: * If breakpoints are being traced, note that a Trcmd is being deleted.
622: */
623:
624: private printrmtr(t)
625: Trcmd t;
626: {
627: if (tracebpts) {
628: printf("removing trace %d", t->trid);
629: if (t->event != nil) {
630: printf(" for event %d", t->event->id);
631: }
632: printf("\n");
633: }
634: }
635:
636: /*
637: * Print out news during single step tracing.
638: */
639:
640: public printnews()
641: {
642: register Trcmd t;
643:
644: foreach (Trcmd, t, eachline)
645: evalcmdlist(t->cmdlist);
646: endfor
647: foreach (Trcmd, t, eachinst)
648: evalcmdlist(t->cmdlist);
649: endfor
650: bpact();
651: }
652:
653: /*
654: * A procedure call/return has occurred while single-stepping,
655: * note it if we're tracing lines.
656: */
657:
658: private boolean chklist();
659:
660: public callnews(iscall)
661: boolean iscall;
662: {
663: if (not chklist(eachline, iscall)) {
664: chklist(eachinst, iscall);
665: }
666: }
667:
668: private boolean chklist(list, iscall)
669: List list;
670: boolean iscall;
671: {
672: register Trcmd t;
673: register Command cmd;
674:
675: setcurfunc(whatblock(pc));
676: foreach (Trcmd, t, list)
677: foreach (Command, cmd, t->cmdlist)
678: if (cmd->op == O_PRINTSRCPOS and
679: (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) {
680: if (iscall) {
681: printentry(curfunc);
682: } else {
683: printexit(curfunc);
684: }
685: return true;
686: }
687: endfor
688: endfor
689: return false;
690: }
691:
692: /*
693: * When tracing variables we keep a copy of their most recent value
694: * and compare it to the current one each time a breakpoint occurs.
695: * MAXTRSIZE is the maximum size variable we allow.
696: */
697:
698: #define MAXTRSIZE 512
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.