xref: /inferno-os/emu/port/devprog.c (revision c094a1409b780cc543c077e8469fdb28b4c90afb)
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 || o->pgrp->privatemem || 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 || o->pgrp->privatemem || 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 	Osenv *o;
1030 
1031 	USED(offset);
1032 	USED(va);
1033 
1034 	if(c->qid.type & QTDIR)
1035 		error(Eisdir);
1036 
1037 	acquire();
1038 	if(waserror()) {
1039 		release();
1040 		nexterror();
1041 	}
1042 	p = progpid(PID(c->qid));
1043 	if(p == nil)
1044 		error(Ethread);
1045 
1046 	switch(QID(c->qid)){
1047 	case Qctl:
1048 		cb = parsecmd(va, n);
1049 		if(waserror()){
1050 			free(cb);
1051 			nexterror();
1052 		}
1053 		ct = lookupcmd(cb, progcmd, nelem(progcmd));
1054 		switch(ct->index){
1055 		case CMkillgrp:
1056 			killgrp(p, "killed");
1057 			break;
1058 		case CMkill:
1059 			killprog(p, "killed");
1060 			break;
1061 		case CMrestricted:
1062 			p->flags |= Prestrict;
1063 			break;
1064 		case CMexceptions:
1065 			if(p->group->id != p->pid)
1066 				error(Eperm);
1067 			if(strcmp(cb->f[1], "propagate") == 0)
1068 				p->flags |= Ppropagate;
1069 			else if(strcmp(cb->f[1], "notifyleader") == 0)
1070 				p->flags |= Pnotifyleader;
1071 			else
1072 				error(Ebadctl);
1073 			break;
1074 		case CMprivate:
1075 			o = p->osenv;
1076 			o->pgrp->privatemem = 1;
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 * volatile o;
1255 	Progctl * volatile 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 
1458 		if(ctl->bpts == nil)
1459 			continue;
1460 
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 save:
1472 	if(ctl->stop)
1473 		strcpy(buf, "stopped");
1474 
1475 	p->R = R;
1476 
1477 	if(env->debug == nil) {
1478 		poperror();
1479 		return;
1480 	}
1481 
1482 	if(p == currun())
1483 		delrun(Pdebug);
1484 
1485 	telldbg(env->debug, buf);
1486 	poperror();
1487 	closedbgctl(env->debug, p);
1488 }
1489 
1490 Dev progdevtab = {
1491 	'p',
1492 	"prog",
1493 
1494 	devinit,
1495 	progattach,
1496 	progwalk,
1497 	progstat,
1498 	progopen,
1499 	devcreate,
1500 	progclose,
1501 	progread,
1502 	devbread,
1503 	progwrite,
1504 	devbwrite,
1505 	devremove,
1506 	progwstat
1507 };
1508