1 /*
2 * iostats - Gather file system information
3 */
4 #include <u.h>
5 #include <libc.h>
6 #include <auth.h>
7 #include <fcall.h>
8 #define Extern
9 #include "statfs.h"
10
11 void runprog(char**);
12
13 void (*fcalls[])(Fsrpc*) =
14 {
15 [Tversion] Xversion,
16 [Tauth] Xauth,
17 [Tflush] Xflush,
18 [Tattach] Xattach,
19 [Twalk] Xwalk,
20 [Topen] slave,
21 [Tcreate] Xcreate,
22 [Tclunk] Xclunk,
23 [Tread] slave,
24 [Twrite] slave,
25 [Tremove] Xremove,
26 [Tstat] Xstat,
27 [Twstat] Xwstat,
28 };
29
30 int p[2];
31
32 void
usage(void)33 usage(void)
34 {
35 fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n");
36 exits("usage");
37 }
38
39 void
main(int argc,char ** argv)40 main(int argc, char **argv)
41 {
42 Fsrpc *r;
43 Rpc *rpc;
44 Proc *m;
45 Frec *fr;
46 Fid *fid;
47 ulong ttime;
48 char *dbfile, *s;
49 char buf[128];
50 float brpsec, bwpsec, bppsec;
51 int type, cpid, fspid, n;
52
53 dbfile = DEBUGFILE;
54
55 ARGBEGIN{
56 case 'd':
57 dbg++;
58 break;
59 case 'f':
60 dbfile = ARGF();
61 break;
62 default:
63 usage();
64 }ARGEND
65
66 if(argc == 0)
67 usage();
68
69 if(dbg) {
70 close(2);
71 create(dbfile, OWRITE, 0666);
72 }
73
74 if(pipe(p) < 0)
75 fatal("pipe");
76
77 switch(cpid = fork()) {
78 case -1:
79 fatal("fork");
80 case 0:
81 close(p[1]);
82 if(getwd(buf, sizeof(buf)) == 0)
83 fatal("no working directory");
84
85 rfork(RFENVG|RFNAMEG|RFNOTEG);
86 if(mount(p[0], -1, "/", MREPL, "") < 0)
87 fatal("mount /");
88
89 bind("#c/pid", "/dev/pid", MREPL);
90 bind("#e", "/env", MREPL|MCREATE);
91 close(0);
92 close(1);
93 close(2);
94 open("/fd/0", OREAD);
95 open("/fd/1", OWRITE);
96 open("/fd/2", OWRITE);
97
98 if(chdir(buf) < 0)
99 fatal("chdir");
100
101 runprog(argv);
102 default:
103 close(p[0]);
104 }
105
106 switch(fspid = fork()) {
107 default:
108 while(cpid != waitpid())
109 ;
110 postnote(PNPROC, fspid, DONESTR);
111 while(fspid != waitpid())
112 ;
113 exits(0);
114 case -1:
115 fatal("fork");
116 case 0:
117 break;
118 }
119
120 /* Allocate work queues in shared memory */
121 malloc(Dsegpad);
122 Workq = malloc(sizeof(Fsrpc)*Nr_workbufs);
123 stats = malloc(sizeof(Stats));
124 fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1);
125
126 if(Workq == 0 || fhash == 0 || stats == 0)
127 fatal("no initial memory");
128
129 memset(Workq, 0, sizeof(Fsrpc)*Nr_workbufs);
130 memset(stats, 0, sizeof(Stats));
131
132 stats->rpc[Tversion].name = "version";
133 stats->rpc[Tauth].name = "auth";
134 stats->rpc[Tflush].name = "flush";
135 stats->rpc[Tattach].name = "attach";
136 stats->rpc[Twalk].name = "walk";
137 stats->rpc[Topen].name = "open";
138 stats->rpc[Tcreate].name = "create";
139 stats->rpc[Tclunk].name = "clunk";
140 stats->rpc[Tread].name = "read";
141 stats->rpc[Twrite].name = "write";
142 stats->rpc[Tremove].name = "remove";
143 stats->rpc[Tstat].name = "stat";
144 stats->rpc[Twstat].name = "wstat";
145
146 for(n = 0; n < Maxrpc; n++)
147 stats->rpc[n].lo = 10000000000LL;
148
149 fmtinstall('M', dirmodefmt);
150 fmtinstall('D', dirfmt);
151 fmtinstall('F', fcallfmt);
152
153 if(chdir("/") < 0)
154 fatal("chdir");
155
156 initroot();
157
158 DEBUG(2, "statfs: %s\n", buf);
159
160 notify(catcher);
161
162 for(;;) {
163 r = getsbuf();
164 if(r == 0)
165 fatal("Out of service buffers");
166
167 n = read9pmsg(p[1], r->buf, sizeof(r->buf));
168 if(done)
169 break;
170 if(n < 0)
171 fatal("read server");
172
173 if(convM2S(r->buf, n, &r->work) == 0)
174 fatal("format error");
175
176 stats->nrpc++;
177 stats->nproto += n;
178
179 DEBUG(2, "%F\n", &r->work);
180
181 type = r->work.type;
182 rpc = &stats->rpc[type];
183 rpc->count++;
184 rpc->bin += n;
185 (fcalls[type])(r);
186 }
187
188 /* Clear away the slave children */
189 for(m = Proclist; m; m = m->next)
190 postnote(PNPROC, m->pid, "kill");
191
192 rpc = &stats->rpc[Tread];
193 brpsec = (float)stats->totread / (((float)rpc->time/1e9)+.000001);
194
195 rpc = &stats->rpc[Twrite];
196 bwpsec = (float)stats->totwrite / (((float)rpc->time/1e9)+.000001);
197
198 ttime = 0;
199 for(n = 0; n < Maxrpc; n++) {
200 rpc = &stats->rpc[n];
201 if(rpc->count == 0)
202 continue;
203 ttime += rpc->time;
204 }
205
206 bppsec = (float)stats->nproto / ((ttime/1e9)+.000001);
207
208 fprint(2, "\nread %lud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
209 fprint(2, "write %lud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
210 fprint(2, "protocol %lud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
211 fprint(2, "rpc %lud count\n\n", stats->nrpc);
212
213 fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
214 "Message", "Count", "Low", "High", "Time", "Averg");
215
216 for(n = 0; n < Maxrpc; n++) {
217 rpc = &stats->rpc[n];
218 if(rpc->count == 0)
219 continue;
220 fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8lud %8lud bytes\n",
221 rpc->name,
222 rpc->count,
223 rpc->lo/1000000,
224 rpc->hi/1000000,
225 rpc->time/1000000,
226 rpc->time/1000000/rpc->count,
227 rpc->bin,
228 rpc->bout);
229 }
230
231 for(n = 0; n < FHASHSIZE; n++)
232 for(fid = fhash[n]; fid; fid = fid->next)
233 if(fid->nread || fid->nwrite)
234 fidreport(fid);
235 if(frhead == 0)
236 exits(0);
237
238 fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
239 for(fr = frhead; fr; fr = fr->next) {
240 s = fr->op;
241 if(*s) {
242 if(strcmp(s, "/fd/0") == 0)
243 s = "(stdin)";
244 else
245 if(strcmp(s, "/fd/1") == 0)
246 s = "(stdout)";
247 else
248 if(strcmp(s, "/fd/2") == 0)
249 s = "(stderr)";
250 }
251 else
252 s = "/.";
253
254 fprint(2, "%5lud %8lud %8lud %8lud %8lud %s\n", fr->opens, fr->nread, fr->bread,
255 fr->nwrite, fr->bwrite, s);
256 }
257
258 exits(0);
259 }
260
261 void
reply(Fcall * r,Fcall * t,char * err)262 reply(Fcall *r, Fcall *t, char *err)
263 {
264 uchar data[IOHDRSZ+Maxfdata];
265 int n;
266
267 t->tag = r->tag;
268 t->fid = r->fid;
269 if(err) {
270 t->type = Rerror;
271 t->ename = err;
272 }
273 else
274 t->type = r->type + 1;
275
276 DEBUG(2, "\t%F\n", t);
277
278 n = convS2M(t, data, sizeof data);
279 if(write(p[1], data, n)!=n)
280 fatal("mount write");
281 stats->nproto += n;
282 stats->rpc[t->type-1].bout += n;
283 }
284
285 Fid *
getfid(int nr)286 getfid(int nr)
287 {
288 Fid *f;
289
290 for(f = fidhash(nr); f; f = f->next)
291 if(f->nr == nr)
292 return f;
293
294 return 0;
295 }
296
297 int
freefid(int nr)298 freefid(int nr)
299 {
300 Fid *f, **l;
301
302 l = &fidhash(nr);
303 for(f = *l; f; f = f->next) {
304 if(f->nr == nr) {
305 *l = f->next;
306 f->next = fidfree;
307 fidfree = f;
308 return 1;
309 }
310 l = &f->next;
311 }
312
313 return 0;
314 }
315
316 Fid *
newfid(int nr)317 newfid(int nr)
318 {
319 Fid *new, **l;
320 int i;
321
322 l = &fidhash(nr);
323 for(new = *l; new; new = new->next)
324 if(new->nr == nr)
325 return 0;
326
327 if(fidfree == 0) {
328 fidfree = mallocz(sizeof(Fid) * Fidchunk, 1);
329 if(fidfree == 0)
330 fatal("out of memory");
331
332 for(i = 0; i < Fidchunk-1; i++)
333 fidfree[i].next = &fidfree[i+1];
334
335 fidfree[Fidchunk-1].next = 0;
336 }
337
338 new = fidfree;
339 fidfree = new->next;
340
341 memset(new, 0, sizeof(Fid));
342 new->next = *l;
343 *l = new;
344 new->nr = nr;
345 new->fid = -1;
346 new->nread = 0;
347 new->nwrite = 0;
348 new->bread = 0;
349 new->bwrite = 0;
350
351 return new;
352 }
353
354 Fsrpc *
getsbuf(void)355 getsbuf(void)
356 {
357 static int ap;
358 int look;
359 Fsrpc *wb;
360
361 for(look = 0; look < Nr_workbufs; look++) {
362 if(++ap == Nr_workbufs)
363 ap = 0;
364 if(Workq[ap].busy == 0)
365 break;
366 }
367
368 if(look == Nr_workbufs)
369 fatal("No more work buffers");
370
371 wb = &Workq[ap];
372 wb->pid = 0;
373 wb->canint = 0;
374 wb->flushtag = NOTAG;
375 wb->busy = 1;
376
377 return wb;
378 }
379
380 char *
strcatalloc(char * p,char * n)381 strcatalloc(char *p, char *n)
382 {
383 char *v;
384
385 v = realloc(p, strlen(p)+strlen(n)+1);
386 if(v == 0)
387 fatal("no memory");
388 strcat(v, n);
389 return v;
390 }
391
392 File *
file(File * parent,char * name)393 file(File *parent, char *name)
394 {
395 char buf[128];
396 File *f, *new;
397 Dir *dir;
398
399 DEBUG(2, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
400
401 for(f = parent->child; f; f = f->childlist)
402 if(strcmp(name, f->name) == 0)
403 break;
404
405 if(f != nil && !f->inval)
406 return f;
407 makepath(buf, parent, name);
408 dir = dirstat(buf);
409 if(dir == nil)
410 return 0;
411 if(f != nil){
412 free(dir);
413 f->inval = 0;
414 return f;
415 }
416
417 new = malloc(sizeof(File));
418 if(new == 0)
419 fatal("no memory");
420
421 memset(new, 0, sizeof(File));
422 new->name = strdup(name);
423 if(new->name == nil)
424 fatal("can't strdup");
425 new->qid.type = dir->qid.type;
426 new->qid.vers = dir->qid.vers;
427 new->qid.path = ++qid;
428
429 new->parent = parent;
430 new->childlist = parent->child;
431 parent->child = new;
432
433 free(dir);
434 return new;
435 }
436
437 void
initroot(void)438 initroot(void)
439 {
440 Dir *dir;
441
442 root = malloc(sizeof(File));
443 if(root == 0)
444 fatal("no memory");
445
446 memset(root, 0, sizeof(File));
447 root->name = strdup("/");
448 if(root->name == nil)
449 fatal("can't strdup");
450 dir = dirstat(root->name);
451 if(dir == nil)
452 fatal("root stat");
453
454 root->qid.type = dir->qid.type;
455 root->qid.vers = dir->qid.vers;
456 root->qid.path = ++qid;
457 free(dir);
458 }
459
460 void
makepath(char * as,File * p,char * name)461 makepath(char *as, File *p, char *name)
462 {
463 char *c, *seg[100];
464 int i;
465 char *s;
466
467 seg[0] = name;
468 for(i = 1; i < 100 && p; i++, p = p->parent){
469 seg[i] = p->name;
470 if(strcmp(p->name, "/") == 0)
471 seg[i] = ""; /* will insert slash later */
472 }
473
474 s = as;
475 while(i--) {
476 for(c = seg[i]; *c; c++)
477 *s++ = *c;
478 *s++ = '/';
479 }
480 while(s[-1] == '/')
481 s--;
482 *s = '\0';
483 if(as == s) /* empty string is root */
484 strcpy(as, "/");
485 }
486
487 void
fatal(char * s)488 fatal(char *s)
489 {
490 Proc *m;
491
492 fprint(2, "iostats: %s: %r\n", s);
493
494 /* Clear away the slave children */
495 for(m = Proclist; m; m = m->next)
496 postnote(PNPROC, m->pid, "exit");
497
498 exits("fatal");
499 }
500
501 char*
rdenv(char * v,char ** end)502 rdenv(char *v, char **end)
503 {
504 int fd, n;
505 char *buf;
506 Dir *d;
507 if((fd = open(v, OREAD)) == -1)
508 return nil;
509 d = dirfstat(fd);
510 if(d == nil || (buf = malloc(d->length + 1)) == nil)
511 return nil;
512 n = (int)d->length;
513 n = read(fd, buf, n);
514 close(fd);
515 if(n <= 0){
516 free(buf);
517 buf = nil;
518 }else{
519 if(buf[n-1] != '\0')
520 buf[n++] = '\0';
521 *end = &buf[n];
522 }
523 free(d);
524 return buf;
525 }
526
527 char Defaultpath[] = ".\0/bin";
528 void
runprog(char * argv[])529 runprog(char *argv[])
530 {
531 char *path, *ep, *p;
532 char arg0[256];
533
534 path = rdenv("/env/path", &ep);
535 if(path == nil){
536 path = Defaultpath;
537 ep = path+sizeof(Defaultpath);
538 }
539 for(p = path; p < ep; p += strlen(p)+1){
540 snprint(arg0, sizeof arg0, "%s/%s", p, argv[0]);
541 exec(arg0, argv);
542 }
543 fatal("exec");
544 }
545
546 void
catcher(void * a,char * msg)547 catcher(void *a, char *msg)
548 {
549 USED(a);
550 if(strcmp(msg, DONESTR) == 0) {
551 done = 1;
552 noted(NCONT);
553 }
554 if(strcmp(msg, "exit") == 0)
555 exits("exit");
556
557 noted(NDFLT);
558 }
559
560 void
fidreport(Fid * f)561 fidreport(Fid *f)
562 {
563 char *p, path[128];
564 Frec *fr;
565
566 p = path;
567 makepath(p, f->f, "");
568
569 for(fr = frhead; fr; fr = fr->next) {
570 if(strcmp(fr->op, p) == 0) {
571 fr->nread += f->nread;
572 fr->nwrite += f->nwrite;
573 fr->bread += f->bread;
574 fr->bwrite += f->bwrite;
575 fr->opens++;
576 return;
577 }
578 }
579
580 fr = malloc(sizeof(Frec));
581 if(fr == 0 || (fr->op = strdup(p)) == 0)
582 fatal("no memory");
583
584 fr->nread = f->nread;
585 fr->nwrite = f->nwrite;
586 fr->bread = f->bread;
587 fr->bwrite = f->bwrite;
588 fr->opens = 1;
589 if(frhead == 0) {
590 frhead = fr;
591 frtail = fr;
592 }
593 else {
594 frtail->next = fr;
595 frtail = fr;
596 }
597 fr->next = 0;
598 }
599