2 * iostats - Gather file system information
9 #define DEBUGFILE "iostats.out"
10 #define DONESTR "done"
13 Maxfile = 1000, /* number of Files we'll log */
16 typedef struct File File;
17 typedef struct Fid Fid;
18 typedef struct Req Req;
19 typedef struct Rpc Rpc;
20 typedef struct Stats Stats;
22 /* per file statistics */
43 File *file; /* set on open/create */
57 /* per rpc statistics */
69 /* all the statistics */
92 catcher(void *a, char *msg)
96 if(strcmp(msg, DONESTR) == 0) {
100 if(strcmp(msg, "exit") == 0)
107 update(Rpc *rpc, vlong t)
126 return &fidtab[fid % nelem(fidtab)];
130 getfid(int fid, int new)
135 for(f = *ff; f != nil; f = f->next){
140 f = mallocz(sizeof(*f), 1);
149 setfid(Fid *f, char *path, Qid qid)
160 rattach(Fcall *fin, Fcall *fout)
162 setfid(getfid(fin->fid, 1), strdup("/"), fout->qid);
166 rwalk(Fcall *fin, Fcall *fout)
171 if((of = getfid(fin->fid, 0)) == nil)
173 f = getfid(fin->newfid, 1);
175 setfid(f, strdup(of->path), of->qid);
176 for(i=0; i<fout->nwqid; i++)
177 setfid(f, cleanname(smprint("%s/%s", f->path, fin->wname[i])), fout->wqid[i]);
181 ropen(Fcall *fin, Fcall *fout)
186 if((f = getfid(fin->fid, 0)) == nil)
188 if(fin->type == Tcreate)
189 setfid(f, cleanname(smprint("%s/%s", f->path, fin->name)), fout->qid);
191 setfid(f, f->path, fout->qid);
192 for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
194 fs->path = strdup(f->path);
199 if(fs->qid.path == f->qid.path && strcmp(fs->path, f->path) == 0){
213 for(ff = fidhash(fin->fid); (f = *ff) != nil; ff = &f->next){
214 if(f->fid == fin->fid){
224 rio(Fcall *fin, Fcall *fout)
230 if((f = getfid(fin->fid, 0)) == nil)
236 f->file->bread += count;
238 stats->totread += count;
243 f->file->bwrite += count;
245 stats->totwrite += count;
253 fprint(2, "usage: iostats [-dC] [-f debugfile] cmds [args ...]\n");
258 main(int argc, char **argv)
264 float brpsec, bwpsec, bppsec;
265 int cpid, fspid, expid, rspid, dbg, n, mflag;
295 sysfatal("pipe: %r");
297 /* dup std fds to be inherited to exportfs */
298 fds[0] = smprint("/fd/%d", dup(0, -1));
299 fds[1] = smprint("/fd/%d", dup(1, -1));
300 fds[2] = smprint("/fd/%d", dup(2, -1));
302 switch(cpid = fork()) {
304 sysfatal("fork: %r");
307 close(atoi(strrchr(fds[0], '/')+1));
308 close(atoi(strrchr(fds[1], '/')+1));
309 close(atoi(strrchr(fds[2], '/')+1));
311 if(getwd(buf, sizeof(buf)) == 0)
312 sysfatal("no working directory: %r");
314 rfork(RFENVG|RFNAMEG);
316 if(mount(pfd[0], -1, "/", mflag, "") == -1)
317 sysfatal("mount /: %r");
319 /* replace std fds with the exported ones */
320 close(0); open(fds[0], OREAD);
321 close(1); open(fds[1], OWRITE);
322 close(2); open(fds[2], OWRITE);
324 bind("#c/pid", "/dev/pid", MREPL);
325 bind("#c/ppid", "/dev/ppid", MREPL);
326 bind("#e", "/env", MREPL|MCREATE);
327 bind("#d", "/fd", MREPL);
330 sysfatal("chdir: %r");
333 if(**argv != '/' && strncmp(*argv, "./", 2) != 0 && strncmp(*argv, "../", 3) != 0)
334 exec(smprint("/bin/%s", *argv), argv);
335 sysfatal("exec: %r");
340 /* isolate us from interrupts */
343 sysfatal("pipe: %r");
346 switch(expid = fork()) {
349 close(atoi(strrchr(fds[0], '/')+1));
350 close(atoi(strrchr(fds[1], '/')+1));
351 close(atoi(strrchr(fds[2], '/')+1));
354 sysfatal("fork: %r");
362 execl("/bin/exportfs", "exportfs", "-df", dbfile, "-r", "/", nil);
364 execl("/bin/exportfs", "exportfs", "-r", "/", nil);
366 sysfatal("exec: %r");
369 switch(fspid = fork()) {
375 while((w = wait()) != nil && (cpid != -1 || fspid != -1 || expid != -1)){
378 else if(w->pid == expid)
380 else if(w->pid == cpid){
384 postnote(PNPROC, fspid, DONESTR);
392 sysfatal("fork: %r");
398 stats->rpc[Tversion].name = "version";
399 stats->rpc[Tauth].name = "auth";
400 stats->rpc[Tflush].name = "flush";
401 stats->rpc[Tattach].name = "attach";
402 stats->rpc[Twalk].name = "walk";
403 stats->rpc[Topen].name = "open";
404 stats->rpc[Tcreate].name = "create";
405 stats->rpc[Tclunk].name = "clunk";
406 stats->rpc[Tread].name = "read";
407 stats->rpc[Twrite].name = "write";
408 stats->rpc[Tremove].name = "remove";
409 stats->rpc[Tstat].name = "stat";
410 stats->rpc[Twstat].name = "wstat";
412 for(n = 0; n < nelem(stats->rpc); n++)
413 stats->rpc[n].lo = 10000000000LL;
415 switch(rspid = rfork(RFPROC|RFMEM)) {
417 /* read response from exportfs and pass to mount */
419 uchar tmp[sizeof(buf)];
423 n = read(efd[1], buf, sizeof(buf));
429 /* convert response */
430 memset(&f, 0, sizeof(f));
431 memmove(tmp, buf, n);
432 if(convM2S(tmp, n, &f) != n)
433 sysfatal("convM2S: %r");
435 /* find request to this response */
437 for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){
438 if(r->f.tag == f.tag){
468 rpc = &stats->rpc[r->f.type];
473 if(write(pfd[1], buf, n) != n)
478 /* read request from mount and pass to exportfs */
480 n = read(pfd[1], buf, sizeof(buf));
486 r = mallocz(sizeof(*r) + n, 1);
487 memmove(r->buf, buf, n);
488 if(convM2S(r->buf, n, &r->f) != n)
489 sysfatal("convM2S: %r");
491 rpc = &stats->rpc[r->f.type];
504 if(write(efd[1], buf, n) != n)
511 postnote(PNPROC, rspid, DONESTR);
515 /* dump statistics */
516 rpc = &stats->rpc[Tread];
517 brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001);
519 rpc = &stats->rpc[Twrite];
520 bwpsec = (double)stats->totwrite / (((float)rpc->time/1e9)+.000001);
523 for(n = 0; n < nelem(stats->rpc); n++) {
524 rpc = &stats->rpc[n];
530 bppsec = (double)stats->nproto / ((ttime/1e9)+.000001);
532 fprint(2, "\nread %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
533 fprint(2, "write %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
534 fprint(2, "protocol %llud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
535 fprint(2, "rpc %lud count\n\n", stats->nrpc);
537 fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
538 "Message", "Count", "Low", "High", "Time", "Averg");
540 for(n = 0; n < nelem(stats->rpc); n++) {
541 rpc = &stats->rpc[n];
544 fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8llud %8llud bytes\n",
550 rpc->time/1000000/rpc->count,
555 fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
556 for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
560 if(strcmp(fs->path, fds[0]) == 0)
562 else if(strcmp(fs->path, fds[1]) == 0)
564 else if(strcmp(fs->path, fds[2]) == 0)
567 fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n",
569 fs->nread, fs->bread,
570 fs->nwrite, fs->bwrite,