diff options
| -rw-r--r-- | sys/man/4/iostats | 7 | ||||
| -rw-r--r-- | sys/src/cmd/iostats.c | 534 | ||||
| -rw-r--r-- | sys/src/cmd/iostats/iostats.c | 601 | ||||
| -rw-r--r-- | sys/src/cmd/iostats/mkfile | 11 | ||||
| -rw-r--r-- | sys/src/cmd/iostats/statfs.h | 142 | ||||
| -rw-r--r-- | sys/src/cmd/iostats/statsrv.c | 672 |
6 files changed, 538 insertions, 1429 deletions
diff --git a/sys/man/4/iostats b/sys/man/4/iostats index cfc77c945..58387a0f5 100644 --- a/sys/man/4/iostats +++ b/sys/man/4/iostats @@ -17,7 +17,7 @@ iostats \- file system to measure I/O ] .SH DESCRIPTION .I Iostats -is a user-level file server that interposes itself between a program +is a user-level 9p filter that interposes itself between a program and the regular file server, which allows it to gather statistics of file system use at the level of the Plan 9 file system protocol, 9P. @@ -76,9 +76,10 @@ Start a new shell, displaying all 9P traffic caused by the shell or its children iostats -df /fd/1 rc .EE .SH SOURCE -.B /sys/src/cmd/iostats +.B /sys/src/cmd/iostats.c .SH SEE ALSO -.IR dup (3) +.IR dup (3), +.IR exportfs (4) .SH BUGS Poor clock resolution means that large amounts of I/O must be done to get accurate rate figures. diff --git a/sys/src/cmd/iostats.c b/sys/src/cmd/iostats.c new file mode 100644 index 000000000..0807f5bb1 --- /dev/null +++ b/sys/src/cmd/iostats.c @@ -0,0 +1,534 @@ +/* + * iostats - Gather file system information + */ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> + +#define DEBUGFILE "iostats.out" +#define DONESTR "done" + +enum{ + Maxfile = 1000, /* number of Files we'll log */ + Maxrpc = 20000,/* number of RPCs we'll log */ +}; + +typedef struct File File; +typedef struct Fid Fid; +typedef struct Req Req; +typedef struct Rpc Rpc; +typedef struct Stats Stats; + +/* per file statistics */ +struct File +{ + Qid qid; + char *path; + + ulong nopen; + + ulong nread; + vlong bread; + + ulong nwrite; + vlong bwrite; +}; + +/* fid context */ +struct Fid +{ + int fid; + Qid qid; + char *path; + File *file; /* set on open/create */ + Fid *next; +}; + +/* a request */ +struct Req +{ + Req *next; + vlong t; + + Fcall f; + uchar buf[]; +}; + +/* per rpc statistics */ +struct Rpc +{ + char *name; + ulong count; + vlong time; + vlong lo; + vlong hi; + vlong bin; + vlong bout; +}; + +/* all the statistics */ +struct Stats +{ + vlong totread; + vlong totwrite; + ulong nrpc; + vlong nproto; + Rpc rpc[Maxrpc]; + File file[Maxfile]; +}; + +Stats stats[1]; + +int pfd[2]; +int efd[2]; +int done; + +Lock rqlock; +Req *rqhead; + +Fid *fidtab[1024]; + +void +catcher(void *a, char *msg) +{ + USED(a); + + if(strcmp(msg, DONESTR) == 0) { + done = 1; + noted(NCONT); + } + if(strcmp(msg, "exit") == 0) + exits("exit"); + + noted(NDFLT); +} + +void +update(Rpc *rpc, vlong t) +{ + vlong t2; + + t2 = nsec(); + t = t2 - t; + if(t < 0) + t = 0; + + rpc->time += t; + if(t < rpc->lo) + rpc->lo = t; + if(t > rpc->hi) + rpc->hi = t; +} + +Fid** +fidhash(int fid) +{ + return &fidtab[fid % nelem(fidtab)]; +} + +Fid* +getfid(int fid, int new) +{ + Fid *f, **ff; + + ff = fidhash(fid); + for(f = *ff; f != nil; f = f->next){ + if(f->fid == fid) + return f; + } + if(new){ + f = mallocz(sizeof(*f), 1); + f->fid = fid; + f->next = *ff; + *ff = f; + } + return f; +} + +void +setfid(Fid *f, char *path, Qid qid) +{ + if(path != f->path){ + free(f->path); + f->path = path; + } + f->qid = qid; + f->file = nil; +} + +void +rattach(Fcall *fin, Fcall *fout) +{ + setfid(getfid(fin->fid, 1), strdup("/"), fout->qid); +} + +void +rwalk(Fcall *fin, Fcall *fout) +{ + Fid *of, *f; + int i; + + if((of = getfid(fin->fid, 0)) == nil) + return; + f = getfid(fin->newfid, 1); + if(f != of) + setfid(f, strdup(of->path), of->qid); + for(i=0; i<fout->nwqid; i++) + setfid(f, cleanname(smprint("%s/%s", f->path, fin->wname[i])), fout->wqid[i]); +} + +void +ropen(Fcall *fin, Fcall *fout) +{ + File *fs; + Fid *f; + + if((f = getfid(fin->fid, 0)) == nil) + return; + if(fin->type == Tcreate) + setfid(f, cleanname(smprint("%s/%s", f->path, fin->name)), fout->qid); + else + setfid(f, f->path, fout->qid); + for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){ + if(fs->nopen == 0){ + fs->path = strdup(f->path); + fs->qid = f->qid; + f->file = fs; + break; + } + if(fs->qid.path == f->qid.path && strcmp(fs->path, f->path) == 0){ + f->file = fs; + break; + } + } + if(f->file != nil) + f->file->nopen++; +} + +void +rclunk(Fcall *fin) +{ + Fid **ff, *f; + + for(ff = fidhash(fin->fid); (f = *ff) != nil; ff = &f->next){ + if(f->fid == fin->fid){ + *ff = f->next; + free(f->path); + free(f); + return; + } + } +} + +void +rio(Fcall *fin, Fcall *fout) +{ + Fid *f; + int count; + + count = fout->count; + if((f = getfid(fin->fid, 0)) == nil) + return; + switch(fout->type){ + case Rread: + if(f->file != nil){ + f->file->nread++; + f->file->bread += count; + } + stats->totread += count; + break; + case Rwrite: + if(f->file != nil){ + f->file->nwrite++; + f->file->bwrite += count; + } + stats->totwrite += count; + break; + } +} + +void +usage(void) +{ + fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + Rpc *rpc; + ulong ttime; + char *dbfile; + char buf[64*1024]; + float brpsec, bwpsec, bppsec; + int cpid, fspid, rspid, dbg, n, mflag; + File *fs; + Req *r, **rr; + + dbg = 0; + mflag = MREPL; + dbfile = DEBUGFILE; + + ARGBEGIN{ + case 'd': + dbg++; + break; + case 'f': + dbfile = ARGF(); + break; + case 'C': + mflag |= MCACHE; + break; + default: + usage(); + }ARGEND + + USED(dbfile); + + if(argc == 0) + usage(); + + if(pipe(pfd) < 0) + sysfatal("pipe"); + + switch(cpid = fork()) { + case -1: + sysfatal("fork"); + case 0: + close(pfd[1]); + if(getwd(buf, sizeof(buf)) == 0) + sysfatal("no working directory"); + + rfork(RFENVG|RFNAMEG|RFNOTEG); + + if(mount(pfd[0], -1, "/", mflag, "") < 0) + sysfatal("mount /"); + + bind("#c/pid", "/dev/pid", MREPL); + bind("#c/ppid", "/dev/ppid", MREPL); + bind("#e", "/env", MREPL|MCREATE); + close(0); + close(1); + close(2); + open("/fd/0", OREAD); + open("/fd/1", OWRITE); + open("/fd/2", OWRITE); + if(chdir(buf) < 0) + sysfatal("chdir"); + exec(argv[0], argv); + exec(smprint("/bin/%s", argv[0]), argv); + sysfatal("exec: %r"); + default: + close(pfd[0]); + } + + switch(fspid = fork()) { + default: + while(cpid != waitpid()) + ; + postnote(PNPROC, fspid, DONESTR); + while(fspid != waitpid()) + ; + exits(0); + case -1: + sysfatal("fork"); + case 0: + notify(catcher); + break; + } + + if(pipe(efd) < 0) + sysfatal("pipe"); + + /* spawn exportfs */ + switch(fork()) { + default: + close(efd[0]); + break; + case -1: + sysfatal("fork"); + case 0: + dup(efd[0], 0); + close(efd[0]); + close(efd[1]); + if(dbg){ + execl("/bin/exportfs", "exportfs", "-df", dbfile, "-r", "/", nil); + } else { + execl("/bin/exportfs", "exportfs", "-r", "/", nil); + } + exits(0); + } + + fmtinstall('F', fcallfmt); + + stats->rpc[Tversion].name = "version"; + stats->rpc[Tauth].name = "auth"; + stats->rpc[Tflush].name = "flush"; + stats->rpc[Tattach].name = "attach"; + stats->rpc[Twalk].name = "walk"; + stats->rpc[Topen].name = "open"; + stats->rpc[Tcreate].name = "create"; + stats->rpc[Tclunk].name = "clunk"; + stats->rpc[Tread].name = "read"; + stats->rpc[Twrite].name = "write"; + stats->rpc[Tremove].name = "remove"; + stats->rpc[Tstat].name = "stat"; + stats->rpc[Twstat].name = "wstat"; + + for(n = 0; n < Maxrpc; n++) + stats->rpc[n].lo = 10000000000LL; + + switch(rspid = rfork(RFPROC|RFMEM)) { + case 0: + /* read response from exportfs and pass to mount */ + while(!done){ + uchar tmp[sizeof(buf)]; + Fcall f; + + n = read(efd[1], buf, sizeof(buf)); + if(n < 0) + break; + if(n == 0) + continue; + + /* convert response */ + memset(&f, 0, sizeof(f)); + memmove(tmp, buf, n); + if(convM2S(tmp, n, &f) != n) + sysfatal("convM2S: %r"); + + /* find request to this response */ + lock(&rqlock); + for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){ + if(r->f.tag == f.tag){ + *rr = r->next; + r->next = nil; + break; + } + } + stats->nproto += n; + unlock(&rqlock); + + switch(f.type){ + case Ropen: + case Rcreate: + ropen(&r->f, &f); + break; + case Rclunk: + case Rremove: + rclunk(&r->f); + break; + case Rattach: + rattach(&r->f, &f); + break; + case Rwalk: + rwalk(&r->f, &f); + break; + case Rread: + case Rwrite: + rio(&r->f, &f); + break; + } + + rpc = &stats->rpc[r->f.type]; + update(rpc, r->t); + rpc->bout += n; + free(r); + + if(write(pfd[1], buf, n) != n) + break; + } + exits(0); + default: + /* read request from mount and pass to exportfs */ + while(!done){ + n = read(pfd[1], buf, sizeof(buf)); + if(n < 0) + break; + if(n == 0) + continue; + + r = mallocz(sizeof(*r) + n, 1); + memmove(r->buf, buf, n); + if(convM2S(r->buf, n, &r->f) != n) + sysfatal("convM2S: %r"); + + rpc = &stats->rpc[r->f.type]; + rpc->count++; + rpc->bin += n; + + lock(&rqlock); + stats->nrpc++; + stats->nproto += n; + r->next = rqhead; + rqhead = r; + unlock(&rqlock); + + r->t = nsec(); + + if(write(efd[1], buf, n) != n) + break; + } + } + + /* shutdown */ + done = 1; + postnote(PNPROC, rspid, DONESTR); + close(pfd[1]); + close(efd[1]); + + /* dump statistics */ + rpc = &stats->rpc[Tread]; + brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001); + + rpc = &stats->rpc[Twrite]; + bwpsec = (double)stats->totwrite / (((float)rpc->time/1e9)+.000001); + + ttime = 0; + for(n = 0; n < Maxrpc; n++) { + rpc = &stats->rpc[n]; + if(rpc->count == 0) + continue; + ttime += rpc->time; + } + + bppsec = (double)stats->nproto / ((ttime/1e9)+.000001); + + fprint(2, "\nread %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0); + fprint(2, "write %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0); + fprint(2, "protocol %llud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0); + fprint(2, "rpc %lud count\n\n", stats->nrpc); + + fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n", + "Message", "Count", "Low", "High", "Time", "Averg"); + + for(n = 0; n < Maxrpc; n++) { + rpc = &stats->rpc[n]; + if(rpc->count == 0) + continue; + fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8llud %8llud bytes\n", + rpc->name, + rpc->count, + rpc->lo/1000000, + rpc->hi/1000000, + rpc->time/1000000, + rpc->time/1000000/rpc->count, + rpc->bin, + rpc->bout); + } + + fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n"); + for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){ + if(fs->nopen == 0) + break; + fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n", + fs->nopen, + fs->nread, fs->bread, + fs->nwrite, fs->bwrite, + fs->path); + } + + exits(0); +} diff --git a/sys/src/cmd/iostats/iostats.c b/sys/src/cmd/iostats/iostats.c deleted file mode 100644 index b85e1d75e..000000000 --- a/sys/src/cmd/iostats/iostats.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * iostats - Gather file system information - */ -#include <u.h> -#include <libc.h> -#include <auth.h> -#include <fcall.h> -#define Extern -#include "statfs.h" - -void runprog(char**); - -void (*fcalls[])(Fsrpc*) = -{ - [Tversion] Xversion, - [Tauth] Xauth, - [Tflush] Xflush, - [Tattach] Xattach, - [Twalk] Xwalk, - [Topen] slave, - [Tcreate] Xcreate, - [Tclunk] Xclunk, - [Tread] slave, - [Twrite] slave, - [Tremove] Xremove, - [Tstat] Xstat, - [Twstat] Xwstat, -}; - -int p[2]; - -void -usage(void) -{ - fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n"); - exits("usage"); -} - -void -main(int argc, char **argv) -{ - Fsrpc *r; - Rpc *rpc; - Proc *m; - Frec *fr; - Fid *fid; - ulong ttime; - char *dbfile, *s; - char buf[128]; - float brpsec, bwpsec, bppsec; - int type, cpid, fspid, n, mflag; - - mflag = MREPL; - dbfile = DEBUGFILE; - - ARGBEGIN{ - case 'd': - dbg++; - break; - case 'f': - dbfile = ARGF(); - break; - case 'C': - mflag |= MCACHE; - break; - default: - usage(); - }ARGEND - - if(argc == 0) - usage(); - - if(dbg) { - close(2); - create(dbfile, OWRITE, 0666); - } - - if(pipe(p) < 0) - fatal("pipe"); - - switch(cpid = fork()) { - case -1: - fatal("fork"); - case 0: - close(p[1]); - if(getwd(buf, sizeof(buf)) == 0) - fatal("no working directory"); - - rfork(RFENVG|RFNAMEG|RFNOTEG); - if(mount(p[0], -1, "/", mflag, "") < 0) - fatal("mount /"); - - bind("#c/pid", "/dev/pid", MREPL); - bind("#e", "/env", MREPL|MCREATE); - close(0); - close(1); - close(2); - open("/fd/0", OREAD); - open("/fd/1", OWRITE); - open("/fd/2", OWRITE); - - if(chdir(buf) < 0) - fatal("chdir"); - - runprog(argv); - default: - close(p[0]); - } - - switch(fspid = fork()) { - default: - while(cpid != waitpid()) - ; - postnote(PNPROC, fspid, DONESTR); - while(fspid != waitpid()) - ; - exits(0); - case -1: - fatal("fork"); - case 0: - break; - } - - /* Allocate work queues in shared memory */ - malloc(Dsegpad); - Workq = malloc(sizeof(Fsrpc)*Nr_workbufs); - stats = malloc(sizeof(Stats)); - fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1); - - if(Workq == 0 || fhash == 0 || stats == 0) - fatal("no initial memory"); - - memset(Workq, 0, sizeof(Fsrpc)*Nr_workbufs); - memset(stats, 0, sizeof(Stats)); - - stats->rpc[Tversion].name = "version"; - stats->rpc[Tauth].name = "auth"; - stats->rpc[Tflush].name = "flush"; - stats->rpc[Tattach].name = "attach"; - stats->rpc[Twalk].name = "walk"; - stats->rpc[Topen].name = "open"; - stats->rpc[Tcreate].name = "create"; - stats->rpc[Tclunk].name = "clunk"; - stats->rpc[Tread].name = "read"; - stats->rpc[Twrite].name = "write"; - stats->rpc[Tremove].name = "remove"; - stats->rpc[Tstat].name = "stat"; - stats->rpc[Twstat].name = "wstat"; - - for(n = 0; n < Maxrpc; n++) - stats->rpc[n].lo = 10000000000LL; - - fmtinstall('M', dirmodefmt); - fmtinstall('D', dirfmt); - fmtinstall('F', fcallfmt); - - if(chdir("/") < 0) - fatal("chdir"); - - initroot(); - - DEBUG(2, "statfs: %s\n", buf); - - notify(catcher); - - for(;;) { - r = getsbuf(); - if(r == 0) - fatal("Out of service buffers"); - - while((n = read9pmsg(p[1], r->buf, sizeof(r->buf))) == 0 && !done) - ; - if(done || n < 0) - break; - - if(convM2S(r->buf, n, &r->work) == 0) - fatal("format error"); - - stats->nrpc++; - stats->nproto += n; - - DEBUG(2, "%F\n", &r->work); - - type = r->work.type; - rpc = &stats->rpc[type]; - rpc->count++; - rpc->bin += n; - (fcalls[type])(r); - } - - /* Clear away the slave children */ - for(m = Proclist; m; m = m->next) - postnote(PNPROC, m->pid, "kill"); - - rpc = &stats->rpc[Tread]; - brpsec = (float)stats->totread / (((float)rpc->time/1e9)+.000001); - - rpc = &stats->rpc[Twrite]; - bwpsec = (float)stats->totwrite / (((float)rpc->time/1e9)+.000001); - - ttime = 0; - for(n = 0; n < Maxrpc; n++) { - rpc = &stats->rpc[n]; - if(rpc->count == 0) - continue; - ttime += rpc->time; - } - - bppsec = (float)stats->nproto / ((ttime/1e9)+.000001); - - fprint(2, "\nread %lud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0); - fprint(2, "write %lud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0); - fprint(2, "protocol %lud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0); - fprint(2, "rpc %lud count\n\n", stats->nrpc); - - fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n", - "Message", "Count", "Low", "High", "Time", "Averg"); - - for(n = 0; n < Maxrpc; n++) { - rpc = &stats->rpc[n]; - if(rpc->count == 0) - continue; - fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8lud %8lud bytes\n", - rpc->name, - rpc->count, - rpc->lo/1000000, - rpc->hi/1000000, - rpc->time/1000000, - rpc->time/1000000/rpc->count, - rpc->bin, - rpc->bout); - } - - for(n = 0; n < FHASHSIZE; n++) - for(fid = fhash[n]; fid; fid = fid->next) - if(fid->nread || fid->nwrite) - fidreport(fid); - if(frhead == 0) - exits(0); - - fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n"); - for(fr = frhead; fr; fr = fr->next) { - s = fr->op; - if(*s) { - if(strcmp(s, "/fd/0") == 0) - s = "(stdin)"; - else - if(strcmp(s, "/fd/1") == 0) - s = "(stdout)"; - else - if(strcmp(s, "/fd/2") == 0) - s = "(stderr)"; - } - else - s = "/."; - - fprint(2, "%5lud %8lud %8lud %8lud %8lud %s\n", fr->opens, fr->nread, fr->bread, - fr->nwrite, fr->bwrite, s); - } - - exits(0); -} - -void -reply(Fcall *r, Fcall *t, char *err) -{ - uchar data[IOHDRSZ+Maxfdata]; - int n; - - t->tag = r->tag; - t->fid = r->fid; - if(err) { - t->type = Rerror; - t->ename = err; - } - else - t->type = r->type + 1; - - DEBUG(2, "\t%F\n", t); - - n = convS2M(t, data, sizeof data); - if(write(p[1], data, n)!=n) - fatal("mount write"); - stats->nproto += n; - stats->rpc[t->type-1].bout += n; -} - -Fid * -getfid(int nr) -{ - Fid *f; - - for(f = fidhash(nr); f; f = f->next) - if(f->nr == nr) - return f; - - return 0; -} - -int -freefid(int nr) -{ - Fid *f, **l; - - l = &fidhash(nr); - for(f = *l; f; f = f->next) { - if(f->nr == nr) { - *l = f->next; - f->next = fidfree; - fidfree = f; - return 1; - } - l = &f->next; - } - - return 0; -} - -Fid * -newfid(int nr) -{ - Fid *new, **l; - int i; - - l = &fidhash(nr); - for(new = *l; new; new = new->next) - if(new->nr == nr) - return 0; - - if(fidfree == 0) { - fidfree = mallocz(sizeof(Fid) * Fidchunk, 1); - if(fidfree == 0) - fatal("out of memory"); - - for(i = 0; i < Fidchunk-1; i++) - fidfree[i].next = &fidfree[i+1]; - - fidfree[Fidchunk-1].next = 0; - } - - new = fidfree; - fidfree = new->next; - - memset(new, 0, sizeof(Fid)); - new->next = *l; - *l = new; - new->nr = nr; - new->fid = -1; - new->nread = 0; - new->nwrite = 0; - new->bread = 0; - new->bwrite = 0; - - return new; -} - -Fsrpc * -getsbuf(void) -{ - static int ap; - int look; - Fsrpc *wb; - - for(look = 0; look < Nr_workbufs; look++) { - if(++ap == Nr_workbufs) - ap = 0; - if(Workq[ap].busy == 0) - break; - } - - if(look == Nr_workbufs) - fatal("No more work buffers"); - - wb = &Workq[ap]; - wb->pid = 0; - wb->canint = 0; - wb->flushtag = NOTAG; - wb->busy = 1; - - return wb; -} - -char * -strcatalloc(char *p, char *n) -{ - char *v; - - v = realloc(p, strlen(p)+strlen(n)+1); - if(v == 0) - fatal("no memory"); - strcat(v, n); - return v; -} - -File * -file(File *parent, char *name) -{ - char buf[128]; - File *f, *new; - Dir *dir; - - DEBUG(2, "\tfile: 0x%p %s name %s\n", parent, parent->name, name); - - for(f = parent->child; f; f = f->childlist) - if(strcmp(name, f->name) == 0) - break; - - if(f != nil && !f->inval) - return f; - makepath(buf, parent, name); - dir = dirstat(buf); - if(dir == nil) - return 0; - if(f != nil){ - free(dir); - f->inval = 0; - return f; - } - - new = malloc(sizeof(File)); - if(new == 0) - fatal("no memory"); - - memset(new, 0, sizeof(File)); - new->name = strdup(name); - if(new->name == nil) - fatal("can't strdup"); - new->qid.type = dir->qid.type; - new->qid.vers = dir->qid.vers; - new->qid.path = ++qid; - - new->parent = parent; - new->childlist = parent->child; - parent->child = new; - - free(dir); - return new; -} - -void -initroot(void) -{ - Dir *dir; - - root = malloc(sizeof(File)); - if(root == 0) - fatal("no memory"); - - memset(root, 0, sizeof(File)); - root->name = strdup("/"); - if(root->name == nil) - fatal("can't strdup"); - dir = dirstat(root->name); - if(dir == nil) - fatal("root stat"); - - root->qid.type = dir->qid.type; - root->qid.vers = dir->qid.vers; - root->qid.path = ++qid; - free(dir); -} - -void -makepath(char *as, File *p, char *name) -{ - char *c, *seg[100]; - int i; - char *s; - - seg[0] = name; - for(i = 1; i < 100 && p; i++, p = p->parent){ - seg[i] = p->name; - if(strcmp(p->name, "/") == 0) - seg[i] = ""; /* will insert slash later */ - } - - s = as; - while(i--) { - for(c = seg[i]; *c; c++) - *s++ = *c; - *s++ = '/'; - } - while(s[-1] == '/') - s--; - *s = '\0'; - if(as == s) /* empty string is root */ - strcpy(as, "/"); -} - -void -fatal(char *s) -{ - Proc *m; - - fprint(2, "iostats: %s: %r\n", s); - - /* Clear away the slave children */ - for(m = Proclist; m; m = m->next) - postnote(PNPROC, m->pid, "exit"); - - exits("fatal"); -} - -char* -rdenv(char *v, char **end) -{ - int fd, n; - char *buf; - Dir *d; - if((fd = open(v, OREAD)) == -1) - return nil; - d = dirfstat(fd); - if(d == nil || (buf = malloc(d->length + 1)) == nil) - return nil; - n = (int)d->length; - n = read(fd, buf, n); - close(fd); - if(n <= 0){ - free(buf); - buf = nil; - }else{ - if(buf[n-1] != '\0') - buf[n++] = '\0'; - *end = &buf[n]; - } - free(d); - return buf; -} - -char Defaultpath[] = ".\0/bin"; -void -runprog(char *argv[]) -{ - char *path, *ep, *p; - char arg0[256]; - - path = rdenv("/env/path", &ep); - if(path == nil){ - path = Defaultpath; - ep = path+sizeof(Defaultpath); - } - for(p = path; p < ep; p += strlen(p)+1){ - snprint(arg0, sizeof arg0, "%s/%s", p, argv[0]); - exec(arg0, argv); - } - fatal("exec"); -} - -void -catcher(void *a, char *msg) -{ - USED(a); - if(strcmp(msg, DONESTR) == 0) { - done = 1; - noted(NCONT); - } - if(strcmp(msg, "exit") == 0) - exits("exit"); - - noted(NDFLT); -} - -void -fidreport(Fid *f) -{ - char *p, path[128]; - Frec *fr; - - p = path; - makepath(p, f->f, ""); - - for(fr = frhead; fr; fr = fr->next) { - if(strcmp(fr->op, p) == 0) { - fr->nread += f->nread; - fr->nwrite += f->nwrite; - fr->bread += f->bread; - fr->bwrite += f->bwrite; - fr->opens++; - return; - } - } - - fr = malloc(sizeof(Frec)); - if(fr == 0 || (fr->op = strdup(p)) == 0) - fatal("no memory"); - - fr->nread = f->nread; - fr->nwrite = f->nwrite; - fr->bread = f->bread; - fr->bwrite = f->bwrite; - fr->opens = 1; - if(frhead == 0) { - frhead = fr; - frtail = fr; - } - else { - frtail->next = fr; - frtail = fr; - } - fr->next = 0; -} diff --git a/sys/src/cmd/iostats/mkfile b/sys/src/cmd/iostats/mkfile deleted file mode 100644 index 1d4bee534..000000000 --- a/sys/src/cmd/iostats/mkfile +++ /dev/null @@ -1,11 +0,0 @@ -</$objtype/mkfile - -TARG=iostats -OFILES=iostats.$O\ - statsrv.$O\ - -HFILES=statfs.h\ - -BIN=/$objtype/bin -</sys/src/cmd/mkone - diff --git a/sys/src/cmd/iostats/statfs.h b/sys/src/cmd/iostats/statfs.h deleted file mode 100644 index 68fccdc00..000000000 --- a/sys/src/cmd/iostats/statfs.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * statfs.h - definitions for statistic gathering file server - */ - -#define DEBUGFILE "iostats.out" -#define DONESTR "done" -#define DEBUG if(!dbg){}else fprint -#define MAXPROC 64 -#define FHASHSIZE 64 -#define fidhash(s) fhash[s%FHASHSIZE] - -enum{ - Maxfdata = 8192, /* max size of data in 9P message */ - Maxrpc = 20000,/* number of RPCs we'll log */ -}; - -typedef struct Fsrpc Fsrpc; -typedef struct Fid Fid; -typedef struct File File; -typedef struct Proc Proc; -typedef struct Stats Stats; -typedef struct Rpc Rpc; -typedef struct Frec Frec; - -struct Frec -{ - Frec *next; - char *op; - ulong nread; - ulong nwrite; - ulong bread; - ulong bwrite; - ulong opens; -}; - -struct Rpc -{ - char *name; - ulong count; - vlong time; - vlong lo; - vlong hi; - ulong bin; - ulong bout; -}; - -struct Stats -{ - ulong totread; - ulong totwrite; - ulong nrpc; - ulong nproto; - Rpc rpc[Maxrpc]; -}; - -struct Fsrpc -{ - int busy; /* Work buffer has pending rpc to service */ - uintptr pid; /* Pid of slave process executing the rpc */ - int canint; /* Interrupt gate */ - int flushtag; /* Tag on which to reply to flush */ - Fcall work; /* Plan 9 incoming Fcall */ - uchar buf[IOHDRSZ+Maxfdata]; /* Data buffer */ -}; - -struct Fid -{ - int fid; /* system fd for i/o */ - File *f; /* File attached to this fid */ - int mode; - int nr; /* fid number */ - Fid *next; /* hash link */ - ulong nread; - ulong nwrite; - ulong bread; - ulong bwrite; - vlong offset; /* for directories */ -}; - -struct File -{ - char *name; - Qid qid; - int inval; - File *parent; - File *child; - File *childlist; -}; - -struct Proc -{ - uintptr pid; - int busy; - Proc *next; -}; - -enum -{ - Nr_workbufs = 40, - Dsegpad = 8192, - Fidchunk = 1000, -}; - -Extern Fsrpc *Workq; -Extern int dbg; -Extern File *root; -Extern Fid **fhash; -Extern Fid *fidfree; -Extern int qid; -Extern Proc *Proclist; -Extern int done; -Extern Stats *stats; -Extern Frec *frhead; -Extern Frec *frtail; -Extern int myiounit; - -/* File system protocol service procedures */ -void Xcreate(Fsrpc*), Xclunk(Fsrpc*); -void Xversion(Fsrpc*), Xauth(Fsrpc*), Xflush(Fsrpc*); -void Xattach(Fsrpc*), Xwalk(Fsrpc*), Xauth(Fsrpc*); -void Xremove(Fsrpc*), Xstat(Fsrpc*), Xwstat(Fsrpc*); -void slave(Fsrpc*); - -void reply(Fcall*, Fcall*, char*); -Fid *getfid(int); -int freefid(int); -Fid *newfid(int); -Fsrpc *getsbuf(void); -void initroot(void); -void fatal(char*); -void makepath(char*, File*, char*); -File *file(File*, char*); -void slaveopen(Fsrpc*); -void slaveread(Fsrpc*); -void slavewrite(Fsrpc*); -void blockingslave(void); -void reopen(Fid *f); -void noteproc(int, char*); -void flushaction(void*, char*); -void catcher(void*, char*); -ulong msec(void); -void fidreport(Fid*); diff --git a/sys/src/cmd/iostats/statsrv.c b/sys/src/cmd/iostats/statsrv.c deleted file mode 100644 index 7222bd825..000000000 --- a/sys/src/cmd/iostats/statsrv.c +++ /dev/null @@ -1,672 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <auth.h> -#include <fcall.h> -#define Extern extern -#include "statfs.h" - -char Ebadfid[] = "Bad fid"; -char Enotdir[] ="Not a directory"; -char Edupfid[] = "Fid already in use"; -char Eopen[] = "Fid already opened"; -char Exmnt[] = "Cannot .. past mount point"; -char Enoauth[] = "iostats: Authentication failed"; -char Ebadver[] = "Unrecognized 9P version"; - -int -okfile(char *s, int mode) -{ - if(strncmp(s, "/fd/", 3) == 0){ - /* 0, 1, and 2 we handle ourselves */ - if(s[4]=='/' || atoi(s+4) > 2) - return 0; - return 1; - } - if(strncmp(s, "/net/ssl", 8) == 0) - return 0; - if(strncmp(s, "/net/tls", 8) == 0) - return 0; - if(strncmp(s, "/srv/", 5) == 0 && ((mode&3) == OWRITE || (mode&3) == ORDWR)) - return 0; - return 1; -} - -void -update(Rpc *rpc, vlong t) -{ - vlong t2; - - t2 = nsec(); - t = t2 - t; - if(t < 0) - t = 0; - - rpc->time += t; - if(t < rpc->lo) - rpc->lo = t; - if(t > rpc->hi) - rpc->hi = t; -} - -void -Xversion(Fsrpc *r) -{ - Fcall thdr; - vlong t; - - t = nsec(); - - if(r->work.msize > IOHDRSZ+Maxfdata) - thdr.msize = IOHDRSZ+Maxfdata; - else - thdr.msize = r->work.msize; - myiounit = thdr.msize - IOHDRSZ; - if(strncmp(r->work.version, "9P2000", 6) != 0){ - reply(&r->work, &thdr, Ebadver); - r->busy = 0; - return; - } - thdr.version = "9P2000"; - /* BUG: should clunk all fids */ - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tversion], t); -} - -void -Xauth(Fsrpc *r) -{ - Fcall thdr; - vlong t; - - t = nsec(); - - reply(&r->work, &thdr, Enoauth); - r->busy = 0; - - update(&stats->rpc[Tauth], t); -} - -void -Xflush(Fsrpc *r) -{ - Fsrpc *t, *e; - Fcall thdr; - - e = &Workq[Nr_workbufs]; - - for(t = Workq; t < e; t++) { - if(t->work.tag == r->work.oldtag) { - DEBUG(2, "\tQ busy %d pid %p can %d\n", t->busy, t->pid, t->canint); - if(t->busy && t->pid) { - t->flushtag = r->work.tag; - DEBUG(2, "\tset flushtag %d\n", r->work.tag); - if(t->canint) - postnote(PNPROC, t->pid, "flush"); - r->busy = 0; - return; - } - } - } - - reply(&r->work, &thdr, 0); - DEBUG(2, "\tflush reply\n"); - r->busy = 0; -} - -void -Xattach(Fsrpc *r) -{ - Fcall thdr; - Fid *f; - vlong t; - - t = nsec(); - - f = newfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - - f->f = root; - thdr.qid = f->f->qid; - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tattach], t); -} - -void -Xwalk(Fsrpc *r) -{ - char errbuf[ERRMAX], *err; - Fcall thdr; - Fid *f, *n; - File *nf; - vlong t; - int i; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - n = nil; - if(r->work.newfid != r->work.fid){ - n = newfid(r->work.newfid); - if(n == 0) { - reply(&r->work, &thdr, Edupfid); - r->busy = 0; - return; - } - n->f = f->f; - f = n; /* walk new guy */ - } - - thdr.nwqid = 0; - err = nil; - for(i=0; i<r->work.nwname; i++){ - if(i >= MAXWELEM) - break; - if(strcmp(r->work.wname[i], "..") == 0) { - if(f->f->parent == 0) { - err = Exmnt; - break; - } - f->f = f->f->parent; - thdr.wqid[thdr.nwqid++] = f->f->qid; - continue; - } - - nf = file(f->f, r->work.wname[i]); - if(nf == 0) { - errstr(errbuf, sizeof errbuf); - err = errbuf; - break; - } - - f->f = nf; - thdr.wqid[thdr.nwqid++] = nf->qid; - continue; - } - - if(err == nil && thdr.nwqid == 0 && r->work.nwname > 0) - err = "file does not exist"; - - if(n != nil && (err != 0 || thdr.nwqid < r->work.nwname)){ - /* clunk the new fid, which is the one we walked */ - freefid(n->nr); - } - - if(thdr.nwqid > 0) - err = nil; - reply(&r->work, &thdr, err); - r->busy = 0; - - update(&stats->rpc[Twalk], t); -} - -void -Xclunk(Fsrpc *r) -{ - Fcall thdr; - Fid *f; - vlong t; - int fid; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - - if(f->fid >= 0) - close(f->fid); - - fid = r->work.fid; - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tclunk], t); - - if(f->nread || f->nwrite) - fidreport(f); - - freefid(fid); -} - -void -Xstat(Fsrpc *r) -{ - char err[ERRMAX], path[128]; - uchar statbuf[STATMAX]; - Fcall thdr; - Fid *f; - int s; - vlong t; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - makepath(path, f->f, ""); - if(!okfile(path, -1)){ - snprint(err, sizeof err, "iostats: can't simulate %s", path); - reply(&r->work, &thdr, err); - r->busy = 0; - return; - } - - if(f->fid >= 0) - s = fstat(f->fid, statbuf, sizeof statbuf); - else - s = stat(path, statbuf, sizeof statbuf); - - if(s < 0) { - errstr(err, sizeof err); - reply(&r->work, &thdr, err); - r->busy = 0; - return; - } - thdr.stat = statbuf; - thdr.nstat = s; - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tstat], t); -} - -void -Xcreate(Fsrpc *r) -{ - char err[ERRMAX], path[128]; - Fcall thdr; - Fid *f; - File *nf; - vlong t; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - - - makepath(path, f->f, r->work.name); - f->fid = create(path, r->work.mode, r->work.perm); - if(f->fid < 0) { - errstr(err, sizeof err); - reply(&r->work, &thdr, err); - r->busy = 0; - return; - } - - nf = file(f->f, r->work.name); - if(nf == 0) { - errstr(err, sizeof err); - reply(&r->work, &thdr, err); - r->busy = 0; - return; - } - - f->mode = r->work.mode; - f->f = nf; - thdr.iounit = myiounit; - thdr.qid = f->f->qid; - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tcreate], t); -} - - -void -Xremove(Fsrpc *r) -{ - char err[ERRMAX], path[128]; - Fcall thdr; - Fid *f; - vlong t; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - - makepath(path, f->f, ""); - DEBUG(2, "\tremove: %s\n", path); - if(remove(path) < 0) { - errstr(err, sizeof err); - reply(&r->work, &thdr, err); - freefid(r->work.fid); - r->busy = 0; - return; - } - - f->f->inval = 1; - if(f->fid >= 0) - close(f->fid); - freefid(r->work.fid); - - reply(&r->work, &thdr, 0); - r->busy = 0; - - update(&stats->rpc[Tremove], t); -} - -void -Xwstat(Fsrpc *r) -{ - char err[ERRMAX], path[128]; - Fcall thdr; - Fid *f; - int s; - vlong t; - - t = nsec(); - - f = getfid(r->work.fid); - if(f == 0) { - reply(&r->work, &thdr, Ebadfid); - r->busy = 0; - return; - } - if(f->fid >= 0) - s = fwstat(f->fid, r->work.stat, r->work.nstat); - else { - makepath(path, f->f, ""); - s = wstat(path, r->work.stat, r->work.nstat); - } - if(s < 0) { - errstr(err, sizeof err); - reply(&r->work, &thdr, err); - } - else - reply(&r->work, &thdr, 0); - - r->busy = 0; - update(&stats->rpc[Twstat], t); -} - -void -slave(Fsrpc *f) -{ - int r; - Proc *p; - uintptr pid; - static int nproc; - - for(;;) { - for(p = Proclist; p; p = p->next) { - if(p->busy == 0) { - f->pid = p->pid; - p->busy = 1; - pid = (uintptr)rendezvous((void*)p->pid, f); - if(pid != p->pid) - fatal("rendezvous sync fail"); - return; - } - } - - if(++nproc > MAXPROC) - fatal("too many procs"); - - r = rfork(RFPROC|RFMEM); - if(r < 0) - fatal("rfork"); - - if(r == 0) - blockingslave(); - - p = malloc(sizeof(Proc)); - if(p == 0) - fatal("out of memory"); - - p->busy = 0; - p->pid = r; - p->next = Proclist; - Proclist = p; - - rendezvous((void*)p->pid, p); - } -} - -void -blockingslave(void) -{ - Proc *m; - uintptr pid; - Fsrpc *p; - Fcall thdr; - - notify(flushaction); - - pid = getpid(); - - m = rendezvous((void*)pid, 0); - - for(;;) { - p = rendezvous((void*)pid, (void*)pid); - if(p == (void*)~0) /* Interrupted */ - continue; - - DEBUG(2, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid); - if(p->flushtag != NOTAG) - return; - - switch(p->work.type) { - case Tread: - slaveread(p); - break; - case Twrite: - slavewrite(p); - break; - case Topen: - slaveopen(p); - break; - default: - reply(&p->work, &thdr, "exportfs: slave type error"); - } - if(p->flushtag != NOTAG) { - p->work.type = Tflush; - p->work.tag = p->flushtag; - reply(&p->work, &thdr, 0); - } - p->busy = 0; - m->busy = 0; - } -} - -void -slaveopen(Fsrpc *p) -{ - char err[ERRMAX], path[128]; - Fcall *work, thdr; - Fid *f; - vlong t; - - work = &p->work; - - t = nsec(); - - f = getfid(work->fid); - if(f == 0) { - reply(work, &thdr, Ebadfid); - return; - } - if(f->fid >= 0) { - close(f->fid); - f->fid = -1; - } - - makepath(path, f->f, ""); - DEBUG(2, "\topen: %s %d\n", path, work->mode); - - p->canint = 1; - if(p->flushtag != NOTAG) - return; - - if(!okfile(path, work->mode)){ - snprint(err, sizeof err, "iostats can't simulate %s", path); - reply(work, &thdr, err); - return; - } - - /* There is a race here I ignore because there are no locks */ - f->fid = open(path, work->mode); - p->canint = 0; - if(f->fid < 0) { - errstr(err, sizeof err); - reply(work, &thdr, err); - return; - } - - DEBUG(2, "\topen: fd %d\n", f->fid); - f->mode = work->mode; - thdr.iounit = myiounit; - thdr.qid = f->f->qid; - reply(work, &thdr, 0); - - update(&stats->rpc[Topen], t); -} - -void -slaveread(Fsrpc *p) -{ - char data[Maxfdata], err[ERRMAX]; - Fcall *work, thdr; - Fid *f; - int n, r; - vlong t; - - work = &p->work; - - t = nsec(); - - f = getfid(work->fid); - if(f == 0) { - reply(work, &thdr, Ebadfid); - return; - } - - n = (work->count > Maxfdata) ? Maxfdata : work->count; - p->canint = 1; - if(p->flushtag != NOTAG) - return; - /* can't just call pread, since directories must update the offset */ - if(f->f->qid.type&QTDIR){ - if(work->offset != f->offset){ - if(work->offset != 0){ - snprint(err, sizeof err, "can't seek in directory from %lld to %lld", f->offset, work->offset); - reply(work, &thdr, err); - return; - } - if(seek(f->fid, 0, 0) != 0){ - errstr(err, sizeof err); - reply(work, &thdr, err); - return; - } - f->offset = 0; - } - r = read(f->fid, data, n); - if(r > 0) - f->offset += r; - }else - r = pread(f->fid, data, n, work->offset); - p->canint = 0; - if(r < 0) { - errstr(err, sizeof err); - reply(work, &thdr, err); - return; - } - - DEBUG(2, "\tread: fd=%d %d bytes\n", f->fid, r); - - thdr.data = data; - thdr.count = r; - stats->totread += r; - f->nread++; - f->bread += r; - reply(work, &thdr, 0); - - update(&stats->rpc[Tread], t); -} - -void -slavewrite(Fsrpc *p) -{ - char err[ERRMAX]; - Fcall *work, thdr; - Fid *f; - int n; - vlong t; - - work = &p->work; - - t = nsec(); - - f = getfid(work->fid); - if(f == 0) { - reply(work, &thdr, Ebadfid); - return; - } - - n = (work->count > Maxfdata) ? Maxfdata : work->count; - p->canint = 1; - if(p->flushtag != NOTAG) - return; - n = pwrite(f->fid, work->data, n, work->offset); - p->canint = 0; - if(n < 0) { - errstr(err, sizeof err); - reply(work, &thdr, err); - return; - } - - DEBUG(2, "\twrite: %d bytes fd=%d\n", n, f->fid); - - thdr.count = n; - f->nwrite++; - f->bwrite += n; - stats->totwrite += n; - reply(work, &thdr, 0); - - update(&stats->rpc[Twrite], t); -} - -void -reopen(Fid *f) -{ - USED(f); - fatal("reopen"); -} - -void -flushaction(void *a, char *cause) -{ - USED(a); - if(strncmp(cause, "kill", 4) == 0) - noted(NDFLT); - - noted(NCONT); -} |
