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