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