xref: /inferno-os/emu/port/devprog.c (revision 54d06533f3d8d73d9369206f7f5be71351cfbcf3)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	<interp.h>
5 #include	<isa.h>
6 #include	"runt.h"
7 
8 /*
9  * Enable the heap device for environments that allow debugging =>
10  * Must be 1 for a production environment.
11  */
12 int	SECURE = 0;
13 
14 enum
15 {
16 	Qdir,
17 	Qctl,
18 	Qdbgctl,
19 	Qheap,
20 	Qns,
21 	Qnsgrp,
22 	Qpgrp,
23 	Qstack,
24 	Qstatus,
25 	Qtext,
26 	Qwait,
27 	Qfd,
28 	Qexception,
29 };
30 
31 /*
32  * must be in same order as enum
33  */
34 Dirtab progdir[] =
35 {
36 	"ctl",		{Qctl},		0,			0200,
37 	"dbgctl",	{Qdbgctl},	0,			0600,
38 	"heap",		{Qheap},	0,			0600,
39 	"ns",		{Qns},		0,			0400,
40 	"nsgrp",	{Qnsgrp},	0,			0444,
41 	"pgrp",		{Qpgrp},	0,			0444,
42 	"stack",	{Qstack},	0,			0400,
43 	"status",	{Qstatus},	0,			0444,
44 	"text",		{Qtext},	0,			0000,
45 	"wait",		{Qwait},	0,			0400,
46 	"fd",		{Qfd},		0,			0400,
47 	"exception",	{Qexception},	0,	0400,
48 };
49 
50 enum
51 {
52 	CMkill,
53 	CMkillgrp,
54 	CMrestricted,
55 	CMexceptions,
56 	CMprivate
57 };
58 
59 static
60 Cmdtab progcmd[] = {
61 	CMkill,	"kill",	1,
62 	CMkillgrp,	"killgrp",	1,
63 	CMrestricted, "restricted", 1,
64 	CMexceptions, "exceptions", 2,
65 	CMprivate, "private",	1,
66 };
67 
68 enum
69 {
70 	CDstep,
71 	CDtoret,
72 	CDcont,
73 	CDstart,
74 	CDstop,
75 	CDunstop,
76 	CDmaim,
77 	CDbpt
78 };
79 
80 static
81 Cmdtab progdbgcmd[] = {
82 	CDstep,	"step",	0,	/* known below to be first, to cope with stepN */
83 	CDtoret,	"toret",	1,
84 	CDcont,	"cont",	1,
85 	CDstart,	"start",	1,
86 	CDstop,	"stop",	1,
87 	CDunstop,	"unstop",	1,
88 	CDmaim,	"maim",	1,
89 	CDbpt,	"bpt",	4,
90 };
91 
92 typedef struct Heapqry Heapqry;
93 struct Heapqry
94 {
95 	char	fmt;
96 	ulong	addr;
97 	ulong	module;
98 	int	count;
99 };
100 
101 typedef struct Bpt	Bpt;
102 
103 struct Bpt
104 {
105 	Bpt	*next;
106 	int	pc;
107 	char	*file;
108 	char	path[1];
109 };
110 
111 typedef struct Progctl Progctl;
112 struct Progctl
113 {
114 	Rendez	r;
115 	int	ref;
116 	Proc	*debugger;	/* waiting for dbgxec */
117 	char	*msg;		/* reply from dbgxec */
118 	int	step;		/* instructions to try */
119 	int	stop;		/* stop running the program */
120 	Bpt*	bpts;		/* active breakpoints */
121 	Queue*	q;		/* status queue */
122 };
123 
124 #define	QSHIFT		4		/* location in qid of pid */
125 #define	QID(q)		(((ulong)(q).path&0x0000000F)>>0)
126 #define QPID(pid)	(((pid)<<QSHIFT))
127 #define	PID(q)		((q).vers)
128 #define PATH(q)		((ulong)(q).path&~((1<<QSHIFT)-1))
129 
130 static char *progstate[] =			/* must correspond to include/interp.h */
131 {
132 	"alt",				/* blocked in alt instruction */
133 	"send",				/* waiting to send */
134 	"recv",				/* waiting to recv */
135 	"debug",			/* debugged */
136 	"ready",			/* ready to be scheduled */
137 	"release",			/* interpreter released */
138 	"exiting",			/* exit because of kill or error */
139 	"broken",			/* thread crashed */
140 };
141 
142 static	void	dbgstep(Progctl*, Prog*, int);
143 static	void	dbgstart(Prog*);
144 static	void	freebpts(Bpt*);
145 static	Bpt*	delbpt(Bpt*, char*, int);
146 static	Bpt*	setbpt(Bpt*, char*, int);
147 static	void	mntscan(Mntwalk*, Pgrp*);
148 extern	Type	*Trdchan;
149 extern	Type	*Twrchan;
150 extern	Module*	modules;
151 static  char 	Emisalign[] = "misaligned address";
152 
153 static int
154 proggen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
155 {
156 	Qid qid;
157 	Prog *p;
158 	char *e;
159 	Osenv *o;
160 	ulong pid, path, perm, len;
161 
162 	USED(ntab);
163 
164 	if(s == DEVDOTDOT){
165 		mkqid(&qid, Qdir, 0, QTDIR);
166 		devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp);
167 		return 1;
168 	}
169 
170 	if((ulong)c->qid.path == Qdir) {
171 		if(name != nil){
172 			/* ignore s and use name to find pid */
173 			pid = strtoul(name, &e, 0);
174 			if(pid == 0 || *e != '\0')
175 				return -1;
176 			acquire();
177 			p = progpid(pid);
178 			if(p == nil){
179 				release();
180 				return -1;
181 			}
182 		}else{
183 			acquire();
184 			p = progn(s);
185 			if(p == nil) {
186 				release();
187 				return -1;
188 			}
189 			pid = p->pid;
190 		}
191 		o = p->osenv;
192 		sprint(up->genbuf, "%lud", pid);
193 		if(name != nil && strcmp(name, up->genbuf) != 0){
194 			release();
195 			return -1;
196 		}
197 		mkqid(&qid, pid<<QSHIFT, pid, QTDIR);
198 		devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp);
199 		release();
200 		return 1;
201 	}
202 
203 	if(s >= nelem(progdir))
204 		return -1;
205 	tab = &progdir[s];
206 	path = PATH(c->qid);
207 
208 	acquire();
209 	p = progpid(PID(c->qid));
210 	if(p == nil) {
211 		release();
212 		return -1;
213 	}
214 
215 	o = p->osenv;
216 
217 	perm = tab->perm;
218 	if((perm & 7) == 0)
219 		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
220 
221 	len = tab->length;
222 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
223 	devdir(c, qid, tab->name, len, o->user, perm, dp);
224 	release();
225 	return 1;
226 }
227 
228 static Chan*
229 progattach(char *spec)
230 {
231 	return devattach('p', spec);
232 }
233 
234 static Walkqid*
235 progwalk(Chan *c, Chan *nc, char **name, int nname)
236 {
237 	return devwalk(c, nc, name, nname, 0, 0, proggen);
238 }
239 
240 static int
241 progstat(Chan *c, uchar *db, int n)
242 {
243 	return devstat(c, db, n, 0, 0, proggen);
244 }
245 
246 static Chan*
247 progopen(Chan *c, int omode)
248 {
249 	Prog *p;
250 	Osenv *o;
251 	Progctl *ctl;
252 	int perm;
253 
254 	if(c->qid.type & QTDIR)
255 		return devopen(c, omode, 0, 0, proggen);
256 
257 	acquire();
258 	if (waserror()) {
259 		release();
260 		nexterror();
261 	}
262 	p = progpid(PID(c->qid));
263 	if(p == nil)
264 		error(Ethread);
265 	o = p->osenv;
266 	perm = progdir[QID(c->qid)-1].perm;
267 	if((perm & 7) == 0)
268 		perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode;
269 	devpermcheck(o->user, perm, omode);
270 	omode = openmode(omode);
271 
272 	switch(QID(c->qid)){
273 	default:
274 		error(Egreg);
275 	case Qnsgrp:
276 	case Qpgrp:
277 	case Qtext:
278 	case Qstatus:
279 	case Qstack:
280 	case Qctl:
281 	case Qfd:
282 	case Qexception:
283 		break;
284 	case Qwait:
285 		c->aux = qopen(1024, Qmsg, nil, nil);
286 		if(c->aux == nil)
287 			error(Enomem);
288 		o->childq = c->aux;
289 		break;
290 	case Qns:
291 		c->aux = malloc(sizeof(Mntwalk));
292 		if(c->aux == nil)
293 			error(Enomem);
294 		break;
295 	case Qheap:
296 		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
297 			error(Eperm);
298 		c->aux = malloc(sizeof(Heapqry));
299 		if(c->aux == nil)
300 			error(Enomem);
301 		break;
302 	case Qdbgctl:
303 		if(SECURE || p->group->flags&Pprivatemem || omode != ORDWR)
304 			error(Eperm);
305 		ctl = malloc(sizeof(Progctl));
306 		if(ctl == nil)
307 			error(Enomem);
308 		ctl->q = qopen(1024, Qmsg, nil, nil);
309 		if(ctl->q == nil) {
310 			free(ctl);
311 			error(Enomem);
312 		}
313 		ctl->bpts = nil;
314 		ctl->ref = 1;
315 		c->aux = ctl;
316 		break;
317 	}
318 	if(p->state != Pexiting)
319 		c->qid.vers = p->pid;
320 
321 	poperror();
322 	release();
323 	c->offset = 0;
324 	c->mode = omode;
325 	c->flag |= COPEN;
326 	return c;
327 }
328 
329 static int
330 progwstat(Chan *c, uchar *db, int n)
331 {
332 	Dir d;
333 	Prog *p;
334 	char *u;
335 	Osenv *o;
336 
337 	if(c->qid.type&QTDIR)
338 		error(Eperm);
339 	acquire();
340 	p = progpid(PID(c->qid));
341 	if(p == nil) {
342 		release();
343 		error(Ethread);
344 	}
345 
346 	u = up->env->user;
347 	o = p->osenv;
348 	if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) {
349 		release();
350 		error(Eperm);
351 	}
352 
353 	n = convM2D(db, n, &d, nil);
354 	if(n == 0){
355 		release();
356 		error(Eshortstat);
357 	}
358 	if(d.mode != ~0UL)
359 		o->pgrp->progmode = d.mode&0777;
360 	release();
361 	return n;
362 }
363 
364 static void
365 closedbgctl(Progctl *ctl, Prog *p)
366 {
367 	Osenv *o;
368 
369 	if(ctl->ref-- > 1)
370 		return;
371 	freebpts(ctl->bpts);
372 	if(p != nil){
373 		o = p->osenv;
374 		if(o->debug == ctl){
375 			o->debug = nil;
376 			p->xec = xec;
377 		}
378 	}
379 	qfree(ctl->q);
380 	free(ctl);
381 }
382 
383 static void
384 progclose(Chan *c)
385 {
386 	int i;
387 	Prog *f;
388 	Osenv *o;
389 	Progctl *ctl;
390 
391 	switch(QID(c->qid)) {
392 	case Qns:
393 	case Qheap:
394 		free(c->aux);
395 		break;
396 	case Qdbgctl:
397 		if((c->flag & COPEN) == 0)
398 			return;
399 		ctl = c->aux;
400 		acquire();
401 		closedbgctl(ctl, progpid(PID(c->qid)));
402 		release();
403 		break;
404 	case Qwait:
405 		acquire();
406 		i = 0;
407 		for(;;) {
408 			f = progn(i++);
409 			if(f == nil)
410 				break;
411 			o = f->osenv;
412 			if(o->waitq == c->aux)
413 				o->waitq = nil;
414 			if(o->childq == c->aux)
415 				o->childq = nil;
416 		}
417 		release();
418 		qfree(c->aux);
419 	}
420 }
421 
422 static int
423 progsize(Prog *p)
424 {
425 	int size;
426 	Frame *f;
427 	uchar *fp;
428 	Modlink *m;
429 
430 	m = p->R.M;
431 	size = 0;
432 	if(m->MP != H)
433 		size += hmsize(D2H(m->MP));
434 	if(m->prog != nil)
435 		size += msize(m->prog);
436 
437 	fp = p->R.FP;
438 	while(fp != nil) {
439 		f = (Frame*)fp;
440 		fp = f->fp;
441 		if(f->mr != nil) {
442 			if(f->mr->MP != H)
443 				size += hmsize(D2H(f->mr->MP));
444 			if(f->mr->prog != nil)
445 				size += msize(f->mr->prog);
446 		}
447 		if(f->t == nil)
448 			size += msize(SEXTYPE(f));
449 	}
450 	return size/1024;
451 }
452 
453 static long
454 progoffset(long offset, char *va, int *np)
455 {
456 	if(offset > 0) {
457 		offset -= *np;
458 		if(offset < 0) {
459 			memmove(va, va+*np+offset, -offset);
460 			*np = -offset;
461 		}
462 		else
463 			*np = 0;
464 	}
465 	return offset;
466 }
467 
468 static int
469 progqidwidth(Chan *c)
470 {
471 	char buf[32];
472 
473 	return sprint(buf, "%lud", c->qid.vers);
474 }
475 
476 int
477 progfdprint(Chan *c, int fd, int w, char *s, int ns)
478 {
479 	int n;
480 
481 	if(w == 0)
482 		w = progqidwidth(c);
483 	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
484 		fd,
485 		&"r w rw"[(c->mode&3)<<1],
486 		devtab[c->type]->dc, c->dev,
487 		c->qid.path, w, c->qid.vers, c->qid.type,
488 		c->iounit, c->offset, c->name->s);
489 	return n;
490 }
491 
492 static int
493 progfds(Osenv *o, char *va, int count, long offset)
494 {
495 	Fgrp *f;
496 	Chan *c;
497 	int n, i, w, ww;
498 
499 	f = o->fgrp;	/* f is not locked because we've acquired */
500 	n = readstr(0, va, count, o->pgrp->dot->name->s);
501 	n += snprint(va+n, count-n, "\n");
502 	offset = progoffset(offset, va, &n);
503 	/* compute width of qid.path */
504 	w = 0;
505 	for(i = 0; i <= f->maxfd; i++) {
506 		c = f->fd[i];
507 		if(c == nil)
508 			continue;
509 		ww = progqidwidth(c);
510 		if(ww > w)
511 			w = ww;
512 	}
513 	for(i = 0; i <= f->maxfd; i++) {
514 		c = f->fd[i];
515 		if(c == nil)
516 			continue;
517 		n += progfdprint(c, i, w, va+n, count-n);
518 		offset = progoffset(offset, va, &n);
519 	}
520 	return n;
521 }
522 
523 Inst *
524 pc2dispc(Inst *pc, Module *mod)
525 {
526 	ulong l, u, m, v;
527 	ulong *tab = mod->pctab;
528 
529 	v = (ulong)pc - (ulong)mod->prog;
530 	l = 0;
531 	u = mod->nprog-1;
532 	while(l < u){
533 		m = (l+u+1)/2;
534 		if(tab[m] < v)
535 			l = m;
536 		else if(tab[m] > v)
537 			u = m-1;
538 		else
539 			l = u = m;
540 	}
541 	if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v)
542 		return &mod->prog[u];
543 	return 0;
544 }
545 
546 static int
547 progstack(REG *reg, int state, char *va, int count, long offset)
548 {
549 	int n;
550 	Frame *f;
551 	Inst *pc;
552 	uchar *fp;
553 	Modlink *m;
554 
555 	n = 0;
556 	m = reg->M;
557 	fp = reg->FP;
558 	pc = reg->PC;
559 
560 	/*
561 	 * all states other than debug and ready block,
562 	 * but interp has already advanced the PC
563 	 */
564 	if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog)
565 		pc--;
566 	if(m->compiled && m->m->pctab != nil)
567 		pc = pc2dispc(pc, m->m);
568 
569 	while(fp != nil) {
570 		f = (Frame*)fp;
571 		n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n",
572 				(ulong)f,		/* FP */
573 				(ulong)(pc - m->prog),	/* PC in dis instructions */
574 				(ulong)m->MP,		/* MP */
575 				(ulong)m->prog,	/* Code for module */
576 				m->compiled && m->m->pctab == nil,	/* True if native assembler: fool stack utility for now */
577 				m->m->path);	/* File system path */
578 
579 		if(offset > 0) {
580 			offset -= n;
581 			if(offset < 0) {
582 				memmove(va, va+n+offset, -offset);
583 				n = -offset;
584 			}
585 			else
586 				n = 0;
587 		}
588 
589 		pc = f->lr;
590 		fp = f->fp;
591 		if(f->mr != nil)
592 			m = f->mr;
593 		if(!m->compiled)
594 			pc--;
595 		else if(m->m->pctab != nil)
596 			pc = pc2dispc(pc, m->m)-1;
597 	}
598 	return n;
599 }
600 
601 static int
602 calldepth(REG *reg)
603 {
604 	int n;
605 	uchar *fp;
606 
607 	n = 0;
608 	for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp)
609 		n++;
610 	return n;
611 }
612 
613 static int
614 progheap(Heapqry *hq, char *va, int count, ulong offset)
615 {
616 	WORD *w;
617 	void *p;
618 	List *hd;
619 	Array *a;
620 	char *fmt, *str;
621 	Module *m;
622 	Modlink *ml;
623 	Channel *c;
624 	ulong addr;
625 	String *ss;
626 	union { REAL r; LONG l; WORD w[2]; } rock;
627 	int i, s, n, len, signed_off;
628 	Type *t;
629 
630 	n = 0;
631 	s = 0;
632 	signed_off = offset;
633 	addr = hq->addr;
634 	for(i = 0; i < hq->count; i++) {
635 		switch(hq->fmt) {
636 		case 'W':
637 			if(addr & 3)
638 				return -1;
639 			n += snprint(va+n, count-n, "%d\n", *(WORD*)addr);
640 			s = sizeof(WORD);
641 			break;
642 		case 'B':
643 			n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr);
644 			s = sizeof(BYTE);
645 			break;
646 		case 'V':
647 			if(addr & 3)
648 				return -1;
649 			w = (WORD*)addr;
650 			rock.w[0] = w[0];
651 			rock.w[1] = w[1];
652 			n += snprint(va+n, count-n, "%lld\n", rock.l);
653 			s = sizeof(LONG);
654 			break;
655 		case 'R':
656 			if(addr & 3)
657 				return -1;
658 			w = (WORD*)addr;
659 			rock.w[0] = w[0];
660 			rock.w[1] = w[1];
661 			n += snprint(va+n, count-n, "%g\n", rock.r);
662 			s = sizeof(REAL);
663 			break;
664 		case 'I':
665 			if(addr & 3)
666 				return -1;
667 			for(m = modules; m != nil; m = m->link)
668 				if(m == (Module*)hq->module)
669 					break;
670 			if(m == nil)
671 				error(Ebadctl);
672 			addr = (ulong)(m->prog+addr);
673 			n += snprint(va+n, count-n, "%D\n", (Inst*)addr);
674 			s = sizeof(Inst);
675 			break;
676 		case 'P':
677 			if(addr & 3)
678 				return -1;
679 			p = *(void**)addr;
680 			fmt = "nil\n";
681 			if(p != H)
682 				fmt = "%lux\n";
683 			n += snprint(va+n, count-n, fmt, p);
684 			s = sizeof(WORD);
685 			break;
686 		case 'L':
687 			if(addr & 3)
688 				return -1;
689 			hd = *(List**)addr;
690 			if(hd == H || D2H(hd)->t != &Tlist)
691 				return -1;
692 			n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data);
693 			s = sizeof(WORD);
694 			break;
695 		case 'A':
696 			if(addr & 3)
697 				return -1;
698 			a = *(Array**)addr;
699 			if(a == H)
700 				n += snprint(va+n, count-n, "nil\n");
701 			else {
702 				if(D2H(a)->t != &Tarray)
703 					return -1;
704 				n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data);
705 			}
706 			s = sizeof(WORD);
707 			break;
708 		case 'C':
709 			if(addr & 3)
710 				return -1;
711 			ss = *(String**)addr;
712 			if(ss == H)
713 				ss = &snil;
714 			else
715 			if(D2H(ss)->t != &Tstring)
716 				return -1;
717 			n += snprint(va+n, count-n, "%d.", abs(ss->len));
718 			str = string2c(ss);
719 			len = strlen(str);
720 			if(count-n < len)
721 				len = count-n;
722 			if(len > 0) {
723 				memmove(va+n, str, len);
724 				n += len;
725 			}
726 			break;
727 		case 'M':
728 			if(addr & 3)
729 				return -1;
730 			ml = *(Modlink**)addr;
731 			fmt = ml == H ? "nil\n" : "%lux\n";
732 			n += snprint(va+n, count-n, fmt, ml->MP);
733 			s = sizeof(WORD);
734 			break;
735 		case 'c':
736 			if(addr & 3)
737 				return -1;
738 			c = *(Channel**)addr;
739 			if(c == H)
740 				n += snprint(va+n, count-n, "nil\n");
741 			else{
742 				t = D2H(c)->t;
743 				if(t != &Tchannel && t != Trdchan && t != Twrchan)
744 					return -1;
745 				if(c->buf == H)
746 					n += snprint(va+n, count-n, "0.%lux\n", (ulong)c);
747 				else
748 					n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size);
749 			}
750 			break;
751 
752 		}
753 		addr += s;
754 		if(signed_off > 0) {
755 			signed_off -= n;
756 			if(signed_off < 0) {
757 				memmove(va, va+n+signed_off, -signed_off);
758 				n = -signed_off;
759 			}
760 			else
761 				n = 0;
762 		}
763 	}
764 	return n;
765 }
766 
767 WORD
768 modstatus(REG *r, char *ptr, int len)
769 {
770 	Inst *PC;
771 	Frame *f;
772 
773 	if(r->M->m->name[0] == '$') {
774 		f = (Frame*)r->FP;
775 		snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name);
776 		if(f->mr->compiled)
777 			return (WORD)f->lr;
778 		return f->lr - f->mr->prog;
779 	}
780 	memmove(ptr, r->M->m->name, len);
781 	if(r->M->compiled)
782 		return (WORD)r->PC;
783 	PC = r->PC;
784 	/* should really check for blocked states */
785 	if(PC > r->M->prog)
786 		PC--;
787 	return PC - r->M->prog;
788 }
789 
790 static void
791 int2flag(int flag, char *s)
792 {
793 	if(flag == 0){
794 		*s = '\0';
795 		return;
796 	}
797 	*s++ = '-';
798 	if(flag & MAFTER)
799 		*s++ = 'a';
800 	if(flag & MBEFORE)
801 		*s++ = 'b';
802 	if(flag & MCREATE)
803 		*s++ = 'c';
804 	if(flag & MCACHE)
805 		*s++ = 'C';
806 	*s = '\0';
807 }
808 
809 static char*
810 progtime(ulong msec, char *buf, char *ebuf)
811 {
812 	int tenths, sec;
813 
814 	tenths = msec/100;
815 	sec = tenths/10;
816 	seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10);
817 	return buf;
818 }
819 
820 static long
821 progread(Chan *c, void *va, long n, vlong offset)
822 {
823 	int i;
824 	Prog *p;
825 	Osenv *o;
826 	Mntwalk *mw;
827 	ulong grpid;
828 	char *a = va;
829 	Progctl *ctl;
830 	char mbuf[64], timebuf[12];
831 	char flag[10];
832 
833 	if(c->qid.type & QTDIR)
834 		return devdirread(c, a, n, 0, 0, proggen);
835 
836 	switch(QID(c->qid)){
837 	case Qdbgctl:
838 		ctl = c->aux;
839 		return qread(ctl->q, va, n);
840 	case Qstatus:
841 		acquire();
842 		p = progpid(PID(c->qid));
843 		if(p == nil || p->state == Pexiting || p->R.M == H) {
844 			release();
845 			snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s",
846 				PID(c->qid),
847 				0,
848 				eve,
849 				progtime(0, timebuf, timebuf+sizeof(timebuf)),
850 				progstate[Pexiting],
851 				0,
852 				"[$Sys]");
853 			return readstr(offset, va, n, up->genbuf);
854 		}
855 		modstatus(&p->R, mbuf, sizeof(mbuf));
856 		o = p->osenv;
857 		snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s",
858 			p->pid,
859 			p->group!=nil? p->group->id: 0,
860 			o->user,
861 			progtime(p->ticks, timebuf, timebuf+sizeof(timebuf)),
862 			progstate[p->state],
863 			progsize(p),
864 			mbuf);
865 		release();
866 		return readstr(offset, va, n, up->genbuf);
867 	case Qwait:
868 		return qread(c->aux, va, n);
869 	case Qns:
870 		acquire();
871 		if(waserror()){
872 			release();
873 			nexterror();
874 		}
875 		p = progpid(PID(c->qid));
876 		if(p == nil)
877 			error(Ethread);
878 		mw = c->aux;
879 		if(mw->cddone){
880 			poperror();
881 			release();
882 			return 0;
883 		}
884 		o = p->osenv;
885 		mntscan(mw, o->pgrp);
886 		if(mw->mh == 0) {
887 			mw->cddone = 1;
888 			i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s);
889 			poperror();
890 			release();
891 			return i;
892 		}
893 		int2flag(mw->cm->mflag, flag);
894 		if(strcmp(mw->cm->to->name->s, "#M") == 0){
895 			i = snprint(a, n, "mount %s %s %s %s\n", flag,
896 				mw->cm->to->mchan->name->s,
897 				mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
898 		}else
899 			i = snprint(a, n, "bind %s %s %s\n", flag,
900 				mw->cm->to->name->s, mw->mh->from->name->s);
901 		poperror();
902 		release();
903 		return i;
904 	case Qnsgrp:
905 		acquire();
906 		p = progpid(PID(c->qid));
907 		if(p == nil) {
908 			release();
909 			error(Ethread);
910 		}
911 		grpid = ((Osenv *)p->osenv)->pgrp->pgrpid;
912 		release();
913 		return readnum(offset, va, n, grpid, NUMSIZE);
914 	case Qpgrp:
915 		acquire();
916 		p = progpid(PID(c->qid));
917 		if(p == nil) {
918 			release();
919 			error(Ethread);
920 		}
921 		grpid = p->group!=nil? p->group->id: 0;
922 		release();
923 		return readnum(offset, va, n, grpid, NUMSIZE);
924 	case Qstack:
925 		acquire();
926 		p = progpid(PID(c->qid));
927 		if(p == nil || p->state == Pexiting) {
928 			release();
929 			error(Ethread);
930 		}
931 		if(p->state == Pready) {
932 			release();
933 			error(Estopped);
934 		}
935 		n = progstack(&p->R, p->state, va, n, offset);
936 		release();
937 		return n;
938 	case Qheap:
939 		acquire();
940 		if(waserror()){
941 			release();
942 			nexterror();
943 		}
944 		n = progheap(c->aux, va, n, offset);
945 		if(n == -1)
946 			error(Emisalign);
947 		poperror();
948 		release();
949 		return n;
950 	case Qfd:
951 		acquire();
952 		if(waserror()) {
953 			release();
954 			nexterror();
955 		}
956 		p = progpid(PID(c->qid));
957 		if(p == nil)
958 			error(Ethread);
959 		o = p->osenv;
960 		n = progfds(o, va, n, offset);
961 		poperror();
962 		release();
963 		return n;
964 	case Qexception:
965 		acquire();
966 		p = progpid(PID(c->qid));
967 		if(p == nil) {
968 			release();
969 			error(Ethread);
970 		}
971 		if(p->exstr == nil)
972 			up->genbuf[0] = 0;
973 		else
974 			snprint(up->genbuf, sizeof(up->genbuf), p->exstr);
975 		release();
976 		return readstr(offset, va, n, up->genbuf);
977 	}
978 	error(Egreg);
979 	return 0;
980 }
981 
982 static void
983 mntscan(Mntwalk *mw, Pgrp *pg)
984 {
985 	Mount *t;
986 	Mhead *f;
987 	int nxt, i;
988 	ulong last, bestmid;
989 
990 	rlock(&pg->ns);
991 
992 	nxt = 0;
993 	bestmid = ~0;
994 
995 	last = 0;
996 	if(mw->mh)
997 		last = mw->cm->mountid;
998 
999 	for(i = 0; i < MNTHASH; i++) {
1000 		for(f = pg->mnthash[i]; f; f = f->hash) {
1001 			for(t = f->mount; t; t = t->next) {
1002 				if(mw->mh == 0 ||
1003 				  (t->mountid > last && t->mountid < bestmid)) {
1004 					mw->cm = t;
1005 					mw->mh = f;
1006 					bestmid = mw->cm->mountid;
1007 					nxt = 1;
1008 				}
1009 			}
1010 		}
1011 	}
1012 	if(nxt == 0)
1013 		mw->mh = 0;
1014 
1015 	runlock(&pg->ns);
1016 }
1017 
1018 static long
1019 progwrite(Chan *c, void *va, long n, vlong offset)
1020 {
1021 	Prog *p, *f;
1022 	Heapqry *hq;
1023 	char buf[512];
1024 	Progctl *ctl;
1025 	char *b;
1026 	int i, pc;
1027 	Cmdbuf *cb;
1028 	Cmdtab *ct;
1029 
1030 	USED(offset);
1031 	USED(va);
1032 
1033 	if(c->qid.type & QTDIR)
1034 		error(Eisdir);
1035 
1036 	acquire();
1037 	if(waserror()) {
1038 		release();
1039 		nexterror();
1040 	}
1041 	p = progpid(PID(c->qid));
1042 	if(p == nil)
1043 		error(Ethread);
1044 
1045 	switch(QID(c->qid)){
1046 	case Qctl:
1047 		cb = parsecmd(va, n);
1048 		if(waserror()){
1049 			free(cb);
1050 			nexterror();
1051 		}
1052 		ct = lookupcmd(cb, progcmd, nelem(progcmd));
1053 		switch(ct->index){
1054 		case CMkillgrp:
1055 			killgrp(p, "killed");
1056 			break;
1057 		case CMkill:
1058 			killprog(p, "killed");
1059 			break;
1060 		case CMrestricted:
1061 			p->flags |= Prestrict;
1062 			break;
1063 		case CMexceptions:
1064 			if(p->group->id != p->pid)
1065 				error(Eperm);
1066 			if(strcmp(cb->f[1], "propagate") == 0)
1067 				p->flags |= Ppropagate;
1068 			else if(strcmp(cb->f[1], "notifyleader") == 0)
1069 				p->flags |= Pnotifyleader;
1070 			else
1071 				error(Ebadctl);
1072 			break;
1073 		case CMprivate:
1074 			p->group->flags |= Pprivatemem;
1075 			break;
1076 		}
1077 		poperror();
1078 		free(cb);
1079 		break;
1080 	case Qdbgctl:
1081 		cb = parsecmd(va, n);
1082 		if(waserror()){
1083 			free(cb);
1084 			nexterror();
1085 		}
1086 		if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0)
1087 			ct = progdbgcmd;
1088 		else
1089 			ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd));
1090 		switch(ct->index){
1091 		case CDstep:
1092 			if(cb->nf == 1)
1093 				i = strtoul(cb->f[0]+4, nil, 0);
1094 			else
1095 				i = strtoul(cb->f[1], nil, 0);
1096 			dbgstep(c->aux, p, i);
1097 			break;
1098 		case CDtoret:
1099 			f = currun();
1100 			i = calldepth(&p->R);
1101 			while(f->kill == nil) {
1102 				dbgstep(c->aux, p, 1024);
1103 				if(i > calldepth(&p->R))
1104 					break;
1105 			}
1106 			break;
1107 		case CDcont:
1108 			f = currun();
1109 			while(f->kill == nil)
1110 				dbgstep(c->aux, p, 1024);
1111 			break;
1112 		case CDstart:
1113 			dbgstart(p);
1114 			break;
1115 		case CDstop:
1116 			ctl = c->aux;
1117 			ctl->stop = 1;
1118 			break;
1119 		case CDunstop:
1120 			ctl = c->aux;
1121 			ctl->stop = 0;
1122 			break;
1123 		case CDbpt:
1124 			pc = strtoul(cb->f[3], nil, 10);
1125 			ctl = c->aux;
1126 			if(strcmp(cb->f[1], "set") == 0)
1127 				ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc);
1128 			else if(strcmp(cb->f[1], "del") == 0)
1129 				ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc);
1130 			else
1131 				error(Ebadctl);
1132 			break;
1133 		case CDmaim:
1134 			p->kill = "maim";
1135 			break;
1136 		}
1137 		poperror();
1138 		free(cb);
1139 		break;
1140 	case Qheap:
1141 		/*
1142 		 * Heap query:
1143 		 *	addr.Fn
1144 		 *	pc+module.In
1145 		 */
1146 		i = n;
1147 		if(i > sizeof(buf)-1)
1148 			i = sizeof(buf)-1;
1149 		memmove(buf, va, i);
1150 		buf[i] = '\0';
1151 		hq = c->aux;
1152 		hq->addr = strtoul(buf, &b, 0);
1153 		if(*b == '+')
1154 			hq->module = strtoul(b, &b, 0);
1155 		if(*b++ != '.')
1156 			error(Ebadctl);
1157 		hq->fmt = *b++;
1158 		hq->count = strtoul(b, nil, 0);
1159 		break;
1160 	default:
1161 		print("unknown qid in procwrite\n");
1162 		error(Egreg);
1163 	}
1164 	poperror();
1165 	release();
1166 	return n;
1167 }
1168 
1169 static Bpt*
1170 setbpt(Bpt *bpts, char *path, int pc)
1171 {
1172 	int n;
1173 	Bpt *b;
1174 
1175 	n = strlen(path);
1176 	b = mallocz(sizeof *b + n, 0);
1177 	if(b == nil)
1178 		return bpts;
1179 	b->pc = pc;
1180 	memmove(b->path, path, n+1);
1181 	b->file = b->path;
1182 	path = strrchr(b->path, '/');
1183 	if(path != nil)
1184 		b->file = path + 1;
1185 	b->next = bpts;
1186 	return b;
1187 }
1188 
1189 static Bpt*
1190 delbpt(Bpt *bpts, char *path, int pc)
1191 {
1192 	Bpt *b, **last;
1193 
1194 	last = &bpts;
1195 	for(b = bpts; b != nil; b = b->next){
1196 		if(b->pc == pc && strcmp(b->path, path) == 0) {
1197 			*last = b->next;
1198 			free(b);
1199 			break;
1200 		}
1201 		last = &b->next;
1202 	}
1203 	return bpts;
1204 }
1205 
1206 static void
1207 freebpts(Bpt *b)
1208 {
1209 	Bpt *next;
1210 
1211 	for(; b != nil; b = next){
1212 		next = b->next;
1213 		free(b);
1214 	}
1215 }
1216 
1217 static void
1218 telldbg(Progctl *ctl, char *msg)
1219 {
1220 	kstrcpy(ctl->msg, msg, ERRMAX);
1221 	ctl->debugger = nil;
1222 	Wakeup(&ctl->r);
1223 }
1224 
1225 static void
1226 dbgstart(Prog *p)
1227 {
1228 	Osenv *o;
1229 	Progctl *ctl;
1230 
1231 	o = p->osenv;
1232 	ctl = o->debug;
1233 	if(ctl != nil && ctl->debugger != nil)
1234 		error("prog debugged");
1235 	if(p->state == Pdebug)
1236 		addrun(p);
1237 	o->debug = nil;
1238 	p->xec = xec;
1239 }
1240 
1241 static int
1242 xecdone(void *vc)
1243 {
1244 	Progctl *ctl = vc;
1245 
1246 	return ctl->debugger == nil;
1247 }
1248 
1249 static void
1250 dbgstep(Progctl *vctl, Prog *p, int n)
1251 {
1252 	Osenv * volatile o;
1253 	Progctl * volatile ctl;
1254 	char buf[ERRMAX+20], *msg;
1255 
1256 	if(p == currun())
1257 		error("cannot step yourself");
1258 
1259 	if(p->state == Pbroken)
1260 		error("prog broken");
1261 
1262 	ctl = vctl;
1263 	if(ctl->debugger != nil)
1264 		error("prog already debugged");
1265 
1266 	o = p->osenv;
1267 	if(o->debug == nil) {
1268 		o->debug = ctl;
1269 		p->xec = dbgxec;
1270 	}else if(o->debug != ctl)
1271 		error("prog already debugged");
1272 
1273 	ctl->step = n;
1274 	if(p->state == Pdebug)
1275 		addrun(p);
1276 	ctl->debugger = up;
1277 	strcpy(buf, "child: ");
1278 	msg = buf+7;
1279 	ctl->msg = msg;
1280 
1281 	/*
1282 	 * wait for reply from dbgxec; release is okay because prog is now
1283 	 * debugged, cannot exit.
1284 	 */
1285 	if(waserror()){
1286 		acquire();
1287 		ctl->debugger = nil;
1288 		ctl->msg = nil;
1289 		o->debug = nil;
1290 		p->xec = xec;
1291 		nexterror();
1292 	}
1293 	release();
1294 	Sleep(&ctl->r, xecdone, ctl);
1295 	poperror();
1296 	acquire();
1297 	if(msg[0] != '\0')
1298 		error(buf);
1299 }
1300 
1301 void
1302 dbgexit(Prog *kid, int broken, char *estr)
1303 {
1304 	int n;
1305 	Osenv *e;
1306 	Progctl *ctl;
1307 	char buf[ERRMAX+20];
1308 
1309 	e = kid->osenv;
1310 	ctl = e->debug;
1311 	e->debug = nil;
1312 	kid->xec = xec;
1313 
1314 	if(broken)
1315 		n = snprint(buf, sizeof(buf), "broken: %s", estr);
1316 	else
1317 		n = snprint(buf, sizeof(buf), "exited");
1318 	if(ctl->debugger)
1319 		telldbg(ctl, buf);
1320 	qproduce(ctl->q, buf, n);
1321 }
1322 
1323 static void
1324 dbgaddrun(Prog *p)
1325 {
1326 	Osenv *o;
1327 
1328 	p->state = Pdebug;
1329 	p->addrun = nil;
1330 	o = p->osenv;
1331 	if(o->rend != nil)
1332 		Wakeup(o->rend);
1333 	o->rend = nil;
1334 }
1335 
1336 static int
1337 bdone(void *vp)
1338 {
1339 	Prog *p = vp;
1340 
1341 	return p->addrun == nil;
1342 }
1343 
1344 static void
1345 dbgblock(Prog *p)
1346 {
1347 	Osenv *o;
1348 	Progctl *ctl;
1349 
1350 	o = p->osenv;
1351 	ctl = o->debug;
1352 	qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state]));
1353 	pushrun(p);
1354 	p->addrun = dbgaddrun;
1355 	o->rend = &up->sleep;
1356 
1357 	/*
1358 	 * bdone(p) is safe after release because p is being debugged,
1359 	 * so cannot exit.
1360 	 */
1361 	if(waserror()){
1362 		acquire();
1363 		nexterror();
1364 	}
1365 	release();
1366 	if(o->rend != nil)
1367 		Sleep(o->rend, bdone, p);
1368 	poperror();
1369 	acquire();
1370 	if(p->kill != nil)
1371 		error(p->kill);
1372 	ctl = o->debug;
1373 	if(ctl != nil)
1374 		qproduce(ctl->q, "run", 3);
1375 }
1376 
1377 void
1378 dbgxec(Prog *p)
1379 {
1380 	Bpt *b;
1381 	Prog *kid;
1382 	Osenv *env;
1383 	Progctl *ctl;
1384 	int op, pc, n;
1385 	char buf[ERRMAX+10];
1386 	extern void (*dec[])(void);
1387 
1388 	env = p->osenv;
1389 	ctl = env->debug;
1390 	ctl->ref++;
1391 	if(waserror()){
1392 		closedbgctl(ctl, p);
1393 		nexterror();
1394 	}
1395 
1396 	R = p->R;
1397 	R.MP = R.M->MP;
1398 
1399 	R.IC = p->quanta;
1400 	if((ulong)R.IC > ctl->step)
1401 		R.IC = ctl->step;
1402 	if(ctl->stop)
1403 		R.IC = 0;
1404 
1405 
1406 	buf[0] = '\0';
1407 
1408 	if(R.IC != 0 && R.M->compiled) {
1409 		comvec();
1410 		if(p != currun())
1411 			dbgblock(p);
1412 		goto save;
1413 	}
1414 
1415 	while(R.IC != 0) {
1416 		if(0)
1417 			print("step: %lux: %s %4ld %D\n",
1418 				(ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC);
1419 
1420 		dec[R.PC->add]();
1421 		op = R.PC->op;
1422 		R.PC++;
1423 		optab[op]();
1424 
1425 		/*
1426 		 * check notification about new progs
1427 		 */
1428 		if(op == ISPAWN || op == IMSPAWN) {
1429 			/* pick up the kid from the end of the run queue */
1430 			kid = delruntail(Pdebug);
1431 			n = snprint(buf, sizeof buf, "new %d", kid->pid);
1432 			qproduce(ctl->q, buf, n);
1433 			buf[0] = '\0';
1434 		}
1435 		if(op == ILOAD) {
1436 			n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s));
1437 			qproduce(ctl->q, buf, n);
1438 			buf[0] = '\0';
1439 		}
1440 
1441 		/*
1442 		 * check for returns at big steps
1443 		 */
1444 		if(op == IRET)
1445 			R.IC = 1;
1446 
1447 		/*
1448 		 * check for blocked progs
1449 		 */
1450 		if(R.IC == 1 && p != currun())
1451 			dbgblock(p);
1452 		if(ctl->stop)
1453 			R.IC = 1;
1454 		R.IC--;
1455 
1456 		if(ctl->bpts == nil)
1457 			continue;
1458 
1459 		pc = R.PC - R.M->prog;
1460 		for(b = ctl->bpts; b != nil; b = b->next) {
1461 			if(pc == b->pc &&
1462 			  (strcmp(R.M->m->path, b->path) == 0 ||
1463 			   strcmp(R.M->m->path, b->file) == 0)) {
1464 				strcpy(buf, "breakpoint");
1465 				goto save;
1466 			}
1467 		}
1468 	}
1469 save:
1470 	if(ctl->stop)
1471 		strcpy(buf, "stopped");
1472 
1473 	p->R = R;
1474 
1475 	if(env->debug == nil) {
1476 		poperror();
1477 		return;
1478 	}
1479 
1480 	if(p == currun())
1481 		delrun(Pdebug);
1482 
1483 	telldbg(env->debug, buf);
1484 	poperror();
1485 	closedbgctl(env->debug, p);
1486 }
1487 
1488 Dev progdevtab = {
1489 	'p',
1490 	"prog",
1491 
1492 	devinit,
1493 	progattach,
1494 	progwalk,
1495 	progstat,
1496 	progopen,
1497 	devcreate,
1498 	progclose,
1499 	progread,
1500 	devbread,
1501 	progwrite,
1502 	devbwrite,
1503 	devremove,
1504 	progwstat
1505 };
1506