2 * iostats - Gather file system information
12 Maxfile = 1000, /* number of Files we'll log */
15 typedef struct File File;
16 typedef struct Fid Fid;
17 typedef struct Req Req;
18 typedef struct Rpc Rpc;
19 typedef struct Stats Stats;
21 /* per file statistics */
42 File *file; /* set on open/create */
56 /* per rpc statistics */
68 /* all the statistics */
91 catcher(void *a, char *msg)
95 if(strcmp(msg, DONESTR) == 0) {
99 if(strcmp(msg, "exit") == 0)
106 update(Rpc *rpc, vlong t)
125 return &fidtab[fid % nelem(fidtab)];
129 getfid(int fid, int new)
134 for(f = *ff; f != nil; f = f->next){
139 f = mallocz(sizeof(*f), 1);
148 setfid(Fid *f, char *path, Qid qid)
159 rattach(Fcall *fin, Fcall *fout)
161 setfid(getfid(fin->fid, 1), strdup("/"), fout->qid);
165 rwalk(Fcall *fin, Fcall *fout)
170 if((of = getfid(fin->fid, 0)) == nil)
172 f = getfid(fin->newfid, 1);
174 setfid(f, strdup(of->path), of->qid);
175 for(i=0; i<fout->nwqid; i++)
176 setfid(f, cleanname(smprint("%s/%s", f->path, fin->wname[i])), fout->wqid[i]);
180 ropen(Fcall *fin, Fcall *fout)
185 if((f = getfid(fin->fid, 0)) == nil)
187 if(fin->type == Tcreate)
188 setfid(f, cleanname(smprint("%s/%s", f->path, fin->name)), fout->qid);
190 setfid(f, f->path, fout->qid);
191 for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
193 fs->path = strdup(f->path);
198 if(fs->qid.path == f->qid.path && strcmp(fs->path, f->path) == 0){
212 for(ff = fidhash(fin->fid); (f = *ff) != nil; ff = &f->next){
213 if(f->fid == fin->fid){
223 rio(Fcall *fin, Fcall *fout)
229 if((f = getfid(fin->fid, 0)) == nil)
235 f->file->bread += count;
237 stats->totread += count;
242 f->file->bwrite += count;
244 stats->totwrite += count;
252 fprint(2, "usage: iostats [-dC] cmds [args ...]\n");
257 main(int argc, char **argv)
262 float brpsec, bwpsec, bppsec;
263 int cpid, fspid, expid, rspid, dbg, n, mflag;
287 sysfatal("pipe: %r");
289 /* dup std fds to be inherited to exportfs */
290 fds[0] = smprint("/fd/%d", dup(0, -1));
291 fds[1] = smprint("/fd/%d", dup(1, -1));
292 fds[2] = smprint("/fd/%d", dup(2, -1));
294 switch(cpid = fork()) {
296 sysfatal("fork: %r");
299 close(atoi(strrchr(fds[0], '/')+1));
300 close(atoi(strrchr(fds[1], '/')+1));
301 close(atoi(strrchr(fds[2], '/')+1));
303 if(getwd(buf, sizeof(buf)) == 0)
304 sysfatal("no working directory: %r");
306 rfork(RFENVG|RFNAMEG);
308 if(mount(pfd[0], -1, "/", mflag, "") == -1)
309 sysfatal("mount /: %r");
311 /* replace std fds with the exported ones */
312 close(0); open(fds[0], OREAD);
313 close(1); open(fds[1], OWRITE);
314 close(2); open(fds[2], OWRITE);
316 bind("#c/pid", "/dev/pid", MREPL);
317 bind("#c/ppid", "/dev/ppid", MREPL);
318 bind("#e", "/env", MREPL|MCREATE);
319 bind("#d", "/fd", MREPL);
322 sysfatal("chdir: %r");
325 if(**argv != '/' && strncmp(*argv, "./", 2) != 0 && strncmp(*argv, "../", 3) != 0)
326 exec(smprint("/bin/%s", *argv), argv);
327 sysfatal("exec: %r");
332 /* isolate us from interrupts */
335 sysfatal("pipe: %r");
338 switch(expid = fork()) {
341 close(atoi(strrchr(fds[0], '/')+1));
342 close(atoi(strrchr(fds[1], '/')+1));
343 close(atoi(strrchr(fds[2], '/')+1));
346 sysfatal("fork: %r");
354 execl("/bin/exportfs", "exportfs", "-d", "-r", "/", nil);
356 execl("/bin/exportfs", "exportfs", "-r", "/", nil);
358 sysfatal("exec: %r");
361 switch(fspid = fork()) {
367 while((w = wait()) != nil && (cpid != -1 || fspid != -1 || expid != -1)){
370 else if(w->pid == expid)
372 else if(w->pid == cpid){
376 postnote(PNPROC, fspid, DONESTR);
384 sysfatal("fork: %r");
390 stats->rpc[Tversion].name = "version";
391 stats->rpc[Tauth].name = "auth";
392 stats->rpc[Tflush].name = "flush";
393 stats->rpc[Tattach].name = "attach";
394 stats->rpc[Twalk].name = "walk";
395 stats->rpc[Topen].name = "open";
396 stats->rpc[Tcreate].name = "create";
397 stats->rpc[Tclunk].name = "clunk";
398 stats->rpc[Tread].name = "read";
399 stats->rpc[Twrite].name = "write";
400 stats->rpc[Tremove].name = "remove";
401 stats->rpc[Tstat].name = "stat";
402 stats->rpc[Twstat].name = "wstat";
404 for(n = 0; n < nelem(stats->rpc); n++)
405 stats->rpc[n].lo = 10000000000LL;
407 switch(rspid = rfork(RFPROC|RFMEM)) {
409 /* read response from exportfs and pass to mount */
411 uchar tmp[sizeof(buf)];
415 n = read(efd[1], buf, sizeof(buf));
421 /* convert response */
422 memset(&f, 0, sizeof(f));
423 memmove(tmp, buf, n);
424 if(convM2S(tmp, n, &f) != n)
425 sysfatal("convM2S: %r");
427 /* find request to this response */
429 for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){
430 if(r->f.tag == f.tag){
460 rpc = &stats->rpc[r->f.type];
465 if(write(pfd[1], buf, n) != n)
470 /* read request from mount and pass to exportfs */
472 n = read(pfd[1], buf, sizeof(buf));
478 r = mallocz(sizeof(*r) + n, 1);
479 memmove(r->buf, buf, n);
480 if(convM2S(r->buf, n, &r->f) != n)
481 sysfatal("convM2S: %r");
483 rpc = &stats->rpc[r->f.type];
496 if(write(efd[1], buf, n) != n)
503 postnote(PNPROC, rspid, DONESTR);
507 /* dump statistics */
508 rpc = &stats->rpc[Tread];
509 brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001);
511 rpc = &stats->rpc[Twrite];
512 bwpsec = (double)stats->totwrite / (((float)rpc->time/1e9)+.000001);
515 for(n = 0; n < nelem(stats->rpc); n++) {
516 rpc = &stats->rpc[n];
522 bppsec = (double)stats->nproto / ((ttime/1e9)+.000001);
524 fprint(2, "\nread %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
525 fprint(2, "write %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
526 fprint(2, "protocol %llud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
527 fprint(2, "rpc %lud count\n\n", stats->nrpc);
529 fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
530 "Message", "Count", "Low", "High", "Time", "Averg");
532 for(n = 0; n < nelem(stats->rpc); n++) {
533 rpc = &stats->rpc[n];
536 fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8llud %8llud bytes\n",
542 rpc->time/1000000/rpc->count,
547 fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
548 for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
552 if(strcmp(fs->path, fds[0]) == 0)
554 else if(strcmp(fs->path, fds[1]) == 0)
556 else if(strcmp(fs->path, fds[2]) == 0)
559 fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n",
561 fs->nread, fs->bread,
562 fs->nwrite, fs->bwrite,