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