xref: /inferno-os/os/port/devprog.c (revision 48f27553574bf59de5f101ae072f82f5f1993d6f)
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 || p->group->flags&Pprivatemem || 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 || p->group->flags&Pprivatemem || 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 
1032 	USED(offset);
1033 	USED(va);
1034 
1035 	if(c->qid.type & QTDIR)
1036 		error(Eisdir);
1037 
1038 	acquire();
1039 	if(waserror()) {
1040 		release();
1041 		nexterror();
1042 	}
1043 	p = progpid(PID(c->qid));
1044 	if(p == nil)
1045 		error(Ethread);
1046 
1047 	switch(QID(c->qid)){
1048 	case Qctl:
1049 		cb = parsecmd(va, n);
1050 		if(waserror()){
1051 			free(cb);
1052 			nexterror();
1053 		}
1054 		ct = lookupcmd(cb, progcmd, nelem(progcmd));
1055 		switch(ct->index){
1056 		case CMkillgrp:
1057 			killgrp(p, "killed");
1058 			break;
1059 		case CMkill:
1060 			killprog(p, "killed");
1061 			break;
1062 		case CMrestricted:
1063 			p->flags |= Prestrict;
1064 			break;
1065 		case CMexceptions:
1066 			if(p->group->id != p->pid)
1067 				error(Eperm);
1068 			if(strcmp(cb->f[1], "propagate") == 0)
1069 				p->flags |= Ppropagate;
1070 			else if(strcmp(cb->f[1], "notifyleader") == 0)
1071 				p->flags |= Pnotifyleader;
1072 			else
1073 				error(Ebadctl);
1074 			break;
1075 		case CMprivate:
1076 			p->group->flags |= Pprivatemem;
1077 			break;
1078 		}
1079 		poperror();
1080 		free(cb);
1081 		break;
1082 	case Qdbgctl:
1083 		cb = parsecmd(va, n);
1084 		if(waserror()){
1085 			free(cb);
1086 			nexterror();
1087 		}
1088 		if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0)
1089 			ct = progdbgcmd;
1090 		else
1091 			ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd));
1092 		switch(ct->index){
1093 		case CDstep:
1094 			if(cb->nf == 1)
1095 				i = strtoul(cb->f[0]+4, nil, 0);
1096 			else
1097 				i = strtoul(cb->f[1], nil, 0);
1098 			dbgstep(c->aux, p, i);
1099 			break;
1100 		case CDtoret:
1101 			f = currun();
1102 			i = calldepth(&p->R);
1103 			while(f->kill == nil) {
1104 				dbgstep(c->aux, p, 1024);
1105 				if(i > calldepth(&p->R))
1106 					break;
1107 			}
1108 			break;
1109 		case CDcont:
1110 			f = currun();
1111 			while(f->kill == nil)
1112 				dbgstep(c->aux, p, 1024);
1113 			break;
1114 		case CDstart:
1115 			dbgstart(p);
1116 			break;
1117 		case CDstop:
1118 			ctl = c->aux;
1119 			ctl->stop = 1;
1120 			break;
1121 		case CDunstop:
1122 			ctl = c->aux;
1123 			ctl->stop = 0;
1124 			break;
1125 		case CDbpt:
1126 			pc = strtoul(cb->f[3], nil, 10);
1127 			ctl = c->aux;
1128 			if(strcmp(cb->f[1], "set") == 0)
1129 				ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc);
1130 			else if(strcmp(cb->f[1], "del") == 0)
1131 				ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc);
1132 			else
1133 				error(Ebadctl);
1134 			break;
1135 		case CDmaim:
1136 			p->kill = "maim";
1137 			break;
1138 		}
1139 		poperror();
1140 		free(cb);
1141 		break;
1142 	case Qheap:
1143 		/*
1144 		 * Heap query:
1145 		 *	addr.Fn
1146 		 *	pc+module.In
1147 		 */
1148 		i = n;
1149 		if(i > sizeof(buf)-1)
1150 			i = sizeof(buf)-1;
1151 		memmove(buf, va, i);
1152 		buf[i] = '\0';
1153 		hq = c->aux;
1154 		hq->addr = strtoul(buf, &b, 0);
1155 		if(*b == '+')
1156 			hq->module = strtoul(b, &b, 0);
1157 		if(*b++ != '.')
1158 			error(Ebadctl);
1159 		hq->fmt = *b++;
1160 		hq->count = strtoul(b, nil, 0);
1161 		break;
1162 	default:
1163 		print("unknown qid in procwrite\n");
1164 		error(Egreg);
1165 	}
1166 	poperror();
1167 	release();
1168 	return n;
1169 }
1170 
1171 static Bpt*
1172 setbpt(Bpt *bpts, char *path, int pc)
1173 {
1174 	int n;
1175 	Bpt *b;
1176 
1177 	n = strlen(path);
1178 	b = mallocz(sizeof *b + n, 0);
1179 	if(b == nil)
1180 		return bpts;
1181 	b->pc = pc;
1182 	memmove(b->path, path, n+1);
1183 	b->file = b->path;
1184 	path = strrchr(b->path, '/');
1185 	if(path != nil)
1186 		b->file = path + 1;
1187 	b->next = bpts;
1188 	return b;
1189 }
1190 
1191 static Bpt*
1192 delbpt(Bpt *bpts, char *path, int pc)
1193 {
1194 	Bpt *b, **last;
1195 
1196 	last = &bpts;
1197 	for(b = bpts; b != nil; b = b->next){
1198 		if(b->pc == pc && strcmp(b->path, path) == 0) {
1199 			*last = b->next;
1200 			free(b);
1201 			break;
1202 		}
1203 		last = &b->next;
1204 	}
1205 	return bpts;
1206 }
1207 
1208 static void
1209 freebpts(Bpt *b)
1210 {
1211 	Bpt *next;
1212 
1213 	for(; b != nil; b = next){
1214 		next = b->next;
1215 		free(b);
1216 	}
1217 }
1218 
1219 static void
1220 telldbg(Progctl *ctl, char *msg)
1221 {
1222 	kstrcpy(ctl->msg, msg, ERRMAX);
1223 	ctl->debugger = nil;
1224 	wakeup(&ctl->r);
1225 }
1226 
1227 static void
1228 dbgstart(Prog *p)
1229 {
1230 	Osenv *o;
1231 	Progctl *ctl;
1232 
1233 	o = p->osenv;
1234 	ctl = o->debug;
1235 	if(ctl != nil && ctl->debugger != nil)
1236 		error("prog debugged");
1237 	if(p->state == Pdebug)
1238 		addrun(p);
1239 	o->debug = nil;
1240 	p->xec = xec;
1241 }
1242 
1243 static int
1244 xecdone(void *vc)
1245 {
1246 	Progctl *ctl = vc;
1247 
1248 	return ctl->debugger == nil;
1249 }
1250 
1251 static void
1252 dbgstep(Progctl *vctl, Prog *p, int n)
1253 {
1254 	Osenv *o;
1255 	Progctl *ctl;
1256 	char buf[ERRMAX+20], *msg;
1257 
1258 	if(p == currun())
1259 		error("cannot step yourself");
1260 
1261 	if(p->state == Pbroken)
1262 		error("prog broken");
1263 
1264 	ctl = vctl;
1265 	if(ctl->debugger != nil)
1266 		error("prog already debugged");
1267 
1268 	o = p->osenv;
1269 	if(o->debug == nil) {
1270 		o->debug = ctl;
1271 		p->xec = dbgxec;
1272 	}else if(o->debug != ctl)
1273 		error("prog already debugged");
1274 
1275 	ctl->step = n;
1276 	if(p->state == Pdebug)
1277 		addrun(p);
1278 	ctl->debugger = up;
1279 	strcpy(buf, "child: ");
1280 	msg = buf+7;
1281 	ctl->msg = msg;
1282 
1283 	/*
1284 	 * wait for reply from dbgxec; release is okay because prog is now
1285 	 * debugged, cannot exit.
1286 	 */
1287 	if(waserror()){
1288 		acquire();
1289 		ctl->debugger = nil;
1290 		ctl->msg = nil;
1291 		o->debug = nil;
1292 		p->xec = xec;
1293 		nexterror();
1294 	}
1295 	release();
1296 	sleep(&ctl->r, xecdone, ctl);
1297 	poperror();
1298 	acquire();
1299 	if(msg[0] != '\0')
1300 		error(buf);
1301 }
1302 
1303 void
1304 dbgexit(Prog *kid, int broken, char *estr)
1305 {
1306 	int n;
1307 	Osenv *e;
1308 	Progctl *ctl;
1309 	char buf[ERRMAX+20];
1310 
1311 	e = kid->osenv;
1312 	ctl = e->debug;
1313 	e->debug = nil;
1314 	kid->xec = xec;
1315 
1316 	if(broken)
1317 		n = snprint(buf, sizeof(buf), "broken: %s", estr);
1318 	else
1319 		n = snprint(buf, sizeof(buf), "exited");
1320 	if(ctl->debugger)
1321 		telldbg(ctl, buf);
1322 	qproduce(ctl->q, buf, n);
1323 }
1324 
1325 static void
1326 dbgaddrun(Prog *p)
1327 {
1328 	Osenv *o;
1329 
1330 	p->state = Pdebug;
1331 	p->addrun = nil;
1332 	o = p->osenv;
1333 	if(o->rend != nil)
1334 		wakeup(o->rend);
1335 	o->rend = nil;
1336 }
1337 
1338 static int
1339 bdone(void *vp)
1340 {
1341 	Prog *p = vp;
1342 
1343 	return p->addrun == nil;
1344 }
1345 
1346 static void
1347 dbgblock(Prog *p)
1348 {
1349 	Osenv *o;
1350 	Progctl *ctl;
1351 
1352 	o = p->osenv;
1353 	ctl = o->debug;
1354 	qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state]));
1355 	pushrun(p);
1356 	p->addrun = dbgaddrun;
1357 	o->rend = &up->sleep;
1358 
1359 	/*
1360 	 * bdone(p) is safe after release because p is being debugged,
1361 	 * so cannot exit.
1362 	 */
1363 	if(waserror()){
1364 		acquire();
1365 		nexterror();
1366 	}
1367 	release();
1368 	if(o->rend != nil)
1369 		sleep(o->rend, bdone, p);
1370 	poperror();
1371 	acquire();
1372 	if(p->kill != nil)
1373 		error(p->kill);
1374 	ctl = o->debug;
1375 	if(ctl != nil)
1376 		qproduce(ctl->q, "run", 3);
1377 }
1378 
1379 void
1380 dbgxec(Prog *p)
1381 {
1382 	Bpt *b;
1383 	Prog *kid;
1384 	Osenv *env;
1385 	Progctl *ctl;
1386 	int op, pc, n;
1387 	char buf[ERRMAX+10];
1388 	extern void (*dec[])(void);
1389 
1390 	env = p->osenv;
1391 	ctl = env->debug;
1392 	ctl->ref++;
1393 	if(waserror()){
1394 		closedbgctl(ctl, p);
1395 		nexterror();
1396 	}
1397 
1398 	R = p->R;
1399 	R.MP = R.M->MP;
1400 
1401 	R.IC = p->quanta;
1402 	if((ulong)R.IC > ctl->step)
1403 		R.IC = ctl->step;
1404 	if(ctl->stop)
1405 		R.IC = 0;
1406 
1407 
1408 	buf[0] = '\0';
1409 
1410 	if(R.IC != 0 && R.M->compiled) {
1411 		comvec();
1412 		if(p != currun())
1413 			dbgblock(p);
1414 		goto save;
1415 	}
1416 
1417 	while(R.IC != 0) {
1418 		if(0)
1419 			print("step: %lux: %s %4ld %D\n",
1420 				(ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC);
1421 
1422 		dec[R.PC->add]();
1423 		op = R.PC->op;
1424 		R.PC++;
1425 		optab[op]();
1426 
1427 		/*
1428 		 * check notification about new progs
1429 		 */
1430 		if(op == ISPAWN || op == IMSPAWN) {
1431 			/* pick up the kid from the end of the run queue */
1432 			kid = delruntail(Pdebug);
1433 			n = snprint(buf, sizeof buf, "new %d", kid->pid);
1434 			qproduce(ctl->q, buf, n);
1435 			buf[0] = '\0';
1436 		}
1437 		if(op == ILOAD) {
1438 			n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s));
1439 			qproduce(ctl->q, buf, n);
1440 			buf[0] = '\0';
1441 		}
1442 
1443 		/*
1444 		 * check for returns at big steps
1445 		 */
1446 		if(op == IRET)
1447 			R.IC = 1;
1448 
1449 		/*
1450 		 * check for blocked progs
1451 		 */
1452 		if(R.IC == 1 && p != currun())
1453 			dbgblock(p);
1454 		if(ctl->stop)
1455 			R.IC = 1;
1456 		R.IC--;
1457 		if(ctl->bpts == nil)
1458 			continue;
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 
1470 save:
1471 	if(ctl->stop)
1472 		strcpy(buf, "stopped");
1473 
1474 	p->R = R;
1475 
1476 	if(env->debug == nil) {
1477 		poperror();
1478 		return;
1479 	}
1480 
1481 	if(p == currun())
1482 		delrun(Pdebug);
1483 
1484 	telldbg(env->debug, buf);
1485 	poperror();
1486 	closedbgctl(env->debug, p);
1487 }
1488 
1489 Dev progdevtab = {
1490 	'p',
1491 	"prog",
1492 
1493 	devreset,
1494 	devinit,
1495 	devshutdown,
1496 	progattach,
1497 	progwalk,
1498 	progstat,
1499 	progopen,
1500 	devcreate,
1501 	progclose,
1502 	progread,
1503 	devbread,
1504 	progwrite,
1505 	devbwrite,
1506 	devremove,
1507 	progwstat,
1508 };
1509