iostats: reimplement iostats as a 9p filter instead of duplicating exportfs

old iostats failed to work when builidng the kernel due to old bugs
that where already fixed in exportfs. instead of backporting the fixes,
reimplement iostats as a filter that sits between exportfs and the
process mount. from users perspective, theres no difference.

the result is much smaller and can handle everything that exportfs
can like /srv.
This commit is contained in:
cinap_lenrek 2014-08-02 02:30:19 +02:00
parent d14b6a0bf9
commit 23aaa0c59c
6 changed files with 538 additions and 1429 deletions

View file

@ -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.

534
sys/src/cmd/iostats.c Normal file
View file

@ -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);
}

View file

@ -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;
}

View file

@ -1,11 +0,0 @@
</$objtype/mkfile
TARG=iostats
OFILES=iostats.$O\
statsrv.$O\
HFILES=statfs.h\
BIN=/$objtype/bin
</sys/src/cmd/mkone

View file

@ -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*);

View file

@ -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);
}