]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/iostats.c
cc: fix void cast crash
[plan9front.git] / sys / src / cmd / 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
9 #define DEBUGFILE       "iostats.out"
10 #define DONESTR         "done"
11
12 enum{
13         Maxfile         = 1000, /* number of Files we'll log */
14 };
15
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;
21
22 /* per file statistics */
23 struct File
24 {
25         Qid     qid;
26         char    *path;
27
28         ulong   nopen;
29
30         ulong   nread;
31         vlong   bread;
32
33         ulong   nwrite;
34         vlong   bwrite;
35 };
36
37 /* fid context */
38 struct Fid
39 {
40         int     fid;
41         Qid     qid;
42         char    *path;
43         File    *file;  /* set on open/create */
44         Fid     *next;
45 };
46
47 /* a request */
48 struct Req
49 {
50         Req     *next;
51         vlong   t;
52
53         Fcall   f;
54         uchar   buf[];
55 };
56
57 /* per rpc statistics */
58 struct Rpc
59 {
60         char    *name;
61         ulong   count;
62         vlong   time;
63         vlong   lo;
64         vlong   hi;
65         vlong   bin;
66         vlong   bout;
67 };
68
69 /* all the statistics */
70 struct Stats
71 {
72         vlong   totread;
73         vlong   totwrite;
74         ulong   nrpc;
75         vlong   nproto;
76         Rpc     rpc[Tmax];
77         File    file[Maxfile];
78 };
79
80 Stats   stats[1];
81
82 int pfd[2];
83 int efd[2];
84 int done;
85
86 Lock rqlock;
87 Req *rqhead;
88
89 Fid *fidtab[1024];
90
91 void
92 catcher(void *a, char *msg)
93 {
94         USED(a);
95
96         if(strcmp(msg, DONESTR) == 0) {
97                 done = 1;
98                 noted(NCONT);
99         }
100         if(strcmp(msg, "exit") == 0)
101                 exits("exit");
102
103         noted(NDFLT);
104 }
105
106 void
107 update(Rpc *rpc, vlong t)
108 {
109         vlong t2;
110
111         t2 = nsec();
112         t = t2 - t;
113         if(t < 0)
114                 t = 0;
115
116         rpc->time += t;
117         if(t < rpc->lo)
118                 rpc->lo = t;
119         if(t > rpc->hi)
120                 rpc->hi = t;
121 }
122
123 Fid**
124 fidhash(int fid)
125 {
126         return &fidtab[fid % nelem(fidtab)];
127 }
128
129 Fid*
130 getfid(int fid, int new)
131 {
132         Fid *f, **ff;
133
134         ff = fidhash(fid);
135         for(f = *ff; f != nil; f = f->next){
136                 if(f->fid == fid)
137                         return f;
138         }
139         if(new){
140                 f = mallocz(sizeof(*f), 1);
141                 f->fid = fid;
142                 f->next = *ff;
143                 *ff = f;
144         }
145         return f;
146 }
147
148 void
149 setfid(Fid *f, char *path, Qid qid)
150 {
151         if(path != f->path){
152                 free(f->path);
153                 f->path = path;
154         }
155         f->qid = qid;
156         f->file = nil;
157 }
158
159 void
160 rattach(Fcall *fin, Fcall *fout)
161 {
162         setfid(getfid(fin->fid, 1), strdup("/"), fout->qid);
163 }
164
165 void
166 rwalk(Fcall *fin, Fcall *fout)
167 {
168         Fid *of, *f;
169         int i;
170
171         if((of = getfid(fin->fid, 0)) == nil)
172                 return;
173         f = getfid(fin->newfid, 1);
174         if(f != of)
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]);
178 }
179
180 void
181 ropen(Fcall *fin, Fcall *fout)
182 {
183         File *fs;
184         Fid *f;
185
186         if((f = getfid(fin->fid, 0)) == nil)
187                 return;
188         if(fin->type == Tcreate)
189                 setfid(f, cleanname(smprint("%s/%s", f->path, fin->name)), fout->qid);
190         else
191                 setfid(f, f->path, fout->qid);
192         for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
193                 if(fs->nopen == 0){
194                         fs->path = strdup(f->path);
195                         fs->qid = f->qid;
196                         f->file = fs;
197                         break;
198                 }
199                 if(fs->qid.path == f->qid.path && strcmp(fs->path, f->path) == 0){
200                         f->file = fs;
201                         break;
202                 }
203         }
204         if(f->file != nil)
205                 f->file->nopen++;
206 }
207
208 void
209 rclunk(Fcall *fin)
210 {
211         Fid **ff, *f;
212
213         for(ff = fidhash(fin->fid); (f = *ff) != nil; ff = &f->next){
214                 if(f->fid == fin->fid){
215                         *ff = f->next;
216                         free(f->path);
217                         free(f);
218                         return;
219                 }
220         }
221 }
222
223 void
224 rio(Fcall *fin, Fcall *fout)
225 {
226         Fid *f;
227         int count;
228
229         count = fout->count;
230         if((f = getfid(fin->fid, 0)) == nil)
231                 return;
232         switch(fout->type){
233         case Rread:
234                 if(f->file != nil){
235                         f->file->nread++;
236                         f->file->bread += count;
237                 }
238                 stats->totread += count;
239                 break;
240         case Rwrite:
241                 if(f->file != nil){
242                         f->file->nwrite++;
243                         f->file->bwrite += count;
244                 }
245                 stats->totwrite += count;
246                 break;
247         }
248 }
249
250 void
251 usage(void)
252 {
253         fprint(2, "usage: iostats [-dC] [-f debugfile] cmds [args ...]\n");
254         exits("usage");
255 }
256
257 void
258 main(int argc, char **argv)
259 {
260         Rpc *rpc;
261         ulong ttime;
262         char *dbfile;
263         char buf[64*1024];
264         float brpsec, bwpsec, bppsec;
265         int cpid, fspid, expid, rspid, dbg, n, mflag;
266         char *fds[3];
267         Waitmsg *w;
268         File *fs;
269         Req *r;
270
271         dbg = 0;
272         mflag = MREPL;
273         dbfile = DEBUGFILE;
274
275         ARGBEGIN{
276         case 'd':
277                 dbg++;
278                 break;
279         case 'f':
280                 dbfile = ARGF();
281                 break;
282         case 'C':
283                 mflag |= MCACHE;
284                 break;
285         default:
286                 usage();
287         }ARGEND
288
289         USED(dbfile);
290
291         if(argc == 0)
292                 usage();
293
294         if(pipe(pfd) < 0)
295                 sysfatal("pipe: %r");
296
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));
301
302         switch(cpid = fork()) {
303         case -1:
304                 sysfatal("fork: %r");
305         case 0:
306                 close(pfd[1]);
307                 close(atoi(strrchr(fds[0], '/')+1));
308                 close(atoi(strrchr(fds[1], '/')+1));
309                 close(atoi(strrchr(fds[2], '/')+1));
310
311                 if(getwd(buf, sizeof(buf)) == 0)
312                         sysfatal("no working directory: %r");
313
314                 rfork(RFENVG|RFNAMEG);
315
316                 if(mount(pfd[0], -1, "/", mflag, "") < 0)
317                         sysfatal("mount /: %r");
318
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);
323
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);
328
329                 if(chdir(buf) < 0)
330                         sysfatal("chdir: %r");
331
332                 exec(*argv, argv);
333                 if(**argv != '/' && strncmp(*argv, "./", 2) != 0 && strncmp(*argv, "../", 3) != 0)
334                         exec(smprint("/bin/%s", *argv), argv);
335                 sysfatal("exec: %r");
336         default:
337                 close(pfd[0]);
338         }
339
340         /* isolate us from interrupts */
341         rfork(RFNOTEG);
342         if(pipe(efd) < 0)
343                 sysfatal("pipe: %r");
344
345         /* spawn exportfs */
346         switch(expid = fork()) {
347         default:
348                 close(efd[0]);
349                 close(atoi(strrchr(fds[0], '/')+1));
350                 close(atoi(strrchr(fds[1], '/')+1));
351                 close(atoi(strrchr(fds[2], '/')+1));
352                 break;
353         case -1:
354                 sysfatal("fork: %r");
355         case 0:
356                 dup(efd[0], 0);
357                 close(efd[0]);
358                 close(efd[1]);
359                 close(pfd[1]);
360                 if(dbg){
361                         execl("/bin/exportfs", "exportfs", "-df", dbfile, "-r", "/", nil);
362                 } else {
363                         execl("/bin/exportfs", "exportfs", "-r", "/", nil);
364                 }
365                 sysfatal("exec: %r");
366         }
367
368         switch(fspid = fork()) {
369         default:
370                 close(pfd[1]);
371                 close(efd[1]);
372
373                 buf[0] = '\0';
374                 while((w = wait()) != nil && (cpid != -1 || fspid != -1 || expid != -1)){
375                         if(w->pid == fspid)
376                                 fspid = -1;
377                         else if(w->pid == expid)
378                                 expid = -1;
379                         else if(w->pid == cpid){
380                                 cpid = -1;
381                                 strcpy(buf, w->msg);
382                                 if(fspid != -1)
383                                         postnote(PNPROC, fspid, DONESTR);
384                         }
385                         if(buf[0] == '\0')
386                                 strcpy(buf, w->msg);
387                         free(w);
388                 }
389                 exits(buf);
390         case -1:
391                 sysfatal("fork: %r");
392         case 0:
393                 notify(catcher);
394                 break;
395         }
396
397         stats->rpc[Tversion].name = "version";
398         stats->rpc[Tauth].name = "auth";
399         stats->rpc[Tflush].name = "flush";
400         stats->rpc[Tattach].name = "attach";
401         stats->rpc[Twalk].name = "walk";
402         stats->rpc[Topen].name = "open";
403         stats->rpc[Tcreate].name = "create";
404         stats->rpc[Tclunk].name = "clunk";
405         stats->rpc[Tread].name = "read";
406         stats->rpc[Twrite].name = "write";
407         stats->rpc[Tremove].name = "remove";
408         stats->rpc[Tstat].name = "stat";
409         stats->rpc[Twstat].name = "wstat";
410
411         for(n = 0; n < nelem(stats->rpc); n++)
412                 stats->rpc[n].lo = 10000000000LL;
413
414         switch(rspid = rfork(RFPROC|RFMEM)) {
415         case 0:
416                 /* read response from exportfs and pass to mount */
417                 while(!done){
418                         uchar tmp[sizeof(buf)];
419                         Fcall f;
420                         Req **rr;
421
422                         n = read(efd[1], buf, sizeof(buf));
423                         if(n < 0)
424                                 break;
425                         if(n == 0)
426                                 continue;
427
428                         /* convert response */
429                         memset(&f, 0, sizeof(f));
430                         memmove(tmp, buf, n);
431                         if(convM2S(tmp, n, &f) != n)
432                                 sysfatal("convM2S: %r");
433
434                         /* find request to this response */
435                         lock(&rqlock);
436                         for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){
437                                 if(r->f.tag == f.tag){
438                                         *rr = r->next;
439                                         r->next = nil;
440                                         break;
441                                 }
442                         }
443                         stats->nproto += n;
444                         unlock(&rqlock);
445
446                         switch(f.type){
447                         case Ropen:
448                         case Rcreate:
449                                 ropen(&r->f, &f);
450                                 break;
451                         case Rclunk:
452                         case Rremove:
453                                 rclunk(&r->f);
454                                 break;
455                         case Rattach:
456                                 rattach(&r->f, &f);
457                                 break;
458                         case Rwalk:
459                                 rwalk(&r->f, &f);
460                                 break;
461                         case Rread:
462                         case Rwrite:
463                                 rio(&r->f, &f);
464                                 break;
465                         }
466
467                         rpc = &stats->rpc[r->f.type];
468                         update(rpc, r->t);
469                         rpc->bout += n;
470                         free(r);
471
472                         if(write(pfd[1], buf, n) != n)
473                                 break;
474                 }
475                 exits(nil);
476         default:
477                 /* read request from mount and pass to exportfs */
478                 while(!done){
479                         n = read(pfd[1], buf, sizeof(buf));
480                         if(n < 0)
481                                 break;
482                         if(n == 0)
483                                 continue;
484
485                         r = mallocz(sizeof(*r) + n, 1);
486                         memmove(r->buf, buf, n);
487                         if(convM2S(r->buf, n, &r->f) != n)
488                                 sysfatal("convM2S: %r");
489
490                         rpc = &stats->rpc[r->f.type];
491                         rpc->count++;
492                         rpc->bin += n;
493
494                         lock(&rqlock);
495                         stats->nrpc++;
496                         stats->nproto += n;
497                         r->next = rqhead;
498                         rqhead = r;
499                         unlock(&rqlock);
500
501                         r->t = nsec();
502
503                         if(write(efd[1], buf, n) != n)
504                                 break;
505                 }
506         }
507
508         /* shutdown */
509         done = 1;
510         postnote(PNPROC, rspid, DONESTR);
511         close(pfd[1]);
512         close(efd[1]);
513
514         /* dump statistics */
515         rpc = &stats->rpc[Tread];
516         brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001);
517
518         rpc = &stats->rpc[Twrite];
519         bwpsec = (double)stats->totwrite / (((float)rpc->time/1e9)+.000001);
520
521         ttime = 0;
522         for(n = 0; n < nelem(stats->rpc); n++) {
523                 rpc = &stats->rpc[n];
524                 if(rpc->count == 0)
525                         continue;
526                 ttime += rpc->time;
527         }
528
529         bppsec = (double)stats->nproto / ((ttime/1e9)+.000001);
530
531         fprint(2, "\nread      %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
532         fprint(2, "write     %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
533         fprint(2, "protocol  %llud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
534         fprint(2, "rpc       %lud count\n\n", stats->nrpc);
535
536         fprint(2, "%-10s %5s %5s %5s %5s %5s          T       R\n", 
537               "Message", "Count", "Low", "High", "Time", "Averg");
538
539         for(n = 0; n < nelem(stats->rpc); n++) {
540                 rpc = &stats->rpc[n];
541                 if(rpc->count == 0)
542                         continue;
543                 fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8llud %8llud bytes\n", 
544                         rpc->name, 
545                         rpc->count,
546                         rpc->lo/1000000,
547                         rpc->hi/1000000,
548                         rpc->time/1000000,
549                         rpc->time/1000000/rpc->count,
550                         rpc->bin,
551                         rpc->bout);
552         }
553
554         fprint(2, "\nOpens    Reads  (bytes)   Writes  (bytes) File\n");
555         for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
556                 if(fs->nopen == 0)
557                         break;
558
559                 if(strcmp(fs->path, fds[0]) == 0)
560                         fs->path = "stdin";
561                 else if(strcmp(fs->path, fds[1]) == 0)
562                         fs->path = "stdout";
563                 else if(strcmp(fs->path, fds[2]) == 0)
564                         fs->path = "stderr";
565
566                 fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n",
567                         fs->nopen,
568                         fs->nread, fs->bread,
569                         fs->nwrite, fs->bwrite,
570                         fs->path);
571         }
572
573         exits(nil);
574 }