]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/iostats/iostats.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[plan9front.git] / sys / src / cmd / iostats / iostats.c
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
33 usage(void)
34 {
35         fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n");
36         exits("usage");
37 }
38
39 void
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
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 *
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
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 *
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 *
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 *
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 *
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
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
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
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*
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
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
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
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 }