summaryrefslogtreecommitdiff
path: root/os/port/devdbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/port/devdbg.c')
-rw-r--r--os/port/devdbg.c1000
1 files changed, 1000 insertions, 0 deletions
diff --git a/os/port/devdbg.c b/os/port/devdbg.c
new file mode 100644
index 00000000..daaf314c
--- /dev/null
+++ b/os/port/devdbg.c
@@ -0,0 +1,1000 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "rdbg.h"
+
+#include <kernel.h>
+#include <interp.h>
+
+/*
+ * The following should be set in the config file to override
+ * the defaults.
+ */
+int dbgstart;
+char *dbgdata;
+char *dbgctl;
+char *dbgctlstart;
+char *dbgctlstop;
+char *dbgctlflush;
+
+//
+// Error messages sent to the remote debugger
+//
+static uchar Ereset[9] = { 'r', 'e', 's', 'e', 't' };
+static uchar Ecount[9] = { 'c', 'o', 'u', 'n', 't' };
+static uchar Eunk[9] = { 'u', 'n', 'k' };
+static uchar Einval[9] = { 'i', 'n', 'v', 'a', 'l' };
+static uchar Ebadpid[9] = {'p', 'i', 'd'};
+static uchar Eunsup[9] = { 'u', 'n', 's', 'u', 'p' };
+static uchar Enotstop[9] = { 'n', 'o', 't', 's', 't', 'o', 'p' };
+
+//
+// Error messages raised via call to error()
+//
+static char Erunning[] = "Not allowed while debugger is running";
+static char Enumarg[] = "Not enough args";
+static char Ebadcmd[] = "Unknown command";
+
+static int PROCREG;
+static struct {
+ Rendez;
+ Bkpt *b;
+} brk;
+
+static Queue *logq;
+
+int dbgchat = 0;
+
+typedef struct Debugger Debugger;
+struct Debugger {
+ RWlock;
+ int running;
+ char data[PRINTSIZE];
+ char ctl[PRINTSIZE];
+ char ctlstart[PRINTSIZE];
+ char ctlstop[PRINTSIZE];
+ char ctlflush[PRINTSIZE];
+};
+
+static Debugger debugger = {
+ .data= "#t/eia0",
+ .ctl= "#t/eia0ctl",
+ .ctlstart= "b19200",
+ .ctlstop= "h",
+ .ctlflush= "f",
+};
+
+enum {
+ BkptStackSize= 256,
+};
+
+typedef struct SkipArg SkipArg;
+struct SkipArg
+{
+ Bkpt *b;
+ Proc *p;
+};
+
+Bkpt *breakpoints;
+void freecondlist(BkptCond *l);
+
+static int
+getbreaks(ulong addr, Bkpt **a, int nb)
+{
+ Bkpt *b;
+ int n;
+
+ n = 0;
+ for(b = breakpoints; b != nil; b = b->next){
+ if(b->addr == addr){
+ a[n++] = b;
+ if(n == nb)
+ break;
+ }
+ }
+ return n;
+}
+
+Bkpt*
+newbreak(int id, ulong addr, BkptCond *conds, void(*handler)(Bkpt*), void *aux)
+{
+ Bkpt *b;
+
+ b = malloc(sizeof(*b));
+ if(b == nil)
+ error(Enomem);
+
+ b->id = id;
+ b->conditions = conds;
+ b->addr = addr;
+ b->handler = handler;
+ b->aux = aux;
+ b->next = nil;
+
+ return b;
+}
+
+void
+freebreak(Bkpt *b)
+{
+ freecondlist(b->conditions);
+ free(b);
+}
+
+BkptCond*
+newcondition(uchar cmd, ulong val)
+{
+ BkptCond *c;
+
+ c = mallocz(sizeof(*c), 0);
+ if(c == nil)
+ error(Enomem);
+
+ c->op = cmd;
+ c->val = val;
+ c->next = nil;
+
+ return c;
+}
+
+void
+freecondlist(BkptCond *l)
+{
+ BkptCond *next;
+
+ while(l != nil){
+ next = l->next;
+ free(l);
+ l = next;
+ }
+}
+
+
+void
+breakset(Bkpt *b)
+{
+ Bkpt *e[1];
+
+ if(getbreaks(b->addr, e, 1) != 0){
+ b->instr = e[0]->instr;
+ } else {
+ b->instr = machinstr(b->addr);
+ machbreakset(b->addr);
+ }
+
+ b->next = breakpoints;
+ breakpoints = b;
+}
+
+void
+breakrestore(Bkpt *b)
+{
+ b->next = breakpoints;
+ breakpoints = b;
+ machbreakset(b->addr);
+}
+
+Bkpt*
+breakclear(int id)
+{
+ Bkpt *b, *e, *p;
+
+ for(b=breakpoints, p=nil; b != nil && b->id != id; p = b, b = b->next)
+ ;
+
+ if(b != nil){
+ if(p == nil)
+ breakpoints = b->next;
+ else
+ p->next = b->next;
+
+ if(getbreaks(b->addr, &e, 1) == 0)
+ machbreakclear(b->addr, b->instr);
+ }
+
+ return b;
+}
+
+void
+breaknotify(Bkpt *b, Proc *p)
+{
+ p->dbgstop = 1; // stop running this process.
+ b->handler(b);
+}
+
+int
+breakmatch(BkptCond *cond, Ureg *ur, Proc *p)
+{
+ ulong a, b;
+ int pos;
+ ulong s[BkptStackSize];
+
+ memset(s, 0, sizeof(s));
+ pos = 0;
+
+ for(;cond != nil; cond = cond->next){
+ switch(cond->op){
+ default:
+ panic("breakmatch: unknown operator %c", cond->op);
+ break;
+ case 'k':
+ if(p == nil || p->pid != cond->val)
+ return 0;
+ s[pos++] = 1;
+ break;
+ case 'b':
+ if(ur->pc != cond->val)
+ return 0;
+ s[pos++] = 1;
+ break;
+ case 'p': s[pos++] = cond->val; break;
+ case '*': a = *(ulong*)s[--pos]; s[pos++] = a; break;
+ case '&': a = s[--pos]; b = s[--pos]; s[pos++] = a & b; break;
+ case '=': a = s[--pos]; b = s[--pos]; s[pos++] = a == b; break;
+ case '!': a = s[--pos]; b = s[--pos]; s[pos++] = a != b; break;
+ case 'a': a = s[--pos]; b = s[--pos]; s[pos++] = a && b; break;
+ case 'o': a = s[--pos]; b = s[--pos]; s[pos++] = a || b; break;
+ }
+ }
+
+ if(pos && s[pos-1])
+ return 1;
+ return 0;
+}
+
+void
+breakinit(void)
+{
+ machbreakinit();
+}
+
+static void
+dbglog(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ qwrite(logq, buf, n);
+}
+
+static int
+get(int dbgfd, uchar *b)
+{
+ int i;
+ uchar c;
+
+ if(kread(dbgfd, &c, 1) < 0)
+ error(Eio);
+ for(i=0; i<9; i++){
+ if(kread(dbgfd, b++, 1) < 0)
+ error(Eio);
+ }
+ return c;
+}
+
+static void
+mesg(int dbgfd, int m, uchar *buf)
+{
+ int i;
+ uchar c;
+
+ c = m;
+ if(kwrite(dbgfd, &c, 1) < 0)
+ error(Eio);
+ for(i=0; i<9; i++){
+ if(kwrite(dbgfd, buf+i, 1) < 0)
+ error(Eio);
+ }
+}
+
+static ulong
+dbglong(uchar *s)
+{
+ return (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0);
+}
+
+static Proc *
+dbgproc(ulong pid, int dbgok)
+{
+ int i;
+ Proc *p;
+
+ if(!dbgok && pid == up->pid)
+ return 0;
+ p = proctab(0);
+ for(i = 0; i < conf.nproc; i++){
+ if(p->pid == pid)
+ return p;
+ p++;
+ }
+ return 0;
+}
+
+static void*
+addr(uchar *s)
+{
+ ulong a;
+ Proc *p;
+ static Ureg ureg;
+
+ a = ((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0));
+ if(a < sizeof(Ureg)){
+ p = dbgproc(PROCREG, 0);
+ if(p == 0){
+ dbglog("dbg: invalid pid\n");
+ return 0;
+ }
+ if(p->dbgreg){
+ /* in trap(), registers are all on stack */
+ memmove(&ureg, p->dbgreg, sizeof(ureg));
+ }
+ else {
+ /* not in trap, only pc and sp are available */
+ memset(&ureg, 0, sizeof(ureg));
+ ureg.sp = p->sched.sp;
+ ureg.pc = p->sched.pc;
+ }
+ return (uchar*)&ureg+a;
+ }
+ return (void*)a;
+}
+
+
+static void
+dumpcmd(uchar cmd, uchar *min)
+{
+ char *s;
+ int n;
+
+ switch(cmd){
+ case Terr: s = "Terr"; break;
+ case Tmget: s = "Tmget"; break;
+ case Tmput: s = "Tmput"; break;
+ case Tspid: s = "Tspid"; break;
+ case Tproc: s = "Tproc"; break;
+ case Tstatus: s = "Tstatus"; break;
+ case Trnote: s = "Trnote"; break;
+ case Tstartstop: s = "Tstartstop"; break;
+ case Twaitstop: s = "Twaitstop"; break;
+ case Tstart: s = "Tstart"; break;
+ case Tstop: s = "Tstop"; break;
+ case Tkill: s = "Tkill"; break;
+ case Tcondbreak: s = "Tcondbreak"; break;
+ default: s = "<Unknown>"; break;
+ }
+ dbglog("%s: [%2.2ux]: ", s, cmd);
+ for(n = 0; n < 9; n++)
+ dbglog("%2.2ux", min[n]);
+ dbglog("\n");
+}
+
+static int
+brkpending(void *a)
+{
+ Proc *p;
+
+ p = a;
+ if(brk.b != nil) return 1;
+
+ p->dbgstop = 0; /* atomic */
+ if(p->state == Stopped)
+ ready(p);
+
+ return 0;
+}
+
+static void
+gotbreak(Bkpt *b)
+{
+ Bkpt *cur, *prev;
+
+ b->link = nil;
+
+ for(prev = nil, cur = brk.b; cur != nil; prev = cur, cur = cur->link)
+ ;
+ if(prev == nil)
+ brk.b = b;
+ else
+ prev->link = b;
+
+ wakeup(&brk);
+}
+
+static int
+startstop(Proc *p)
+{
+ int id;
+ int s;
+ Bkpt *b;
+
+ sleep(&brk, brkpending, p);
+
+ s = splhi();
+ b = brk.b;
+ brk.b = b->link;
+ splx(s);
+
+ id = b->id;
+
+ return id;
+}
+
+static int
+condbreak(char cmd, ulong val)
+{
+ BkptCond *c;
+ static BkptCond *head = nil;
+ static BkptCond *tail = nil;
+ static Proc *p = nil;
+ static int id = -1;
+ int s;
+
+ if(waserror()){
+ dbglog(up->env->errstr);
+ freecondlist(head);
+ head = tail = nil;
+ p = nil;
+ id = -1;
+ return 0;
+ }
+
+ switch(cmd){
+ case 'b': case 'p':
+ case '*': case '&': case '=':
+ case '!': case 'a': case 'o':
+ break;
+ case 'n':
+ id = val;
+ poperror();
+ return 1;
+ case 'k':
+ p = dbgproc(val, 0);
+ if(p == nil)
+ error("k: unknown pid");
+ break;
+ case 'd': {
+ Bkpt *b;
+
+ s = splhi();
+ b = breakclear(val);
+ if(b != nil){
+ Bkpt *cur, *prev;
+
+ prev = nil;
+ cur = brk.b;
+ while(cur != nil){
+ if(cur->id == b->id){
+ if(prev == nil)
+ brk.b = cur->link;
+ else
+ prev->link = cur->link;
+ break;
+ }
+ cur = cur->link;
+ }
+ freebreak(b);
+ }
+ splx(s);
+ poperror();
+ return 1;
+ }
+ default:
+ dbglog("condbreak(): unknown op %c %lux\n", cmd, val);
+ error("unknown op");
+ }
+
+ c = newcondition(cmd, val);
+
+ //
+ // the 'b' command comes last, (so we know we have reached the end
+ // of the condition list), but it should be the first thing
+ // checked, so put it at the head.
+ //
+ if(cmd == 'b'){
+ if(p == nil) error("no pid");
+ if(id == -1) error("no id");
+
+ c->next = head;
+ s = splhi();
+ breakset(newbreak(id, val, c, gotbreak, p));
+ splx(s);
+ head = tail = nil;
+ p = nil;
+ id = -1;
+ } else if(tail != nil){
+ tail->next = c;
+ tail = c;
+ } else
+ head = tail = c;
+
+ poperror();
+
+ return 1;
+}
+
+static void
+dbg(void*)
+{
+ Proc *p;
+ ulong val;
+ int n, cfd, dfd;
+ uchar cmd, *a, min[RDBMSGLEN-1], mout[RDBMSGLEN-1];
+
+ rlock(&debugger);
+
+ setpri(PriRealtime);
+
+ closefgrp(up->env->fgrp);
+ up->env->fgrp = newfgrp(nil);
+
+ if(waserror()){
+ dbglog("dbg: quits: %s\n", up->env->errstr);
+ runlock(&debugger);
+ wlock(&debugger);
+ debugger.running = 0;
+ wunlock(&debugger);
+ pexit("", 0);
+ }
+
+ dfd = kopen(debugger.data, ORDWR);
+ if(dfd < 0){
+ dbglog("dbg: can't open %s: %s\n",debugger.data, up->env->errstr);
+ error(Eio);
+ }
+ if(waserror()){
+ kclose(dfd);
+ nexterror();
+ }
+
+ if(debugger.ctl[0] != 0){
+ cfd = kopen(debugger.ctl, ORDWR);
+ if(cfd < 0){
+ dbglog("dbg: can't open %s: %s\n", debugger.ctl, up->env->errstr);
+ error(Eio);
+ }
+ if(kwrite(cfd, debugger.ctlstart, strlen(debugger.ctlstart)) < 0){
+ dbglog("dbg: write %s: %s\n", debugger.ctl, up->env->errstr);
+ error(Eio);
+ }
+ }else
+ cfd = -1;
+ if(waserror()){
+ if(cfd != -1){
+ kwrite(cfd, debugger.ctlflush, strlen(debugger.ctlflush));
+ kclose(cfd);
+ }
+ nexterror();
+ }
+
+ mesg(dfd, Rerr, Ereset);
+
+ for(;;){
+ memset(mout, 0, sizeof(mout));
+ cmd = get(dfd, min);
+ if(dbgchat)
+ dumpcmd(cmd, min);
+ switch(cmd){
+ case Tmget:
+ n = min[4];
+ if(n > 9){
+ mesg(dfd, Rerr, Ecount);
+ break;
+ }
+ a = addr(min+0);
+ if(!isvalid_va(a)){
+ mesg(dfd, Rerr, Einval);
+ break;
+ }
+ memmove(mout, a, n);
+ mesg(dfd, Rmget, mout);
+ break;
+ case Tmput:
+ n = min[4];
+ if(n > 4){
+ mesg(dfd, Rerr, Ecount);
+ break;
+ }
+ a = addr(min+0);
+ if(!isvalid_va(a)){
+ mesg(dfd, Rerr, Einval);
+ break;
+ }
+ memmove(a, min+5, n);
+ segflush(a, n);
+ mesg(dfd, Rmput, mout);
+ break;
+ case Tproc:
+ p = dbgproc(dbglong(min+0), 0);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ PROCREG = p->pid; /* try this instead of Tspid */
+ sprint((char*)mout, "%8.8lux", p);
+ mesg(dfd, Rproc, mout);
+ break;
+ case Tstatus:
+ p = dbgproc(dbglong(min+0), 1);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ if(p->state > Rendezvous || p->state < Dead)
+ sprint((char*)mout, "%8.8ux", p->state);
+ else if(p->dbgstop == 1)
+ strncpy((char*)mout, statename[Stopped], sizeof(mout));
+ else
+ strncpy((char*)mout, statename[p->state], sizeof(mout));
+ mesg(dfd, Rstatus, mout);
+ break;
+ case Trnote:
+ p = dbgproc(dbglong(min+0), 0);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ mout[0] = 0; /* should be trap status, if any */
+ mesg(dfd, Rrnote, mout);
+ break;
+ case Tstop:
+ p = dbgproc(dbglong(min+0), 0);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ p->dbgstop = 1; /* atomic */
+ mout[0] = 0;
+ mesg(dfd, Rstop, mout);
+ break;
+ case Tstart:
+ p = dbgproc(dbglong(min+0), 0);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ p->dbgstop = 0; /* atomic */
+ if(p->state == Stopped)
+ ready(p);
+ mout[0] = 0;
+ mesg(dfd, Rstart, mout);
+ break;
+ case Tstartstop:
+ p = dbgproc(dbglong(min+0), 0);
+ if(p == 0){
+ mesg(dfd, Rerr, Ebadpid);
+ break;
+ }
+ if(!p->dbgstop){
+ mesg(dfd, Rerr, Enotstop);
+ break;
+ }
+ mout[0] = startstop(p);
+ mesg(dfd, Rstartstop, mout);
+ break;
+ case Tcondbreak:
+ val = dbglong(min+0);
+ if(!condbreak(min[4], val)){
+ mesg(dfd, Rerr, Eunk);
+ break;
+ }
+ mout[0] = 0;
+ mesg(dfd, Rcondbreak, mout);
+ break;
+ default:
+ dumpcmd(cmd, min);
+ mesg(dfd, Rerr, Eunk);
+ break;
+ }
+ }
+}
+
+static void
+dbgnote(Proc *p, Ureg *ur)
+{
+ if(p){
+ p->dbgreg = ur;
+ PROCREG = p->pid; /* acid can get the trap info from regs */
+ }
+}
+
+enum {
+ Qdir,
+ Qdbgctl,
+ Qdbglog,
+
+ DBGrun = 1,
+ DBGstop = 2,
+
+ Loglimit = 4096,
+};
+
+static Dirtab dbgdir[]=
+{
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "dbgctl", {Qdbgctl}, 0, 0660,
+ "dbglog", {Qdbglog}, 0, 0440,
+};
+
+static void
+start_debugger(void)
+{
+ breakinit();
+ dbglog("starting debugger\n");
+ debugger.running++;
+ kproc("dbg", dbg, 0, KPDUPPG);
+}
+
+static void
+dbginit(void)
+{
+
+ logq = qopen(Loglimit, 0, 0, 0);
+ if(logq == nil)
+ error(Enomem);
+ qnoblock(logq, 1);
+
+ wlock(&debugger);
+ if(waserror()){
+ wunlock(&debugger);
+ qfree(logq);
+ logq = nil;
+ nexterror();
+ }
+
+ if(dbgdata != nil){
+ strncpy(debugger.data, dbgdata, sizeof(debugger.data));
+ debugger.data[sizeof(debugger.data)-1] = 0;
+ }
+ if(dbgctl != nil){
+ strncpy(debugger.ctl, dbgctl, sizeof(debugger.ctl));
+ debugger.ctl[sizeof(debugger.ctl)-1] = 0;
+ }
+ if(dbgctlstart != nil){
+ strncpy(debugger.ctlstart, dbgctlstart, sizeof(debugger.ctlstart));
+ debugger.ctlstart[sizeof(debugger.ctlstart)-1] = 0;
+ }
+ if(dbgctlstop != nil){
+ strncpy(debugger.ctlstop, dbgctlstop, sizeof(debugger.ctlstop));
+ debugger.ctlstop[sizeof(debugger.ctlstop)-1] = 0;
+ }
+ if(dbgctlflush != nil){
+ strncpy(debugger.ctlflush, dbgctlflush, sizeof(debugger.ctlflush));
+ debugger.ctlflush[sizeof(debugger.ctlflush)-1] = 0;
+ }
+ if(dbgstart)
+ start_debugger();
+
+ poperror();
+ wunlock(&debugger);
+}
+
+static Chan*
+dbgattach(char *spec)
+{
+ return devattach('b', spec);
+}
+
+static Walkqid*
+dbgwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, dbgdir, nelem(dbgdir), devgen);
+}
+
+static int
+dbgstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, dbgdir, nelem(dbgdir), devgen);
+}
+
+static Chan*
+dbgopen(Chan *c, int omode)
+{
+ return devopen(c, omode, dbgdir, nelem(dbgdir), devgen);
+}
+
+static long
+dbgread(Chan *c, void *buf, long n, vlong offset)
+{
+ char *ctlstate;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, buf, n, dbgdir, nelem(dbgdir), devgen);
+ case Qdbgctl:
+ rlock(&debugger);
+ ctlstate = smprint("%s data %s ctl %s ctlstart %s ctlstop %s ctlflush %s\n",
+ debugger.running ? "running" : "stopped",
+ debugger.data, debugger.ctl,
+ debugger.ctlstart, debugger.ctlstop, debugger.ctlflush);
+ runlock(&debugger);
+ if(ctlstate == nil)
+ error(Enomem);
+ if(waserror()){
+ free(ctlstate);
+ nexterror();
+ }
+ n = readstr(offset, buf, n, ctlstate);
+ poperror();
+ free(ctlstate);
+ return n;
+ case Qdbglog:
+ return qread(logq, buf, n);
+ default:
+ error(Egreg);
+ }
+ return -1; /* never reached */
+}
+
+static void
+ctl(Cmdbuf *cb)
+{
+ Debugger d;
+ int dbgstate = 0;
+ int i;
+ char *df;
+ int dfsize;
+ int setval;
+
+ memset(&d, 0, sizeof(d));
+ for(i=0; i < cb->nf; i++){
+ setval = 0;
+ df = nil;
+ dfsize = 0;
+ switch(cb->f[i][0]){
+ case 'd':
+ df = d.data;
+ dfsize = sizeof(d.data);
+ setval=1;
+ break;
+ case 'c':
+ df = d.ctl;
+ dfsize = sizeof(d.ctl);
+ setval=1;
+ break;
+ case 'i':
+ df = d.ctlstart;
+ dfsize = sizeof(d.ctlstart);
+ setval=1;
+ break;
+ case 'h':
+ df = d.ctlstop;
+ dfsize = sizeof(d.ctlstop);
+ setval=1;
+ break;
+ case 'f':
+ df = d.ctlflush;
+ dfsize = sizeof(d.ctlflush);
+ setval=1;
+ break;
+ case 'r':
+ dbgstate = DBGrun;
+ break;
+ case 's':
+ dbgstate = DBGstop;
+ break;
+ default:
+ error(Ebadcmd);
+ }
+ if(setval){
+ if(i+1 >= cb->nf)
+ cmderror(cb, Enumarg);
+ strncpy(df, cb->f[i+1], dfsize-1);
+ df[dfsize-1] = 0;
+ ++d.running;
+ ++i;
+ }
+ }
+
+ if(d.running){
+ wlock(&debugger);
+ if(debugger.running){
+ wunlock(&debugger);
+ error(Erunning);
+ }
+ if(d.data[0] != 0){
+ strcpy(debugger.data, d.data);
+ dbglog("data %s\n",debugger.data);
+ }
+ if(d.ctl[0] != 0){
+ strcpy(debugger.ctl, d.ctl);
+ dbglog("ctl %s\n",debugger.ctl);
+ }
+ if(d.ctlstart[0] != 0){
+ strcpy(debugger.ctlstart, d.ctlstart);
+ dbglog("ctlstart %s\n",debugger.ctlstart);
+ }
+ if(d.ctlstop[0] != 0){
+ strcpy(debugger.ctlstop, d.ctlstop);
+ dbglog("ctlstop %s\n",debugger.ctlstop);
+ }
+ wunlock(&debugger);
+ }
+
+ if(dbgstate == DBGrun){
+ if(!debugger.running){
+ wlock(&debugger);
+ if(waserror()){
+ wunlock(&debugger);
+ nexterror();
+ }
+ if(!debugger.running)
+ start_debugger();
+ else
+ dbglog("debugger already running\n");
+ poperror();
+ wunlock(&debugger);
+ } else
+ dbglog("debugger already running\n");
+ } else if(dbgstate == DBGstop){
+ if(debugger.running){
+ /* force hangup to stop the dbg process */
+ int cfd;
+ if(debugger.ctl[0] == 0)
+ return;
+ cfd = kopen(debugger.ctl, OWRITE);
+ if(cfd == -1)
+ error(up->env->errstr);
+ dbglog("stopping debugger\n");
+ if(kwrite(cfd, debugger.ctlstop, strlen(debugger.ctlstop)) == -1)
+ error(up->env->errstr);
+ kclose(cfd);
+ } else
+ dbglog("debugger not running\n");
+ }
+}
+
+static long
+dbgwrite(Chan *c, void *va, long n, vlong)
+{
+ Cmdbuf *cb;
+
+ switch((ulong)c->qid.path){
+ default:
+ error(Egreg);
+ break;
+ case Qdbgctl:
+ cb = parsecmd(va, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ctl(cb);
+ poperror();
+ break;
+ }
+ return n;
+}
+
+static void
+dbgclose(Chan*)
+{
+}
+
+Dev dbgdevtab = {
+ 'b',
+ "dbg",
+
+ devreset,
+ dbginit,
+ devshutdown,
+ dbgattach,
+ dbgwalk,
+ dbgstat,
+ dbgopen,
+ devcreate,
+ dbgclose,
+ dbgread,
+ devbread,
+ dbgwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};