date and time created 82/12/15 04:07:28 by linton
authorMark Linton <linton@ucbvax.Berkeley.EDU>
Wed, 15 Dec 1982 20:07:28 +0000 (12:07 -0800)
committerMark Linton <linton@ucbvax.Berkeley.EDU>
Wed, 15 Dec 1982 20:07:28 +0000 (12:07 -0800)
SCCS-vsn: old/dbx/events.c 1.1

usr/src/old/dbx/events.c [new file with mode: 0644]

diff --git a/usr/src/old/dbx/events.c b/usr/src/old/dbx/events.c
new file mode 100644 (file)
index 0000000..8ade9a0
--- /dev/null
@@ -0,0 +1,792 @@
+/* Copyright (c) 1982 Regents of the University of California */
+
+static char sccsid[] = "@(#)@(#)events.c 1.1 %G%";
+
+/*
+ * Event/breakpoint managment.
+ */
+
+#include "defs.h"
+#include "events.h"
+#include "main.h"
+#include "symbols.h"
+#include "tree.h"
+#include "eval.h"
+#include "source.h"
+#include "mappings.h"
+#include "process.h"
+#include "machine.h"
+#include "lists.h"
+
+#ifndef public
+typedef struct Event *Event;
+typedef struct Breakpoint *Breakpoint;
+
+Boolean inst_tracing;
+Boolean single_stepping;
+Boolean isstopped;
+
+#include "symbols.h"
+
+Symbol linesym;
+Symbol procsym;
+Symbol pcsym;
+Symbol retaddrsym;
+
+#define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist)
+#define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist)
+
+#endif
+
+struct Event {
+    unsigned int id;
+    Boolean temporary;
+    Node condition;
+    Cmdlist actions;
+};
+
+struct Breakpoint {
+    Event event;
+    Address bpaddr;    
+    Lineno bpline;
+    Cmdlist actions;
+};
+
+typedef List Eventlist;
+typedef List Bplist;
+
+#define eventlist_append(event, el) list_append(list_item(event), nil, el)
+#define bplist_append(bp, bl) list_append(list_item(bp), nil, bl)
+
+private Eventlist eventlist;           /* list of active events */
+private Bplist bplist;                 /* list of active breakpoints */
+private Integer eventid;               /* id number of next allocated event */
+private Integer trid;                  /* id number of next allocated trace */
+
+typedef struct Trcmd {
+    Integer trid;
+    Event event;
+    Cmdlist cmdlist;
+} *Trcmd;
+
+private List eachline;         /* commands to execute after each line */
+private List eachinst;         /* commands to execute after each instruction */
+
+private Breakpoint bp_alloc();
+
+/*
+ * Initialize breakpoint information.
+ */
+
+private Symbol builtinsym(str, class, type)
+String str;
+Symclass class;
+Symbol type;
+{
+    Symbol s;
+
+    s = insert(identname(str, true));
+    s->language = findlanguage(".s");
+    s->class = class;
+    s->type = type;
+    return s;
+}
+
+public bpinit()
+{
+    linesym = builtinsym("$line", VAR, t_int);
+    procsym = builtinsym("$proc", PROC, nil);
+    pcsym = lookup(identname("$pc", true));
+    if (pcsym == nil) {
+       panic("can't find $pc");
+    }
+    retaddrsym = builtinsym("$retaddr", VAR, t_int);
+    eventlist = list_alloc();
+    bplist = list_alloc();
+    eachline = list_alloc();
+    eachinst = list_alloc();
+}
+
+/*
+ * Trap an event and do the associated commands when it occurs.
+ */
+
+public Event event_alloc(istmp, econd, cmdlist)
+Boolean istmp;
+Node econd;
+Cmdlist cmdlist;
+{
+    register Event e;
+
+    e = new(Event);
+    ++eventid;
+    e->id = eventid;
+    e->temporary = istmp;
+    e->condition = econd;
+    e->actions = cmdlist;
+    eventlist_append(e, eventlist);
+    translate(e);
+    return e;
+}
+
+/*
+ * Delete the event with the given id.
+ */
+
+public delevent(id)
+unsigned int id;
+{
+    Event e;
+    Breakpoint bp;
+    Trcmd t;
+
+    foreach (Event, e, eventlist)
+       if (e->id == id) {
+           list_delete(list_curitem(eventlist), eventlist);
+           foreach (Breakpoint, bp, bplist)
+               if (bp->event == e) {
+                   list_delete(list_curitem(bplist), bplist);
+               }
+           endfor
+           break;
+       }
+    endfor
+    foreach (Trcmd, t, eachline)
+       if (t->event->id == id) {
+           printrmtr(t);
+           list_delete(list_curitem(eachline), eachline);
+       }
+    endfor
+    foreach (Trcmd, t, eachinst)
+       if (t->event->id == id) {
+           printrmtr(t);
+           list_delete(list_curitem(eachinst), eachinst);
+       }
+    endfor
+    if (list_size(eachinst) == 0) {
+       inst_tracing = false;
+       if (list_size(eachline) == 0) {
+           single_stepping = false;
+       }
+    }
+}
+
+/*
+ * Translate an event into the appropriate breakpoints and actions.
+ * While we're at it, turn on the breakpoints if the condition is true.
+ */
+
+private translate(e)
+Event e;
+{
+    Breakpoint bp;
+    Symbol s;
+    Node place;
+    Lineno line;
+    Address addr;
+
+    checkref(e->condition);
+    switch (e->condition->op) {
+       case O_EQ:
+           if (e->condition->value.arg[0]->op == O_SYM) {
+               s = e->condition->value.arg[0]->value.sym;
+               place = e->condition->value.arg[1];
+               if (s == linesym) {
+                   if (place->op == O_QLINE) {
+                       line = place->value.arg[1]->value.lcon;
+                       addr = objaddr(line,
+                           place->value.arg[0]->value.scon);
+                   } else {
+                       eval(place);
+                       line = pop(long);
+                       addr = objaddr(line, cursource);
+                   }
+                   if (addr == NOADDR) {
+                       delevent(e->id);
+                       beginerrmsg();
+                       fprintf(stderr, "no executable code at line ");
+                       prtree(stderr, place);
+                       enderrmsg();
+                   }
+                   bp = bp_alloc(e, addr, line, e->actions);
+               } else if (s == procsym) {
+                   eval(place);
+                   s = pop(Symbol);
+                   bp = bp_alloc(e, codeloc(s), 0, e->actions);
+                   if (isactive(s) and pc != codeloc(program)) {
+                       evalcmdlist(e->actions);
+                   }
+               } else if (s == pcsym) {
+                   eval(place);
+                   bp = bp_alloc(e, pop(Address), 0, e->actions);
+               } else {
+                   condbp(e);
+               }
+           } else {
+               condbp(e);
+           }
+           break;
+
+       /*
+        * These should be handled specially.
+        * But for now I'm ignoring the problem.
+        */
+       case O_AND:
+       case O_OR:
+       default:
+           condbp(e);
+           break;
+    }
+}
+
+/*
+ * Create a breakpoint for a condition that cannot be pinpointed
+ * to happening at a particular address, but one for which we
+ * must single step and check the condition after each statement.
+ */
+
+private condbp(e)
+Event e;
+{
+    Symbol p;
+    Breakpoint bp;
+    Cmdlist actions;
+
+    p = tcontainer(e->condition);
+    if (p == nil) {
+       p = program;
+    }
+    actions = buildcmdlist(build(O_IF, e->condition, e->actions));
+    actions = buildcmdlist(build(O_TRACEON, false, actions));
+    bp = bp_alloc(e, codeloc(p), 0, actions);
+}
+
+/*
+ * Determine the deepest nested subprogram that still contains
+ * all elements in the given expression.
+ */
+
+public Symbol tcontainer(exp)
+Node exp;
+{
+    Integer i;
+    Symbol s, t, u, v;
+
+    checkref(exp);
+    s = nil;
+    if (exp->op == O_SYM) {
+       s = container(exp->value.sym);
+    } else if (not isleaf(exp->op)) {
+       for (i = 0; i < nargs(exp->op); i++) {
+           t = tcontainer(exp->value.arg[i]);
+           if (t != nil) {
+               if (s == nil) {
+                   s = t;
+               } else {
+                   u = s;
+                   v = t;
+                   while (u != v and u != nil) {
+                       u = container(u);
+                       v = container(v);
+                   }
+                   if (u == nil) {
+                       panic("bad ancestry for \"%s\"", symname(s));
+                   } else {
+                       s = u;
+                   }
+               }
+           }
+       }
+    }
+    return s;
+}
+
+/*
+ * Print out what's currently being traced by looking at
+ * the currently active events.
+ *
+ * Some convolution here to translate internal representation
+ * of events back into something more palatable.
+ */
+
+public status()
+{
+    Event e;
+    Command cmd;
+
+    foreach (Event, e, eventlist)
+       if (not e->temporary) {
+           if (not isredirected()) {
+               printf("(%d) ", e->id);
+           }
+           cmd = list_element(Command, list_head(e->actions));
+           if (cmd->op == O_PRINTCALL) {
+               printf("trace ");
+               printname(stdout, cmd->value.sym);
+           } else {
+               if (list_size(e->actions) > 1) {
+                   printf("{ ");
+               }
+               foreach (Command, cmd, e->actions)
+                   printcmd(stdout, cmd);
+                   if (not list_islast()) {
+                       printf("; ");
+                   }
+               endfor
+               if (list_size(e->actions) > 1) {
+                   printf(" }");
+               }
+               printcond(e->condition);
+           }
+           printf("\n");
+       }
+    endfor
+}
+
+/*
+ * Print out a condition.
+ */
+
+private printcond(cond)
+Node cond;
+{
+    Symbol s;
+    Node place;
+
+    if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) {
+       s = cond->value.arg[0]->value.sym;
+       place = cond->value.arg[1];
+       if (s == procsym) {
+           if (place->value.sym != program) {
+               printf(" in ");
+               printname(stdout, place->value.sym);
+           }
+       } else if (s == linesym) {
+           printf(" at ");
+           prtree(stdout, place);
+       } else if (s == pcsym or s == retaddrsym) {
+           printf("i at ");
+           prtree(stdout, place);
+       } else {
+           printf(" when ");
+           prtree(stdout, cond);
+       }
+    } else {
+       printf(" when ");
+       prtree(stdout, cond);
+    }
+}
+
+/*
+ * Add a breakpoint to the list and return it.
+ */
+
+private Breakpoint bp_alloc(e, addr, line, actions)
+Event e;
+Address addr;
+Lineno line;
+Cmdlist actions;
+{
+    register Breakpoint p;
+
+    p = new(Breakpoint);
+    p->event = e;
+    p->bpaddr = addr;
+    p->bpline = line;
+    p->actions = actions;
+    if (tracebpts) {
+       printf("new bp at 0x%x\n", addr);
+       fflush(stdout);
+    }
+    bplist_append(p, bplist);
+    return p;
+}
+
+/*
+ * Free all storage in the event and breakpoint tables.
+ */
+
+public bpfree()
+{
+    register Event e;
+
+    fixbps();
+    foreach (Event, e, eventlist)
+       delevent(e->id);
+       list_delete(list_curitem(eventlist), eventlist);
+    endfor
+}
+
+/*
+ * Determine if the program stopped at a known breakpoint
+ * and if so do the associated commands.
+ */
+
+public Boolean bpact()
+{
+    register Breakpoint p;
+    Boolean found;
+
+    found = false;
+    foreach (Breakpoint, p, bplist)
+       if (p->bpaddr == pc) {
+           if (tracebpts) {
+               printf("breakpoint found at location 0x%x\n", pc);
+           }
+           found = true;
+           if (p->event->temporary) {
+               delevent(p->event->id);
+           }
+           evalcmdlist(p->actions);
+       }
+    endfor
+    if (isstopped) {
+       printstatus();
+    }
+    fflush(stdout);
+    return found;
+}
+
+/*
+ * Begin single stepping and executing the given commands after each step.
+ * If the first argument is true step by instructions, otherwise
+ * step by source lines.
+ *
+ * We automatically set a breakpoint at the end of the current procedure
+ * to turn off the given tracing.
+ */
+
+public traceon(inst, event, cmdlist)
+Boolean inst;
+Event event;
+Cmdlist cmdlist;
+{
+    register Trcmd trcmd;
+    Breakpoint bp;
+    Node until;
+    Cmdlist actions;
+
+    trcmd = new(Trcmd);
+    ++trid;
+    trcmd->trid = trid;
+    trcmd->event = event;
+    trcmd->cmdlist = cmdlist;
+    single_stepping = true;
+    if (inst) {
+       inst_tracing = true;
+       list_append(list_item(trcmd), nil, eachinst);
+    } else {
+       list_append(list_item(trcmd), nil, eachline);
+    }
+    until = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, return_addr()));
+    actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid));
+    event_once(until, actions);
+    if (tracebpts) {
+       printf("adding trace %d for event %d\n", trcmd->trid, event->id);
+    }
+}
+
+/*
+ * Turn off some kind of tracing.
+ * Strictly an internal command, this cannot be invoked by the user.
+ */
+
+public traceoff(id)
+Integer id;
+{
+    register Trcmd t;
+    register Boolean found;
+
+    found = false;
+    foreach (Trcmd, t, eachline)
+       if (t->trid == id) {
+           printrmtr(t);
+           list_delete(list_curitem(eachline), eachline);
+           found = true;
+           break;
+       }
+    endfor
+    if (not found) {
+       foreach (Trcmd, t, eachinst)
+           if (t->event->id == id) {
+               printrmtr(t);
+               list_delete(list_curitem(eachinst), eachinst);
+               found = true;
+               break;
+           }
+       endfor
+       if (not found) {
+           panic("missing trid %d", id);
+       }
+    }
+    if (list_size(eachinst) == 0) {
+       inst_tracing = false;
+       if (list_size(eachline) == 0) {
+           single_stepping = false;
+       }
+    }
+}
+
+/*
+ * If breakpoints are being traced, note that a Trcmd is being deleted.
+ */
+
+private printrmtr(t)
+Trcmd t;
+{
+    if (tracebpts) {
+       printf("removing trace %d for event %d\n", t->trid, t->event->id);
+    }
+}
+
+/*
+ * Print out news during single step tracing.
+ */
+
+public printnews()
+{
+    register Trcmd t;
+
+    foreach (Trcmd, t, eachline)
+       evalcmdlist(t->cmdlist);
+    endfor
+    foreach (Trcmd, t, eachinst)
+       evalcmdlist(t->cmdlist);
+    endfor
+    bpact();
+}
+
+/*
+ * A procedure call/return has occurred while single-stepping,
+ * note it if we're tracing lines.
+ */
+
+private Boolean chklist();
+
+public callnews(iscall)
+Boolean iscall;
+{
+    if (not chklist(eachline, iscall)) {
+       chklist(eachinst, iscall);
+    }
+}
+
+private Boolean chklist(list, iscall)
+List list;
+Boolean iscall;
+{
+    register Trcmd t;
+    register Command cmd;
+
+    foreach (Trcmd, t, list)
+       foreach (Command, cmd, t->cmdlist)
+           if (cmd->op == O_PRINTSRCPOS and
+             (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) {
+               curfunc = whatblock(pc);
+               if (iscall) {
+                   printentry(curfunc);
+               } else {
+                   printexit(curfunc);
+               }
+               return true;
+           }
+       endfor
+    endfor
+    return false;
+}
+
+/*
+ * When tracing variables we keep a copy of their most recent value
+ * and compare it to the current one each time a breakpoint occurs.
+ * MAXTRSIZE is the maximum size variable we allow.
+ */
+
+#define MAXTRSIZE 512
+
+/*
+ * List of variables being watched.
+ */
+
+typedef struct Trinfo *Trinfo;
+
+struct Trinfo {
+    Node variable;
+    Address traddr;
+    Symbol trblock;
+    char *trvalue;
+};
+
+private List trinfolist;
+
+/*
+ * Find the trace information record associated with the given record.
+ * If there isn't one then create it and add it to the list.
+ */
+
+private Trinfo findtrinfo(p)
+Node p;
+{
+    register Trinfo tp;
+    Boolean isnew;
+
+    isnew = true;
+    if (trinfolist == nil) {
+       trinfolist = list_alloc();
+    } else {
+       foreach (Trinfo, tp, trinfolist)
+           if (tp->variable == p) {
+               isnew = false;
+               break;
+           }
+       endfor
+    }
+    if (isnew) {
+       if (tracebpts) {
+           printf("adding trinfo for \"");
+           prtree(stdout, p);
+           printf("\"\n");
+       }
+       tp = new(Trinfo);
+       tp->variable = p;
+       tp->traddr = lval(p);
+       tp->trvalue = nil;
+       list_append(list_item(tp), nil, trinfolist);
+    }
+    return tp;
+}
+
+/*
+ * Print out the value of a variable if it has changed since the
+ * last time we checked.
+ */
+
+public printifchanged(p)
+Node p;
+{
+    register Trinfo tp;
+    register int n;
+    char buff[MAXTRSIZE];
+    static Lineno prevline;
+
+    tp = findtrinfo(p);
+    n = size(p->nodetype);
+    dread(buff, tp->traddr, n);
+    if (tp->trvalue == nil) {
+       tp->trvalue = newarr(char, n);
+       mov(buff, tp->trvalue, n);
+       mov(buff, sp, n);
+       sp += n;
+       printf("initially (at line %d):\t", curline);
+       prtree(stdout, p);
+       printf(" = ");
+       printval(p->nodetype);
+       putchar('\n');
+    } else if (cmp(tp->trvalue, buff, n) != 0) {
+       mov(buff, tp->trvalue, n);
+       mov(buff, sp, n);
+       sp += n;
+       printf("after line %d:\t", prevline);
+       prtree(stdout, p);
+       printf(" = ");
+       printval(p->nodetype);
+       putchar('\n');
+    }
+    prevline = curline;
+}
+
+/*
+ * Stop if the value of the given expression has changed.
+ */
+
+public stopifchanged(p)
+Node p;
+{
+    register Trinfo tp;
+    register int n;
+    char buff[MAXTRSIZE];
+    static Lineno prevline;
+
+    tp = findtrinfo(p);
+    n = size(p->nodetype);
+    dread(buff, tp->traddr, n);
+    if (tp->trvalue == nil) {
+       tp->trvalue = newarr(char, n);
+       mov(buff, tp->trvalue, n);
+       isstopped = true;
+    } else if (cmp(tp->trvalue, buff, n) != 0) {
+       mov(buff, tp->trvalue, n);
+       isstopped = true;
+    }
+    prevline = curline;
+}
+
+/*
+ * Free the tracing table.
+ */
+
+public trfree()
+{
+    register Trinfo tp;
+
+    foreach (Trinfo, tp, trinfolist)
+       dispose(tp->trvalue);
+       dispose(tp);
+       list_delete(list_curitem(trinfolist), trinfolist);
+    endfor
+}
+
+/*
+ * Fix up breakpoint information before continuing execution.
+ *
+ * It's necessary to destroy events and breakpoints that were created
+ * temporarily and still exist because the program terminated abnormally.
+ */
+
+public fixbps()
+{
+    register Event e;
+    register Trcmd t;
+
+    single_stepping = false;
+    inst_tracing = false;
+    trfree();
+    foreach (Event, e, eventlist)
+       if (e->temporary) {
+           delevent(e->id);
+       }
+    endfor
+    foreach (Trcmd, t, eachline)
+       printrmtr(t);
+       list_delete(list_curitem(eachline), eachline);
+    endfor
+    foreach (Trcmd, t, eachinst)
+       printrmtr(t);
+       list_delete(list_curitem(eachinst), eachinst);
+    endfor
+}
+
+/*
+ * Set all breakpoints in object code.
+ */
+
+public setallbps()
+{
+    register Breakpoint p;
+
+    foreach (Breakpoint, p, bplist)
+       setbp(p->bpaddr);
+    endfor
+}
+
+/*
+ * Undo damage done by "setallbps".
+ */
+
+public unsetallbps()
+{
+    register Breakpoint p;
+
+    foreach (Breakpoint, p, bplist)
+       unsetbp(p->bpaddr);
+    endfor
+}