xref: /inferno-os/os/port/devdbg.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "ureg.h"
7 #include "../port/error.h"
8 #include	"rdbg.h"
9 
10 #include	<kernel.h>
11 #include	<interp.h>
12 
13 /*
14  *	The following should be set in the config file to override
15  *	the defaults.
16  */
17 int	dbgstart;
18 char	*dbgdata;
19 char	*dbgctl;
20 char	*dbgctlstart;
21 char	*dbgctlstop;
22 char	*dbgctlflush;
23 
24 //
25 // Error messages sent to the remote debugger
26 //
27 static	uchar	Ereset[9] = { 'r', 'e', 's', 'e', 't' };
28 static	uchar	Ecount[9] = { 'c', 'o', 'u', 'n', 't' };
29 static	uchar	Eunk[9] = { 'u', 'n', 'k' };
30 static	uchar	Einval[9] = { 'i', 'n', 'v', 'a', 'l' };
31 static	uchar	Ebadpid[9] = {'p', 'i', 'd'};
32 static	uchar	Eunsup[9] = { 'u', 'n', 's', 'u', 'p' };
33 static	uchar	Enotstop[9] = { 'n', 'o', 't', 's', 't', 'o', 'p' };
34 
35 //
36 // Error messages raised via call to error()
37 //
38 static	char	Erunning[] = "Not allowed while debugger is running";
39 static	char	Enumarg[] = "Not enough args";
40 static	char	Ebadcmd[] = "Unknown command";
41 
42 static	int	PROCREG;
43 static	struct {
44 	Rendez;
45 	Bkpt *b;
46 } brk;
47 
48 static	Queue	*logq;
49 
50 int	dbgchat = 0;
51 
52 typedef struct Debugger Debugger;
53 struct Debugger {
54 	RWlock;
55 	int	running;
56 	char	data[PRINTSIZE];
57 	char	ctl[PRINTSIZE];
58 	char	ctlstart[PRINTSIZE];
59 	char	ctlstop[PRINTSIZE];
60 	char	ctlflush[PRINTSIZE];
61 };
62 
63 static Debugger debugger = {
64 	.data=		"#t/eia0",
65 	.ctl=		"#t/eia0ctl",
66 	.ctlstart=	"b19200",
67 	.ctlstop=	"h",
68 	.ctlflush=	"f",
69 };
70 
71 enum {
72 	BkptStackSize=	256,
73 };
74 
75 typedef struct SkipArg SkipArg;
76 struct SkipArg
77 {
78 	Bkpt *b;
79 	Proc *p;
80 };
81 
82 Bkpt	*breakpoints;
83 void	freecondlist(BkptCond *l);
84 
85 static int
86 getbreaks(ulong addr, Bkpt **a, int nb)
87 {
88 	Bkpt *b;
89 	int n;
90 
91 	n = 0;
92 	for(b = breakpoints; b != nil; b = b->next){
93 		if(b->addr == addr){
94 			a[n++] = b;
95 			if(n == nb)
96 				break;
97 		}
98 	}
99 	return n;
100 }
101 
102 Bkpt*
103 newbreak(int id, ulong addr, BkptCond *conds, void(*handler)(Bkpt*), void *aux)
104 {
105 	Bkpt *b;
106 
107 	b = malloc(sizeof(*b));
108 	if(b == nil)
109 		error(Enomem);
110 
111 	b->id = id;
112 	b->conditions = conds;
113 	b->addr = addr;
114 	b->handler = handler;
115 	b->aux = aux;
116 	b->next = nil;
117 
118 	return b;
119 }
120 
121 void
122 freebreak(Bkpt *b)
123 {
124 	freecondlist(b->conditions);
125 	free(b);
126 }
127 
128 BkptCond*
129 newcondition(uchar cmd, ulong val)
130 {
131 	BkptCond *c;
132 
133 	c = mallocz(sizeof(*c), 0);
134 	if(c == nil)
135 		error(Enomem);
136 
137 	c->op = cmd;
138 	c->val = val;
139 	c->next = nil;
140 
141 	return c;
142 }
143 
144 void
145 freecondlist(BkptCond *l)
146 {
147 	BkptCond *next;
148 
149 	while(l != nil){
150 		next = l->next;
151 		free(l);
152 		l = next;
153 	}
154 }
155 
156 
157 void
158 breakset(Bkpt *b)
159 {
160 	Bkpt *e[1];
161 
162 	if(getbreaks(b->addr, e, 1) != 0){
163 		b->instr = e[0]->instr;
164 	} else {
165 		b->instr = machinstr(b->addr);
166 		machbreakset(b->addr);
167 	}
168 
169 	b->next = breakpoints;
170 	breakpoints = b;
171 }
172 
173 void
174 breakrestore(Bkpt *b)
175 {
176 	b->next = breakpoints;
177 	breakpoints = b;
178 	machbreakset(b->addr);
179 }
180 
181 Bkpt*
182 breakclear(int id)
183 {
184 	Bkpt *b, *e, *p;
185 
186 	for(b=breakpoints, p=nil; b != nil && b->id != id; p = b, b = b->next)
187 		;
188 
189 	if(b != nil){
190 		if(p == nil)
191 			breakpoints = b->next;
192 		else
193 			p->next = b->next;
194 
195 		if(getbreaks(b->addr, &e, 1) == 0)
196 			machbreakclear(b->addr, b->instr);
197 	}
198 
199 	return b;
200 }
201 
202 void
203 breaknotify(Bkpt *b, Proc *p)
204 {
205 	p->dbgstop = 1;		// stop running this process.
206 	b->handler(b);
207 }
208 
209 int
210 breakmatch(BkptCond *cond, Ureg *ur, Proc *p)
211 {
212 	ulong a, b;
213 	int pos;
214 	ulong s[BkptStackSize];
215 
216 	memset(s, 0, sizeof(s));
217 	pos = 0;
218 
219 	for(;cond != nil; cond = cond->next){
220 		switch(cond->op){
221 		default:
222 			panic("breakmatch: unknown operator %c", cond->op);
223 			break;
224 		case 'k':
225 			if(p == nil || p->pid != cond->val)
226 				return 0;
227 			s[pos++] = 1;
228 			break;
229 		case 'b':
230 			if(ur->pc != cond->val)
231 				return 0;
232 			s[pos++] = 1;
233 			break;
234 		case 'p': s[pos++] = cond->val; break;
235 		case '*': a = *(ulong*)s[--pos]; s[pos++] = a; break;
236 		case '&': a = s[--pos]; b = s[--pos]; s[pos++] = a & b; break;
237 		case '=': a = s[--pos]; b = s[--pos]; s[pos++] = a == b; break;
238 		case '!': a = s[--pos]; b = s[--pos]; s[pos++] = a != b; break;
239 		case 'a': a = s[--pos]; b = s[--pos]; s[pos++] = a && b; break;
240 		case 'o': a = s[--pos]; b = s[--pos]; s[pos++] = a || b; break;
241 		}
242 	}
243 
244 	if(pos && s[pos-1])
245 		return 1;
246 	return 0;
247 }
248 
249 void
250 breakinit(void)
251 {
252 	machbreakinit();
253 }
254 
255 static void
256 dbglog(char *fmt, ...)
257 {
258 	int n;
259 	va_list arg;
260 	char buf[PRINTSIZE];
261 
262 	va_start(arg, fmt);
263 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
264 	va_end(arg);
265 	qwrite(logq, buf, n);
266 }
267 
268 static int
269 get(int dbgfd, uchar *b)
270 {
271 	int i;
272 	uchar c;
273 
274 	if(kread(dbgfd, &c, 1) < 0)
275 		error(Eio);
276 	for(i=0; i<9; i++){
277 		if(kread(dbgfd, b++, 1) < 0)
278 			error(Eio);
279 	}
280 	return c;
281 }
282 
283 static void
284 mesg(int dbgfd, int m, uchar *buf)
285 {
286 	int i;
287 	uchar c;
288 
289 	c = m;
290 	if(kwrite(dbgfd, &c, 1) < 0)
291 		error(Eio);
292 	for(i=0; i<9; i++){
293 		if(kwrite(dbgfd, buf+i, 1) < 0)
294 			error(Eio);
295 	}
296 }
297 
298 static ulong
299 dbglong(uchar *s)
300 {
301 	return (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0);
302 }
303 
304 static Proc *
305 dbgproc(ulong pid, int dbgok)
306 {
307 	int i;
308 	Proc *p;
309 
310 	if(!dbgok && pid == up->pid)
311 		return 0;
312 	p = proctab(0);
313 	for(i = 0; i < conf.nproc; i++){
314 		if(p->pid == pid)
315 			return p;
316 		p++;
317 	}
318 	return 0;
319 }
320 
321 static void*
322 addr(uchar *s)
323 {
324 	ulong a;
325 	Proc *p;
326 	static Ureg ureg;
327 
328 	a = ((s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]<<0));
329 	if(a < sizeof(Ureg)){
330 		p = dbgproc(PROCREG, 0);
331 		if(p == 0){
332 			dbglog("dbg: invalid pid\n");
333 			return 0;
334 		}
335 		if(p->dbgreg){
336 			/* in trap(), registers are all on stack */
337 			memmove(&ureg, p->dbgreg, sizeof(ureg));
338 		}
339 		else {
340 			/* not in trap, only pc and sp are available */
341 			memset(&ureg, 0, sizeof(ureg));
342 			ureg.sp = p->sched.sp;
343 			ureg.pc = p->sched.pc;
344 		}
345 		return (uchar*)&ureg+a;
346 	}
347 	return (void*)a;
348 }
349 
350 
351 static void
352 dumpcmd(uchar cmd, uchar *min)
353 {
354 	char *s;
355 	int n;
356 
357 	switch(cmd){
358 	case Terr:		s = "Terr"; break;
359 	case Tmget:		s = "Tmget"; break;
360 	case Tmput:		s = "Tmput"; break;
361 	case Tspid:		s = "Tspid"; break;
362 	case Tproc:		s = "Tproc"; break;
363 	case Tstatus:		s = "Tstatus"; break;
364 	case Trnote:		s = "Trnote"; break;
365 	case Tstartstop:	s = "Tstartstop"; break;
366 	case Twaitstop:		s = "Twaitstop"; break;
367 	case Tstart:		s = "Tstart"; break;
368 	case Tstop:		s = "Tstop"; break;
369 	case Tkill:		s = "Tkill"; break;
370 	case Tcondbreak:	s = "Tcondbreak"; break;
371 	default:		s = "<Unknown>"; break;
372 	}
373 	dbglog("%s: [%2.2ux]: ", s, cmd);
374 	for(n = 0; n < 9; n++)
375 		dbglog("%2.2ux", min[n]);
376 	dbglog("\n");
377 }
378 
379 static int
380 brkpending(void *a)
381 {
382 	Proc *p;
383 
384 	p = a;
385 	if(brk.b != nil) return 1;
386 
387 	p->dbgstop = 0;			/* atomic */
388 	if(p->state == Stopped)
389 		ready(p);
390 
391 	return 0;
392 }
393 
394 static void
395 gotbreak(Bkpt *b)
396 {
397 	Bkpt *cur, *prev;
398 
399 	b->link = nil;
400 
401 	for(prev = nil, cur = brk.b; cur != nil; prev = cur, cur = cur->link)
402 		;
403 	if(prev == nil)
404 		brk.b = b;
405 	else
406 		prev->link = b;
407 
408 	wakeup(&brk);
409 }
410 
411 static int
412 startstop(Proc *p)
413 {
414 	int id;
415 	int s;
416 	Bkpt *b;
417 
418 	sleep(&brk, brkpending, p);
419 
420 	s = splhi();
421 	b = brk.b;
422 	brk.b = b->link;
423 	splx(s);
424 
425 	id = b->id;
426 
427 	return id;
428 }
429 
430 static int
431 condbreak(char cmd, ulong val)
432 {
433 	BkptCond *c;
434 	static BkptCond *head = nil;
435 	static BkptCond *tail = nil;
436 	static Proc *p = nil;
437 	static int id = -1;
438 	int s;
439 
440 	if(waserror()){
441 		dbglog(up->env->errstr);
442 		freecondlist(head);
443 		head = tail = nil;
444 		p = nil;
445 		id = -1;
446 		return 0;
447 	}
448 
449 	switch(cmd){
450 	case 'b': case 'p':
451 	case '*': case '&': case '=':
452 	case '!': case 'a': case 'o':
453 		break;
454 	case 'n':
455 		id = val;
456 		poperror();
457 		return 1;
458 	case 'k':
459 		p = dbgproc(val, 0);
460 		if(p == nil)
461 			error("k: unknown pid");
462 		break;
463 	case 'd': {
464 		Bkpt *b;
465 
466 		s = splhi();
467 		b = breakclear(val);
468 		if(b != nil){
469 			Bkpt *cur, *prev;
470 
471 			prev = nil;
472 			cur = brk.b;
473 			while(cur != nil){
474 				if(cur->id == b->id){
475 					if(prev == nil)
476 						brk.b = cur->link;
477 					else
478 						prev->link = cur->link;
479 					break;
480 				}
481 				cur = cur->link;
482 			}
483 			freebreak(b);
484 		}
485 		splx(s);
486 		poperror();
487 		return 1;
488 		}
489 	default:
490 		dbglog("condbreak(): unknown op %c %lux\n", cmd, val);
491 		error("unknown op");
492 	}
493 
494 	c = newcondition(cmd, val);
495 
496 	 //
497 	 // the 'b' command comes last, (so we know we have reached the end
498 	 // of the condition list), but it should be the first thing
499 	 // checked, so put it at the head.
500 	 //
501 	if(cmd == 'b'){
502 		if(p == nil) error("no pid");
503 		if(id == -1) error("no id");
504 
505 		c->next = head;
506 		s = splhi();
507 		breakset(newbreak(id, val, c, gotbreak, p));
508 		splx(s);
509 		head = tail = nil;
510 		p = nil;
511 		id = -1;
512 	} else if(tail != nil){
513 		tail->next = c;
514 		tail = c;
515 	} else
516 		head = tail = c;
517 
518 	poperror();
519 
520 	return 1;
521 }
522 
523 static void
524 dbg(void*)
525 {
526 	Proc *p;
527 	ulong val;
528 	int n, cfd, dfd;
529 	uchar cmd, *a, min[RDBMSGLEN-1], mout[RDBMSGLEN-1];
530 
531 	rlock(&debugger);
532 
533 	setpri(PriRealtime);
534 
535 	closefgrp(up->env->fgrp);
536 	up->env->fgrp = newfgrp(nil);
537 
538 	if(waserror()){
539 		dbglog("dbg: quits: %s\n", up->env->errstr);
540 		runlock(&debugger);
541 		wlock(&debugger);
542 		debugger.running = 0;
543 		wunlock(&debugger);
544 		pexit("", 0);
545 	}
546 
547 	dfd = kopen(debugger.data, ORDWR);
548 	if(dfd < 0){
549 		dbglog("dbg: can't open %s: %s\n",debugger.data, up->env->errstr);
550 		error(Eio);
551 	}
552 	if(waserror()){
553 		kclose(dfd);
554 		nexterror();
555 	}
556 
557 	if(debugger.ctl[0] != 0){
558 		cfd = kopen(debugger.ctl, ORDWR);
559 		if(cfd < 0){
560 			dbglog("dbg: can't open %s: %s\n", debugger.ctl, up->env->errstr);
561 			error(Eio);
562 		}
563 		if(kwrite(cfd, debugger.ctlstart, strlen(debugger.ctlstart)) < 0){
564 			dbglog("dbg: write %s: %s\n", debugger.ctl, up->env->errstr);
565 			error(Eio);
566 		}
567 	}else
568 		cfd = -1;
569 	if(waserror()){
570 		if(cfd != -1){
571 			kwrite(cfd, debugger.ctlflush, strlen(debugger.ctlflush));
572 			kclose(cfd);
573 		}
574 		nexterror();
575 	}
576 
577 	mesg(dfd, Rerr, Ereset);
578 
579 	for(;;){
580 		memset(mout, 0, sizeof(mout));
581 		cmd = get(dfd, min);
582 		if(dbgchat)
583 			dumpcmd(cmd, min);
584 		switch(cmd){
585 		case Tmget:
586 			n = min[4];
587 			if(n > 9){
588 				mesg(dfd, Rerr, Ecount);
589 				break;
590 			}
591 			a = addr(min+0);
592 			if(!isvalid_va(a)){
593 				mesg(dfd, Rerr, Einval);
594 				break;
595 			}
596 			memmove(mout, a, n);
597 			mesg(dfd, Rmget, mout);
598 			break;
599 		case Tmput:
600 			n = min[4];
601 			if(n > 4){
602 				mesg(dfd, Rerr, Ecount);
603 				break;
604 			}
605 			a = addr(min+0);
606 			if(!isvalid_va(a)){
607 				mesg(dfd, Rerr, Einval);
608 				break;
609 			}
610 			memmove(a, min+5, n);
611 			segflush(a, n);
612 			mesg(dfd, Rmput, mout);
613 			break;
614 		case Tproc:
615 			p = dbgproc(dbglong(min+0), 0);
616 			if(p == 0){
617 				mesg(dfd, Rerr, Ebadpid);
618 				break;
619 			}
620 			PROCREG = p->pid;	/* try this instead of Tspid */
621 			sprint((char*)mout, "%8.8lux", p);
622 			mesg(dfd, Rproc, mout);
623 			break;
624 		case Tstatus:
625 			p = dbgproc(dbglong(min+0), 1);
626 			if(p == 0){
627 				mesg(dfd, Rerr, Ebadpid);
628 				break;
629 			}
630 			if(p->state > Rendezvous || p->state < Dead)
631 				sprint((char*)mout, "%8.8ux", p->state);
632 			else if(p->dbgstop == 1)
633 				strncpy((char*)mout, statename[Stopped], sizeof(mout));
634 			else
635 				strncpy((char*)mout, statename[p->state], sizeof(mout));
636 			mesg(dfd, Rstatus, mout);
637 			break;
638 		case Trnote:
639 			p = dbgproc(dbglong(min+0), 0);
640 			if(p == 0){
641 				mesg(dfd, Rerr, Ebadpid);
642 				break;
643 			}
644 			mout[0] = 0;	/* should be trap status, if any */
645 			mesg(dfd, Rrnote, mout);
646 			break;
647 		case Tstop:
648 			p = dbgproc(dbglong(min+0), 0);
649 			if(p == 0){
650 				mesg(dfd, Rerr, Ebadpid);
651 				break;
652 			}
653 			p->dbgstop = 1;			/* atomic */
654 			mout[0] = 0;
655 			mesg(dfd, Rstop, mout);
656 			break;
657 		case Tstart:
658 			p = dbgproc(dbglong(min+0), 0);
659 			if(p == 0){
660 				mesg(dfd, Rerr, Ebadpid);
661 				break;
662 			}
663 			p->dbgstop = 0;			/* atomic */
664 			if(p->state == Stopped)
665 				ready(p);
666 			mout[0] = 0;
667 			mesg(dfd, Rstart, mout);
668 			break;
669 		case Tstartstop:
670 			p = dbgproc(dbglong(min+0), 0);
671 			if(p == 0){
672 				mesg(dfd, Rerr, Ebadpid);
673 				break;
674 			}
675 			if(!p->dbgstop){
676 				mesg(dfd, Rerr, Enotstop);
677 				break;
678 			}
679 			mout[0] = startstop(p);
680 			mesg(dfd, Rstartstop, mout);
681 			break;
682 		case Tcondbreak:
683 			val = dbglong(min+0);
684 			if(!condbreak(min[4], val)){
685 				mesg(dfd, Rerr, Eunk);
686 				break;
687 			}
688 			mout[0] = 0;
689 			mesg(dfd, Rcondbreak, mout);
690 			break;
691 		default:
692 			dumpcmd(cmd, min);
693 			mesg(dfd, Rerr, Eunk);
694 			break;
695 		}
696 	}
697 }
698 
699 static void
700 dbgnote(Proc *p, Ureg *ur)
701 {
702 	if(p){
703 		p->dbgreg = ur;
704 		PROCREG = p->pid;	/* acid can get the trap info from regs */
705 	}
706 }
707 
708 enum {
709 	Qdir,
710 	Qdbgctl,
711 	Qdbglog,
712 
713 	DBGrun = 1,
714 	DBGstop = 2,
715 
716 	Loglimit = 4096,
717 };
718 
719 static Dirtab dbgdir[]=
720 {
721 	".",		{Qdir, 0, QTDIR},	0,	0555,
722 	"dbgctl",	{Qdbgctl},	0,		0660,
723 	"dbglog",	{Qdbglog},	0,		0440,
724 };
725 
726 static void
727 start_debugger(void)
728 {
729 	breakinit();
730 	dbglog("starting debugger\n");
731 	debugger.running++;
732 	kproc("dbg", dbg, 0, KPDUPPG);
733 }
734 
735 static void
736 dbginit(void)
737 {
738 
739 	logq = qopen(Loglimit, 0, 0, 0);
740 	if(logq == nil)
741 		error(Enomem);
742 	qnoblock(logq, 1);
743 
744 	wlock(&debugger);
745 	if(waserror()){
746 		wunlock(&debugger);
747 		qfree(logq);
748 		logq = nil;
749 		nexterror();
750 	}
751 
752 	if(dbgdata != nil){
753 		strncpy(debugger.data, dbgdata, sizeof(debugger.data));
754 		debugger.data[sizeof(debugger.data)-1] = 0;
755 	}
756 	if(dbgctl != nil){
757 		strncpy(debugger.ctl, dbgctl, sizeof(debugger.ctl));
758 		debugger.ctl[sizeof(debugger.ctl)-1] = 0;
759 	}
760 	if(dbgctlstart != nil){
761 		strncpy(debugger.ctlstart, dbgctlstart, sizeof(debugger.ctlstart));
762 		debugger.ctlstart[sizeof(debugger.ctlstart)-1] = 0;
763 	}
764 	if(dbgctlstop != nil){
765 		strncpy(debugger.ctlstop, dbgctlstop, sizeof(debugger.ctlstop));
766 		debugger.ctlstop[sizeof(debugger.ctlstop)-1] = 0;
767 	}
768 	if(dbgctlflush != nil){
769 		strncpy(debugger.ctlflush, dbgctlflush, sizeof(debugger.ctlflush));
770 		debugger.ctlflush[sizeof(debugger.ctlflush)-1] = 0;
771 	}
772 	if(dbgstart)
773 		start_debugger();
774 
775 	poperror();
776 	wunlock(&debugger);
777 }
778 
779 static Chan*
780 dbgattach(char *spec)
781 {
782 	return devattach('b', spec);
783 }
784 
785 static Walkqid*
786 dbgwalk(Chan *c, Chan *nc, char **name, int nname)
787 {
788 	return devwalk(c, nc, name, nname, dbgdir, nelem(dbgdir), devgen);
789 }
790 
791 static int
792 dbgstat(Chan *c, uchar *dp, int n)
793 {
794 	return devstat(c, dp, n, dbgdir, nelem(dbgdir), devgen);
795 }
796 
797 static Chan*
798 dbgopen(Chan *c, int omode)
799 {
800 	return devopen(c, omode, dbgdir, nelem(dbgdir), devgen);
801 }
802 
803 static long
804 dbgread(Chan *c, void *buf, long n, vlong offset)
805 {
806 	char *ctlstate;
807 
808 	switch((ulong)c->qid.path){
809 	case Qdir:
810 		return devdirread(c, buf, n, dbgdir, nelem(dbgdir), devgen);
811 	case Qdbgctl:
812 		rlock(&debugger);
813 		ctlstate = smprint("%s data %s ctl %s ctlstart %s ctlstop %s ctlflush %s\n",
814 			debugger.running ? "running" : "stopped",
815 			debugger.data, debugger.ctl,
816 			debugger.ctlstart, debugger.ctlstop, debugger.ctlflush);
817 		runlock(&debugger);
818 		if(ctlstate == nil)
819 			error(Enomem);
820 		if(waserror()){
821 			free(ctlstate);
822 			nexterror();
823 		}
824 		n = readstr(offset, buf, n, ctlstate);
825 		poperror();
826 		free(ctlstate);
827 		return n;
828 	case Qdbglog:
829 		return qread(logq, buf, n);
830 	default:
831 		error(Egreg);
832 	}
833 	return -1;		/* never reached */
834 }
835 
836 static void
837 ctl(Cmdbuf *cb)
838 {
839 	Debugger d;
840 	int dbgstate = 0;
841 	int i;
842 	char *df;
843 	int dfsize;
844 	int setval;
845 
846 	memset(&d, 0, sizeof(d));
847 	for(i=0; i < cb->nf; i++){
848 		setval = 0;
849 		df = nil;
850 		dfsize = 0;
851 		switch(cb->f[i][0]){
852 		case 'd':
853 			df = d.data;
854 			dfsize = sizeof(d.data);
855 			setval=1;
856 			break;
857 		case 'c':
858 			df = d.ctl;
859 			dfsize = sizeof(d.ctl);
860 			setval=1;
861 			break;
862 		case 'i':
863 			df = d.ctlstart;
864 			dfsize = sizeof(d.ctlstart);
865 			setval=1;
866 			break;
867 		case 'h':
868 			df = d.ctlstop;
869 			dfsize = sizeof(d.ctlstop);
870 			setval=1;
871 			break;
872 		case 'f':
873 			df = d.ctlflush;
874 			dfsize = sizeof(d.ctlflush);
875 			setval=1;
876 			break;
877 		case 'r':
878 			dbgstate = DBGrun;
879 			break;
880 		case 's':
881 			dbgstate = DBGstop;
882 			break;
883 		default:
884 			error(Ebadcmd);
885 		}
886 		if(setval){
887 			if(i+1 >= cb->nf)
888 				cmderror(cb, Enumarg);
889 			strncpy(df, cb->f[i+1], dfsize-1);
890 			df[dfsize-1] = 0;
891 			++d.running;
892 			++i;
893 		}
894 	}
895 
896 	if(d.running){
897 		wlock(&debugger);
898 		if(debugger.running){
899 			wunlock(&debugger);
900 			error(Erunning);
901 		}
902 		if(d.data[0] != 0){
903 			strcpy(debugger.data, d.data);
904 			dbglog("data %s\n",debugger.data);
905 		}
906 		if(d.ctl[0] != 0){
907 			strcpy(debugger.ctl, d.ctl);
908 			dbglog("ctl %s\n",debugger.ctl);
909 		}
910 		if(d.ctlstart[0] != 0){
911 			strcpy(debugger.ctlstart, d.ctlstart);
912 			dbglog("ctlstart %s\n",debugger.ctlstart);
913 		}
914 		if(d.ctlstop[0] != 0){
915 			strcpy(debugger.ctlstop, d.ctlstop);
916 			dbglog("ctlstop %s\n",debugger.ctlstop);
917 		}
918 		wunlock(&debugger);
919 	}
920 
921 	if(dbgstate == DBGrun){
922 		if(!debugger.running){
923 			wlock(&debugger);
924 			if(waserror()){
925 				wunlock(&debugger);
926 				nexterror();
927 			}
928 			if(!debugger.running)
929 				start_debugger();
930 			else
931 				dbglog("debugger already running\n");
932 			poperror();
933 			wunlock(&debugger);
934 		} else
935 			dbglog("debugger already running\n");
936 	} else if(dbgstate == DBGstop){
937 		if(debugger.running){
938 			/* force hangup to stop the dbg process */
939 			int cfd;
940 			if(debugger.ctl[0] == 0)
941 				return;
942 			cfd = kopen(debugger.ctl, OWRITE);
943 			if(cfd == -1)
944 				error(up->env->errstr);
945 			dbglog("stopping debugger\n");
946 			if(kwrite(cfd, debugger.ctlstop, strlen(debugger.ctlstop)) == -1)
947 				error(up->env->errstr);
948 			kclose(cfd);
949 		} else
950 			dbglog("debugger not running\n");
951 	}
952 }
953 
954 static long
955 dbgwrite(Chan *c, void *va, long n, vlong)
956 {
957 	Cmdbuf *cb;
958 
959 	switch((ulong)c->qid.path){
960 	default:
961 		error(Egreg);
962 		break;
963 	case Qdbgctl:
964 		cb = parsecmd(va, n);
965 		if(waserror()){
966 			free(cb);
967 			nexterror();
968 		}
969 		ctl(cb);
970 		poperror();
971 		break;
972 	}
973 	return n;
974 }
975 
976 static void
977 dbgclose(Chan*)
978 {
979 }
980 
981 Dev dbgdevtab = {
982 	'b',
983 	"dbg",
984 
985 	devreset,
986 	dbginit,
987 	devshutdown,
988 	dbgattach,
989 	dbgwalk,
990 	dbgstat,
991 	dbgopen,
992 	devcreate,
993 	dbgclose,
994 	dbgread,
995 	devbread,
996 	dbgwrite,
997 	devbwrite,
998 	devremove,
999 	devwstat,
1000 };
1001