4 #include "../port/lib.h"
8 #include "../port/error.h"
77 #define STATSIZE (2*KNAMELEN+12+9*12)
79 * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
80 * particularly on shared servers.
81 * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
85 "args", {Qargs}, 0, 0660,
86 "ctl", {Qctl}, 0, 0000,
88 "fpregs", {Qfpregs}, sizeof(FPsave), 0000,
89 "kregs", {Qkregs}, sizeof(Ureg), 0400,
90 "mem", {Qmem}, 0, 0000,
91 "note", {Qnote}, 0, 0000,
92 "noteid", {Qnoteid}, 0, 0664,
93 "notepg", {Qnotepg}, 0, 0000,
95 "ppid", {Qppid}, 0, 0444,
96 "proc", {Qproc}, 0, 0400,
97 "regs", {Qregs}, sizeof(Ureg), 0000,
98 "segment", {Qsegment}, 0, 0444,
99 "status", {Qstatus}, STATSIZE, 0444,
100 "text", {Qtext}, 0, 0000,
101 "wait", {Qwait}, 0, 0400,
102 "profile", {Qprofile}, 0, 0400,
103 "syscall", {Qsyscall}, 0, 0400,
109 CMclosefiles, "closefiles", 1,
110 CMfixedpri, "fixedpri", 2,
112 CMnohang, "nohang", 1,
113 CMnoswap, "noswap", 1,
116 CMprivate, "private", 1,
117 CMprofile, "profile", 1,
119 CMstartstop, "startstop", 1,
120 CMstartsyscall, "startsyscall", 1,
122 CMwaitstop, "waitstop", 1,
125 CMinterrupt, "interrupt", 1,
126 CMnointerrupt, "nointerrupt", 1,
127 CMperiod, "period", 2,
128 CMdeadline, "deadline", 2,
130 CMsporadic, "sporadic", 1,
131 CMdeadlinenotes, "deadlinenotes", 1,
138 /* Segment type from portdat.h */
139 static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed", };
143 * 4 bits of file type (qids above)
144 * 23 bits of process slot number + 1
146 * 32 bits of pid, for consistency checking
147 * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid.
149 #define QSHIFT 5 /* location in qid of proc slot # */
151 #define QID(q) ((((ulong)(q).path)&0x0000001F)>>0)
152 #define SLOT(q) (((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
153 #define PID(q) ((q).vers)
154 #define NOTEID(q) ((q).vers)
156 void procctlreq(Proc*, char*, int);
157 long procctlmemio(Chan*, Proc*, uintptr, void*, long, int);
158 Chan* proctext(Chan*, Proc*);
159 int procstopped(void*);
160 ulong procpagecount(Proc *);
162 static Traceevent *tevents;
165 static int tproduced, tconsumed;
166 void (*proctrace)(Proc*, int, vlong);
169 profclock(Ureg *ur, Timer *)
173 if(up == nil || up->state != Running)
176 /* user profiling clock */
178 tos = (Tos*)(USTKTOP-sizeof(Tos));
179 tos->clock += TK2MS(1);
185 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
191 ulong pid, path, perm, len;
194 mkqid(&qid, Qdir, 0, QTDIR);
195 devdir(c, qid, "#p", 0, eve, 0555, dp);
199 if(c->qid.path == Qdir){
201 strcpy(up->genbuf, "trace");
202 mkqid(&qid, Qtrace, -1, QTFILE);
203 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
208 /* ignore s and use name to find pid */
209 pid = strtol(name, &ename, 10);
210 if(pid==0 || ename[0]!='\0')
216 else if(--s >= conf.nproc)
223 sprint(up->genbuf, "%lud", pid);
225 * String comparison is done in devwalk so name must match its formatted pid
227 if(name != nil && strcmp(name, up->genbuf) != 0)
229 mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
230 devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
233 if(c->qid.path == Qtrace){
234 strcpy(up->genbuf, "trace");
235 mkqid(&qid, Qtrace, -1, QTFILE);
236 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
239 if(s >= nelem(procdir))
245 path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
247 /* p->procmode determines default mode for files in /proc */
248 p = proctab(SLOT(c->qid));
252 else /* just copy read bits */
253 perm |= p->procmode & 0444;
256 switch(QID(c->qid)) {
258 len = p->nwait; /* incorrect size, but >0 means there's something to read */
262 if(q != nil && q->profile != nil) {
263 len = (q->top-q->base)>>LRESPROF;
264 len *= sizeof(*q->profile);
269 mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
270 devdir(c, qid, tab->name, len, p->user, perm, dp);
275 _proctrace(Proc* p, Tevent etype, vlong ts)
279 if (p->trace == 0 || topens == 0 ||
280 tproduced - tconsumed >= Nevents)
283 te = &tevents[tproduced&Emask];
287 te->time = todget(nil);
296 if(conf.nproc >= (1<<(16-QSHIFT))-1)
297 print("warning: too many procs for devproc\n");
298 addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
302 procattach(char *spec)
304 return devattach('p', spec);
308 procwalk(Chan *c, Chan *nc, char **name, int nname)
310 return devwalk(c, nc, name, nname, 0, 0, procgen);
314 procstat(Chan *c, uchar *db, int n)
316 return devstat(c, db, n, 0, 0, procgen);
320 * none can't read or write state on other
321 * processes. This is to contain access of
322 * servers running as none should they be
323 * subverted by, for example, a stack attack.
330 if(strcmp(up->user, "none") != 0)
338 procopen(Chan *c, int omode)
345 if(c->qid.type & QTDIR)
346 return devopen(c, omode, 0, 0, procgen);
348 if(QID(c->qid) == Qtrace){
358 error("already open");
360 tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
363 tproduced = tconsumed = 0;
365 proctrace = _proctrace;
369 c->mode = openmode(omode);
375 p = proctab(SLOT(c->qid));
385 omode = openmode(omode);
438 c->pgrpid.path = pg->pgrpid+1;
439 c->pgrpid.vers = p->noteid;
443 print("procopen %#lux\n", QID(c->qid));
447 /* Affix pid to qid */
449 c->qid.vers = p->pid;
451 /* make sure the process slot didn't get reallocated while we were playing */
456 tc = devopen(c, omode, 0, 0, procgen);
464 procwstat(Chan *c, uchar *db, int n)
469 if(c->qid.type&QTDIR)
472 if(QID(c->qid) == Qtrace)
473 return devwstat(c, db, n);
475 p = proctab(SLOT(c->qid));
486 if(p->pid != PID(c->qid))
489 if(strcmp(up->user, p->user) != 0 && !iseve())
492 d = smalloc(sizeof(Dir)+n);
493 n = convM2D(db, n, &d[0], (char*)&d[1]);
496 if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
499 kstrdup(&p->user, d->uid);
501 /* p->procmode determines default mode for files in /proc */
503 p->procmode = d->mode&0777;
516 if((c->flag & COPEN) == 0)
532 segio(sio, nil, nil, 0, 0, 0);
540 procargs(Proc *p, char *buf, int nbuf)
548 snprint(buf, nbuf, "%s [%s]", p->text, p->args);
552 for(j = 0; j < nbuf - 1; j += m){
557 m = snprint(buf+j, nbuf-j, "%q", a);
566 eventsavailable(void *)
568 return tproduced > tconsumed;
572 prochaswaitq(void *x)
578 p = proctab(SLOT(c->qid));
579 return p->pid != PID(c->qid) || p->waitq != nil;
583 int2flag(int flag, char *s)
602 readns1(Chan *c, Proc *p, char *buf, int nbuf)
607 ulong minid, bestmid;
612 if(pg == nil || p->dot == nil || p->pid != PID(c->qid))
624 for(i = 0; i < MNTHASH; i++) {
625 for(f = pg->mnthash[i]; f != nil; f = f->hash) {
626 for(t = f->mount; t != nil; t = t->next) {
627 if(t->mountid >= minid && t->mountid < bestmid) {
628 bestmid = t->mountid;
638 i = snprint(buf, nbuf, "cd %s\n", p->dot->path->s);
640 c->nrock = bestmid+1;
642 int2flag(cm->mflag, flag);
643 if(strcmp(cm->to->path->s, "#M") == 0){
644 srv = srvname(cm->to->mchan);
645 i = snprint(buf, nbuf, "mount %s %s %s %s\n", flag,
646 srv==nil? cm->to->mchan->path->s : srv,
647 mh->from->path->s, cm->spec? cm->spec : "");
650 i = snprint(buf, nbuf, "bind %s %s %s\n", flag,
651 cm->to->path->s, mh->from->path->s);
661 procfdprint(Chan *c, int fd, char *s, int ns)
663 return snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %lud %.2ux) %5ld %8lld %s\n",
665 &"r w rw"[(c->mode&3)<<1],
666 devtab[c->type]->dc, c->dev,
667 c->qid.path, c->qid.vers, c->qid.type,
668 c->iounit, c->offset, c->path->s);
672 readfd1(Chan *c, Proc *p, char *buf, int nbuf)
678 if(fg == nil || p->dot == nil || p->pid != PID(c->qid))
683 return snprint(buf, nbuf, "%s\n", p->dot->path->s);
690 if(i < 0 || i > fg->maxfd)
693 if(fg->fd[i] != nil){
694 n = procfdprint(fg->fd[i], i, buf, nbuf);
704 * userspace can't pass negative file offset for a
705 * 64 bit kernel address, so we use 63 bit and sign
717 procread(Chan *c, void *va, long n, vlong off)
719 char *a, *sps, statbuf[1024];
720 int i, j, navail, ne, rsize;
733 if(c->qid.type & QTDIR)
734 return devdirread(c, a, n, 0, 0, procgen);
736 if(QID(c->qid) == Qtrace){
737 if(!eventsavailable(nil))
741 navail = tproduced - tconsumed;
742 if(navail > n / sizeof(Traceevent))
743 navail = n / sizeof(Traceevent);
745 ne = ((tconsumed & Emask) + navail > Nevents)?
746 Nevents - (tconsumed & Emask): navail;
747 memmove(rptr, &tevents[tconsumed & Emask],
748 ne * sizeof(Traceevent));
751 rptr += ne * sizeof(Traceevent);
754 return rptr - (uchar*)va;
757 p = proctab(SLOT(c->qid));
758 if(p->pid != PID(c->qid))
764 j = procargs(p, statbuf, sizeof(statbuf));
771 memmove(a, statbuf+offset, n);
780 if(p->pid != PID(c->qid))
783 if(p->syscalltrace != nil)
784 j = readstr(offset, a, n, p->syscalltrace);
790 addr = off2addr(off);
792 return procctlmemio(c, p, addr, va, n, 1);
794 if(!iseve() || poolisoverlap(secrmem, (uchar*)addr, n))
797 /* validate kernel addresses */
798 if(addr < (uintptr)end) {
799 if(addr+n > (uintptr)end)
800 n = (uintptr)end - addr;
801 memmove(a, (char*)addr, n);
804 for(i=0; i<nelem(conf.mem); i++){
806 /* klimit-1 because klimit might be zero! */
807 if(cm->kbase <= addr && addr <= cm->klimit-1){
808 if(addr+n >= cm->klimit-1)
809 n = cm->klimit - addr;
810 memmove(a, (char*)addr, n);
818 if(s == nil || s->profile == nil)
819 error("profile is off");
820 i = (s->top-s->base)>>LRESPROF;
821 i *= sizeof(s->profile[0]);
822 if(i < 0 || offset >= i)
826 memmove(a, ((char*)s->profile)+offset, n);
835 if(p->pid != PID(c->qid))
837 if(n < 1) /* must accept at least the '\0' */
842 i = strlen(p->note[0].msg) + 1;
845 memmove(a, p->note[0].msg, n-1);
849 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
856 if(offset >= sizeof(Proc))
858 if(offset+n > sizeof(Proc))
859 n = sizeof(Proc) - offset;
860 memmove(a, ((char*)p)+offset, n);
864 rptr = (uchar*)p->dbgreg;
865 rsize = sizeof(Ureg);
869 memset(&kur, 0, sizeof(Ureg));
872 rsize = sizeof(Ureg);
876 rptr = (uchar*)&p->fpsave;
877 rsize = sizeof(FPsave);
885 memmove(a, rptr+offset, n);
889 if(offset >= STATSIZE)
891 if(offset+n > STATSIZE)
892 n = STATSIZE - offset;
896 sps = statename[p->state];
898 memset(statbuf, ' ', sizeof statbuf);
899 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
900 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
901 readstr(0, statbuf+2*KNAMELEN, 11, sps);
904 for(i = 0; i < 6; i++) {
907 l = MACHP(0)->ticks - l;
908 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, tk2ms(l), NUMSIZE);
911 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, procpagecount(p)*BY2PG/1024, NUMSIZE);
912 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
913 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
918 for(i = 0; i < NSEG; i++) {
922 j += sprint(statbuf+j, "%-6s %c%c %8p %8p %4ld\n",
923 sname[sg->type&SG_TYPE],
924 sg->type&SG_RONLY ? 'R' : ' ',
925 sg->profile ? 'P' : ' ',
926 sg->base, sg->top, sg->ref);
935 if(!canqlock(&p->qwaitr))
944 while(p->waitq == nil && p->pid == PID(c->qid)) {
945 if(up == p && p->nchild == 0) {
950 sleep(&p->waitr, prochaswaitq, c);
953 if(p->pid != PID(c->qid)){
965 j = snprint(statbuf, sizeof(statbuf), "%d %lud %lud %lud %q",
967 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
982 if(offset == 0 || offset != c->mrock)
983 c->nrock = c->mrock = 0;
985 if(QID(c->qid) == Qns)
986 j = readns1(c, p, statbuf, sizeof(statbuf));
988 j = readfd1(c, p, statbuf, sizeof(statbuf));
992 } while(c->mrock <= offset);
993 i = c->mrock - offset;
1005 return readnum(offset, va, n, p->noteid, NUMSIZE);
1008 return readnum(offset, va, n, p->parentpid, NUMSIZE);
1012 return 0; /* not reached */
1016 procwrite(Chan *c, void *va, long n, vlong off)
1020 char *a, *arg, buf[ERRMAX];
1025 if(c->qid.type & QTDIR)
1028 p = proctab(SLOT(c->qid));
1030 /* Use the remembered noteid in the channel rather
1031 * than the process pgrpid
1033 if(QID(c->qid) == Qnotepg) {
1034 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1043 if(p->pid != PID(c->qid))
1046 switch(QID(c->qid)){
1055 memmove(arg, va, n);
1066 if(p->state != Stopped)
1068 n = procctlmemio(c, p, off2addr(off), va, n, 0);
1072 if(offset >= sizeof(Ureg))
1074 else if(offset+n > sizeof(Ureg))
1075 n = sizeof(Ureg) - offset;
1076 if(p->dbgreg == nil)
1078 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1082 if(offset >= sizeof(FPsave))
1084 else if(offset+n > sizeof(FPsave))
1085 n = sizeof(FPsave) - offset;
1086 memmove((uchar*)&p->fpsave+offset, va, n);
1090 procctlreq(p, va, n);
1098 memmove(buf, va, n);
1100 if(!postnote(p, 0, buf, NUser))
1101 error("note not posted");
1114 for(et = t+conf.nproc; t < et; t++) {
1115 if(t->state == Dead || t->kp)
1117 if(id == t->noteid) {
1119 if(strcmp(p->user, t->user) != 0)
1129 print("unknown qid in procwrite\n");
1159 proctext(Chan *c, Proc *p)
1185 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode != OREAD) {
1190 if(p->pid != PID(c->qid)) {
1202 procstopwait(Proc *p, int ctl)
1208 if(procstopped(p) || p->state == Broken)
1217 up->psstate = "Stopwait";
1223 sleep(&up->sleep, procstopped, p);
1231 procctlclosefiles(Proc *p, int all, int fd)
1244 while(fd <= f->maxfd){
1263 parsetime(vlong *rt, char *s)
1268 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1271 return("missing value");
1272 ticks=strtoul(s, &e, 10);
1275 l = strtoul(p, &e, 10);
1276 if(e-p > nelem(p10))
1277 return "too many digits after decimal point";
1279 return "ill-formed number";
1283 if (*e == '\0' || strcmp(e, "s") == 0){
1284 ticks = 1000000000 * ticks + l;
1285 }else if (strcmp(e, "ms") == 0){
1286 ticks = 1000000 * ticks + l/1000;
1287 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1288 ticks = 1000 * ticks + l/1000000;
1289 }else if (strcmp(e, "ns") != 0)
1290 return "unrecognized unit";
1296 procctlreq(Proc *p, char *va, int n)
1304 void (*pt)(Proc*, int, vlong);
1306 if(p->kp) /* no ctl requests to kprocs */
1309 cb = parsecmd(va, n);
1315 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1319 procctlclosefiles(p, 0, atoi(cb->f[1]));
1322 procctlclosefiles(p, 1, 0);
1333 p->procctl = Proc_exitme;
1334 postnote(p, 0, "sys: killed", NExit);
1338 p->procctl = Proc_exitme;
1339 postnote(p, 0, "sys: killed", NExit);
1349 pri = atoi(cb->f[1]);
1350 if(pri > PriNormal && !iseve())
1352 procpriority(p, pri, 0);
1355 pri = atoi(cb->f[1]);
1356 if(pri > PriNormal && !iseve())
1358 procpriority(p, pri, 1);
1365 if(s == nil || (s->type&SG_TYPE) != SG_TEXT)
1367 if(s->profile != nil)
1369 npc = (s->top-s->base)>>LRESPROF;
1370 s->profile = malloc(npc*sizeof(*s->profile));
1371 if(s->profile == nil)
1375 if(p->state != Stopped)
1380 if(p->state != Stopped)
1382 p->procctl = Proc_traceme;
1384 procstopwait(p, Proc_traceme);
1386 case CMstartsyscall:
1387 if(p->state != Stopped)
1389 p->procctl = Proc_tracesyscall;
1391 procstopwait(p, Proc_tracesyscall);
1394 procstopwait(p, Proc_stopme);
1400 procwired(p, atoi(cb->f[1]));
1408 p->trace = (atoi(cb->f[1]) != 0);
1415 postnote(p, 0, nil, NUser);
1421 error("notes pending");
1427 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1430 p->edf->T = time/1000; /* Edf times are in µs */
1435 if(e=parsetime(&time, cb->f[1]))
1438 p->edf->D = time/1000;
1443 if(e=parsetime(&time, cb->f[1]))
1446 p->edf->C = time/1000;
1451 p->edf->flags |= Sporadic;
1453 case CMdeadlinenotes:
1456 p->edf->flags |= Sendnotes;
1460 error("edf params");
1467 p->edf->flags |= Extratime;
1475 if(up->trace && pt != nil)
1485 procstopped(void *a)
1487 return ((Proc*)a)->state == Stopped;
1491 procctlmemio(Chan *c, Proc *p, uintptr offset, void *a, long n, int read)
1497 s = seg(p, offset, 0);
1500 eqlock(&p->seglock);
1502 qunlock(&p->seglock);
1507 sio = smalloc(sizeof(Segio));
1510 for(i = 0; i < NSEG; i++) {
1515 error(Egreg); /* segment gone */
1521 if(!read && (s->type&SG_TYPE) == SG_TEXT) {
1526 incref(s); /* for us while we copy */
1529 qunlock(&p->seglock);
1536 n = segio(sio, s, a, n, offset, read);