#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ureg.h" #include "devtab.h" enum { Qctl, Qdir, Qfpregs, Qkregs, Qmem, Qnote, Qnoteid, Qnotepg, Qns, Qproc, Qregs, Qsegment, Qstatus, Qcounters, Qtext, Qwait, }; #define STATSIZE (2*NAMELEN+12+7*12) Dirtab procdir[] = { "counters", {Qcounters}, 0, 0444, "ctl", {Qctl}, 0, 0000, "fpregs", {Qfpregs}, sizeof(FPsave), 0000, "kregs", {Qkregs}, sizeof(Ureg), 0000, "mem", {Qmem}, 0, 0000, "note", {Qnote}, 0, 0000, "noteid", {Qnoteid}, 0, 0666, "notepg", {Qnotepg}, 0, 0000, "ns", {Qns}, 0, 0400, "proc", {Qproc}, 0, 0400, "regs", {Qregs}, sizeof(Ureg), 0000, "segment", {Qsegment}, 0, 0444, "status", {Qstatus}, STATSIZE, 0444, "text", {Qtext}, 0, 0000, "wait", {Qwait}, 0, 0400, }; /* Segment type from portdat.h */ char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Shdata" }; /* * Qids are, in path: * 4 bits of file type (qids above) * 23 bits of process slot number + 1 * in vers, * 32 bits of pid, for consistency checking * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid. */ #define NPROC (sizeof procdir/sizeof(Dirtab)) #define QSHIFT 4 /* location in qid of proc slot # */ #define QID(q) (((q).path&0x0000000F)>>0) #define SLOT(q) ((((q).path&0x07FFFFFF0)>>QSHIFT)-1) #define PID(q) ((q).vers) #define NOTEID(q) ((q).vers) void procctlreq(Proc*, char*, int); int procctlmemio(Proc*, ulong, int, void*, int); Chan* proctext(Chan*, Proc*); Segment* txt2data(Proc*, Segment*); int procstopped(void*); void mntscan(Mntwalk*); ulong readcounters(Proc*, ulong, void*, int); int procgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp) { Qid qid; Proc *p; char buf[NAMELEN]; ulong pid, path, perm, len; USED(ntab); if(c->qid.path == CHDIR){ if(s >= conf.nproc) return -1; p = proctab(s); pid = p->pid; if(pid == 0) return 0; sprint(buf, "%d", pid); qid = (Qid){CHDIR|((s+1)<user, CHDIR|0555, dp); return 1; } if(s >= NPROC) return -1; if(tab) panic("procgen"); tab = &procdir[s]; path = c->qid.path&~(CHDIR|((1<qid)); perm = tab->perm; if(perm == 0) perm = p->procmode; len = tab->length; if(QID(c->qid) == Qwait) len = p->nwait * sizeof(Waitmsg); qid = (Qid){path|tab->qid.path, c->qid.vers}; devdir(c, qid, tab->name, len, p->user, perm, dp); return 1; } void procinit(void) { /* standard counters - this ordering is important */ proccounter("sched"); proccounter("fault"); proccounter("tlbmiss"); if(conf.nproc >= (1<<(16-QSHIFT))-1) print("warning: too many procs for devproc\n"); } void procreset(void) { } Chan* procattach(char *spec) { return devattach('p', spec); } Chan* procclone(Chan *c, Chan *nc) { return devclone(c, nc); } int procwalk(Chan *c, char *name) { if(strcmp(name, "..") == 0) { c->qid.path = CHDIR; return 1; } return devwalk(c, name, 0, 0, procgen); } void procstat(Chan *c, char *db) { devstat(c, db, 0, 0, procgen); } Chan * procopen(Chan *c, int omode) { Proc *p; Pgrp *pg; Chan *tc; if(c->qid.path & CHDIR) return devopen(c, omode, 0, 0, procgen); p = proctab(SLOT(c->qid)); pg = p->pgrp; if(p->pid != PID(c->qid)) error(Eprocdied); omode = openmode(omode); switch(QID(c->qid)){ case Qtext: if(omode != OREAD) error(Eperm); tc = proctext(c, p); tc->offset = 0; return tc; case Qproc: case Qkregs: case Qsegment: if(omode != OREAD) error(Eperm); break; case Qcounters: case Qctl: case Qnote: case Qnoteid: case Qmem: case Qstatus: case Qwait: case Qregs: case Qfpregs: break; case Qns: if(omode != OREAD) error(Eperm); c->aux = malloc(sizeof(Mntwalk)); break; case Qnotepg: if(omode!=OWRITE || pg->pgrpid == 1) error(Eperm); c->pgrpid.path = pg->pgrpid+1; c->pgrpid.vers = p->noteid; break; default: pprint("procopen %lux\n", c->qid); error(Egreg); } /* Affix pid to qid */ if(p->state != Dead) c->qid.vers = p->pid; return devopen(c, omode, 0, 0, procgen); } void proccreate(Chan *c, char *name, int omode, ulong perm) { USED(c, name, omode, perm); error(Eperm); } void procremove(Chan *c) { USED(c); error(Eperm); } void procwstat(Chan *c, char *db) { Proc *p; Dir d; if(c->qid.path&CHDIR) error(Eperm); p = proctab(SLOT(c->qid)); if(p->pid != PID(c->qid)) error(Eprocdied); if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0) error(Eperm); convM2D(db, &d); p->procmode = d.mode&0777; } void procclose(Chan * c) { if(QID(c->qid) == Qns && c->aux != 0) free(c->aux); } long procread(Chan *c, void *va, long n, ulong offset) { long l; Proc *p; Waitq *wq; Ureg kur; uchar *rptr; Mntwalk *mw; int i, j, rsize; Segment *sg, *s; char *a = va, *sps; char statbuf[NSEG*32]; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, procgen); p = proctab(SLOT(c->qid)); if(p->pid != PID(c->qid)) error(Eprocdied); switch(QID(c->qid)){ case Qmem: if(offset < KZERO || (offset >= USTKTOP-USTKSIZE && offset < USTKTOP)) return procctlmemio(p, offset, n, va, 1); /* Protect crypt key memory */ if(offset >= palloc.cmembase&&offset < palloc.cmemtop) error(Eperm); /* validate physical kernel addresses */ if(offset < (ulong)end) { if(offset+n > (ulong)end) n = (ulong)end - offset; memmove(a, (char*)offset, n); return n; } if(offset >= conf.base0 && offset < conf.npage0){ if(offset+n > conf.npage0) n = conf.npage0 - offset; memmove(a, (char*)offset, n); return n; } if(offset >= conf.base1 && offset < conf.npage1){ if(offset+n > conf.npage1) n = conf.npage1 - offset; memmove(a, (char*)offset, n); return n; } error(Ebadarg); case Qnote: qlock(&p->debug); if(waserror()){ qunlock(&p->debug); nexterror(); } if(p->pid != PID(c->qid)) error(Eprocdied); if(n < ERRLEN) error(Etoosmall); if(p->nnote == 0) n = 0; else { memmove(va, p->note[0].msg, ERRLEN); p->nnote--; memmove(p->note, p->note+1, p->nnote*sizeof(Note)); n = ERRLEN; } if(p->nnote == 0) p->notepending = 0; poperror(); qunlock(&p->debug); return n; case Qproc: return readnum(offset, va, n, (ulong)p, NUMSIZE); case Qregs: rptr = (uchar*)p->dbgreg; rsize = sizeof(Ureg); goto regread; case Qkregs: memset(&kur, 0, sizeof(Ureg)); setkernur(&kur, p); rptr = (uchar*)&kur; rsize = sizeof(Ureg); goto regread; case Qfpregs: rptr = (uchar*)&p->fpsave; rsize = sizeof(FPsave); regread: if(offset >= rsize) return 0; if(offset+n > rsize) n = rsize - offset; memmove(a, rptr+offset, n); return n; case Qcounters: return readcounters(p, offset, va, n); case Qstatus: if(offset >= STATSIZE) return 0; if(offset+n > STATSIZE) n = STATSIZE - offset; sps = p->psstate; if(sps == 0) sps = statename[p->state]; memset(statbuf, ' ', sizeof statbuf); memmove(statbuf+0*NAMELEN, p->text, strlen(p->text)); memmove(statbuf+1*NAMELEN, p->user, strlen(p->user)); memmove(statbuf+2*NAMELEN, sps, strlen(sps)); j = 2*NAMELEN + 12; for(i = 0; i < 6; i++) { l = p->time[i]; if(i == TReal) l = MACHP(0)->ticks - l; l = TK2MS(l); readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } /* ignore stack, which is mostly non-existent */ l = 0; for(i=1; iseg[i]; if(s) l += s->top - s->base; } readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l>>10, NUMSIZE); memmove(a, statbuf+offset, n); return n; case Qsegment: j = 0; for(i = 0; i < NSEG; i++) { sg = p->seg[i]; if(sg == 0) continue; j += sprint(&statbuf[j], "%-6s %c %.8lux %.8lux %4d\n", sname[sg->type&SG_TYPE], sg->type&SG_RONLY ? 'R' : ' ', sg->base, sg->top, sg->ref); } if(offset >= j) return 0; if(offset+n > j) n = j-offset; if(n == 0 && offset == 0) exhausted("segments"); memmove(a, &statbuf[offset], n); return n; case Qwait: if(n < sizeof(Waitmsg)) error(Etoosmall); if(!canqlock(&p->qwaitr)) error(Einuse); if(waserror()) { qunlock(&p->qwaitr); nexterror(); } lock(&p->exl); if(up == p && p->nchild == 0 && p->waitq == 0) { unlock(&p->exl); error(Enochild); } while(p->waitq == 0) { unlock(&p->exl); sleep(&p->waitr, haswaitq, p); lock(&p->exl); } wq = p->waitq; p->waitq = wq->next; p->nwait--; unlock(&p->exl); qunlock(&p->qwaitr); poperror(); memmove(a, &wq->w, sizeof(Waitmsg)); free(wq); return sizeof(Waitmsg); case Qns: mw = c->aux; mntscan(mw); if(mw->mh == 0) return 0; if(n < NAMELEN+11) error(Etoosmall); i = sprint(a, "%s %d ", mw->cm->spec, mw->cm->flag); n -= i; a += i; i = ptpath(mw->mh->from->path, a, n); n -= i; a += i; if(n > 0) { *a++ = ' '; n--; } a += ptpath(mw->cm->to->path, a, n); return a - (char*)va; case Qnoteid: return readnum(offset, va, n, p->noteid, NUMSIZE); } error(Egreg); return 0; /* not reached */ } void mntscan(Mntwalk *mw) { Pgrp *pg; Mount *t; int nxt; ulong last, bestmid; Mhead **h, **he, *f; pg = up->pgrp; rlock(&pg->ns); nxt = 0; last = 0; bestmid = ~0; if(mw->mh) last = mw->cm->mountid; he = &pg->mnthash[MNTHASH]; for(h = pg->mnthash; h < he; h++) { for(f = *h; f; f = f->hash) { for(t = f->mount; t; t = t->next) { if(mw->mh == 0 || (t->mountid > last && t->mountid < bestmid)) { mw->cm = t; mw->mh = f; bestmid = mw->cm->mountid; nxt = 1; } } } } if(nxt == 0) mw->mh = 0; runlock(&pg->ns); } long procwrite(Chan *c, void *va, long n, ulong offset) { int id; Proc *p, *t, *et; char *a, buf[ERRLEN]; a = va; if(c->qid.path & CHDIR) error(Eisdir); p = proctab(SLOT(c->qid)); /* Use the remembered noteid in the channel rather * than the process pgrpid */ if(QID(c->qid) == Qnotepg) { pgrpnote(NOTEID(c->pgrpid), va, n, NUser); return n; } qlock(&p->debug); if(waserror()){ qunlock(&p->debug); nexterror(); } if(p->pid != PID(c->qid)) error(Eprocdied); switch(QID(c->qid)){ case Qmem: if(p->state != Stopped) error(Ebadctl); n = procctlmemio(p, offset, n, va, 0); break; case Qregs: if(offset >= sizeof(Ureg)) return 0; if(offset+n > sizeof(Ureg)) n = sizeof(Ureg) - offset; setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n); break; case Qfpregs: if(offset >= sizeof(FPsave)) return 0; if(offset+n > sizeof(FPsave)) n = sizeof(FPsave) - offset; memmove((uchar*)&p->fpsave+offset, va, n); break; case Qctl: procctlreq(p, va, n); break; case Qnote: if(p->kp) error(Eperm); if(n >= ERRLEN-1) error(Etoobig); memmove(buf, va, n); buf[n] = 0; if(!postnote(p, 0, buf, NUser)) error("note not posted"); break; case Qnoteid: id = atoi(a); if(id == p->pid) { p->noteid = id; break; } t = proctab(0); for(et = t+conf.nproc; t < et; t++) { if(id == t->noteid) { if(strcmp(p->user, t->user) != 0) error(Eperm); p->noteid = id; break; } } if(p->noteid != id) error(Ebadarg); break; default: pprint("unknown qid in procwrite\n"); error(Egreg); } poperror(); qunlock(&p->debug); return n; } Chan * proctext(Chan *c, Proc *p) { Chan *tc; Image *i; Segment *s; s = p->seg[TSEG]; if(s == 0) error(Enonexist); if(p->state==Dead) error(Eprocdied); lock(s); i = s->image; if(i == 0) { unlock(s); error(Eprocdied); } unlock(s); lock(i); if(waserror()) { unlock(i); nexterror(); } tc = i->c; if(tc == 0) error(Eprocdied); if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) { close(tc); error(Eprocdied); } if(p->pid != PID(c->qid)) error(Eprocdied); unlock(i); poperror(); return tc; } void procstopwait(Proc *p, int ctl) { int pid; if(p->pdbg) error(Einuse); if(procstopped(p)) return; if(ctl != 0) p->procctl = ctl; p->pdbg = up; pid = p->pid; qunlock(&p->debug); up->psstate = "Stopwait"; if(waserror()) { p->pdbg = 0; qlock(&p->debug); nexterror(); } sleep(&up->sleep, procstopped, p); poperror(); qlock(&p->debug); if(p->pid != pid) error(Eprocdied); } void procctlreq(Proc *p, char *va, int n) { char buf[NAMELEN]; extern int pfthreshold; if(n > NAMELEN) n = NAMELEN; strncpy(buf, va, n); if(strncmp(buf, "stop", 4) == 0) procstopwait(p, Proc_stopme); else if(strncmp(buf, "kill", 4) == 0) { switch(p->state) { case Broken: unbreak(p); break; case Stopped: postnote(p, 0, "sys: killed", NExit); p->procctl = Proc_exitme; ready(p); break; default: postnote(p, 0, "sys: killed", NExit); p->procctl = Proc_exitme; } } else if(strncmp(buf, "hang", 4) == 0) p->hang = 1; else if(strncmp(buf, "nohang", 6) == 0) p->hang = 0; else if(strncmp(buf, "waitstop", 8) == 0) procstopwait(p, 0); else if(strncmp(buf, "startstop", 9) == 0) { if(p->state != Stopped) error(Ebadctl); p->procctl = Proc_traceme; ready(p); procstopwait(p, Proc_traceme); } else if(strncmp(buf, "start", 5) == 0) { if(p->state != Stopped) error(Ebadctl); ready(p); } else error(Ebadctl); } int procstopped(void *a) { Proc *p = a; return p->state == Stopped; } int procctlmemio(Proc *p, ulong offset, int n, void *va, int read) { KMap *k; Pte *pte; Page *pg; Segment *s; ulong soff, l; char *a = va, *b; for(;;) { s = seg(p, offset, 1); if(s == 0) error(Ebadarg); if(offset+n >= s->top) n = s->top-offset; if(!read && (s->type&SG_TYPE) == SG_TEXT) s = txt2data(p, s); s->steal++; soff = offset-s->base; if(waserror()) { s->steal--; nexterror(); } if(fixfault(s, offset, read, 0) == 0) break; poperror(); s->steal--; } poperror(); pte = s->map[soff/PTEMAPMEM]; if(pte == 0) panic("procctlmemio"); pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG]; if(pagedout(pg)) panic("procctlmemio1"); l = BY2PG - (offset&(BY2PG-1)); if(n > l) n = l; k = kmap(pg); b = (char*)VA(k); if(read == 1) memmove(a, b+(offset&(BY2PG-1)), n); else memmove(b+(offset&(BY2PG-1)), a, n); kunmap(k); /* Ensure the process sees text page changes */ if(s->flushme) memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); s->steal--; if(read == 0) p->newtlb = 1; return n; } Segment* txt2data(Proc *p, Segment *s) { int i; Segment *ps; ps = newseg(SG_DATA, s->base, s->size); ps->image = s->image; incref(ps->image); ps->fstart = s->fstart; ps->flen = s->flen; ps->flushme = 1; for(i = 0; i < NSEG; i++) if(p->seg[i] == s) break; if(p->seg[i] != s) panic("segment gone"); qunlock(&s->lk); putseg(s); qlock(&ps->lk); p->seg[i] = ps; return ps; } Segment* data2txt(Segment *s) { Segment *ps; ps = newseg(SG_TEXT, s->base, s->size); ps->image = s->image; incref(ps->image); ps->fstart = s->fstart; ps->flen = s->flen; ps->flushme = 1; return ps; } static char *counter[Pcounter]; int ncounter; int proccounter(char *name) { if(ncounter >= Pcounter) panic("too many process counters"); counter[ncounter] = name; return ncounter++; } ulong readcounters(Proc *p, ulong offset, void *va, int n) { char *bp; int i, j, m; extern char *sysctab[]; bp = smalloc((nsyscall+ncounter)*(NAMELEN+8)); m = 0; for(i = 0; i < Pcounter; i++) if(counter[i]) m += sprint(bp+m, "%s\t%8d\n", counter[i], p->counter[i]); j = 0; for(i = 0; i < nsyscall; i++) j += p->syscall[i]; m += sprint(bp+m, "syscall\t%8d\n", j); for(i = 0; i < nsyscall; i++) if(p->syscall[i]) m += sprint(bp+m, "%s\t%8d\n", sysctab[i], p->syscall[i]); n = readstr(offset, va, n, bp); free(bp); memset(p->counter, 0, sizeof(p->counter)); memset(p->syscall, 0, sizeof(p->syscall)); return n; }