]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/iostats.c
iostats: cleanup, remove bogus Maxrpc constant
[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 [-d] [-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, rspid, dbg, n, mflag;
266         File *fs;
267         Req *r, **rr;
268
269         dbg = 0;
270         mflag = MREPL;
271         dbfile = DEBUGFILE;
272
273         ARGBEGIN{
274         case 'd':
275                 dbg++;
276                 break;
277         case 'f':
278                 dbfile = ARGF();
279                 break;
280         case 'C':
281                 mflag |= MCACHE;
282                 break;
283         default:
284                 usage();
285         }ARGEND
286
287         USED(dbfile);
288
289         if(argc == 0)
290                 usage();
291
292         if(pipe(pfd) < 0)
293                 sysfatal("pipe");
294
295         switch(cpid = fork()) {
296         case -1:
297                 sysfatal("fork");
298         case 0:
299                 close(pfd[1]);
300                 if(getwd(buf, sizeof(buf)) == 0)
301                         sysfatal("no working directory");
302
303                 rfork(RFENVG|RFNAMEG|RFNOTEG);
304
305                 if(mount(pfd[0], -1, "/", mflag, "") < 0)
306                         sysfatal("mount /");
307
308                 bind("#c/pid", "/dev/pid", MREPL);
309                 bind("#c/ppid", "/dev/ppid", MREPL);
310                 bind("#e", "/env", MREPL|MCREATE);
311                 close(0);
312                 close(1);
313                 close(2);
314                 open("/fd/0", OREAD);
315                 open("/fd/1", OWRITE);
316                 open("/fd/2", OWRITE);
317                 if(chdir(buf) < 0)
318                         sysfatal("chdir");
319                 exec(argv[0], argv);
320                 exec(smprint("/bin/%s", argv[0]), argv);
321                 sysfatal("exec: %r");
322         default:
323                 close(pfd[0]);
324         }
325
326         switch(fspid = fork()) {
327         default:
328                 while(cpid != waitpid())
329                         ;
330                 postnote(PNPROC, fspid, DONESTR);
331                 while(fspid != waitpid())
332                         ;
333                 exits(0);
334         case -1:
335                 sysfatal("fork");
336         case 0:
337                 notify(catcher);
338                 break;
339         }
340
341         if(pipe(efd) < 0)
342                 sysfatal("pipe");
343
344         /* spawn exportfs */
345         switch(fork()) {
346         default:
347                 close(efd[0]);
348                 break;
349         case -1:
350                 sysfatal("fork");
351         case 0:
352                 dup(efd[0], 0);
353                 close(efd[0]);
354                 close(efd[1]);
355                 if(dbg){
356                         execl("/bin/exportfs", "exportfs", "-df", dbfile, "-r", "/", nil);
357                 } else {
358                         execl("/bin/exportfs", "exportfs", "-r", "/", nil);
359                 }
360                 exits(0);
361         }
362
363         fmtinstall('F', fcallfmt);
364
365         stats->rpc[Tversion].name = "version";
366         stats->rpc[Tauth].name = "auth";
367         stats->rpc[Tflush].name = "flush";
368         stats->rpc[Tattach].name = "attach";
369         stats->rpc[Twalk].name = "walk";
370         stats->rpc[Topen].name = "open";
371         stats->rpc[Tcreate].name = "create";
372         stats->rpc[Tclunk].name = "clunk";
373         stats->rpc[Tread].name = "read";
374         stats->rpc[Twrite].name = "write";
375         stats->rpc[Tremove].name = "remove";
376         stats->rpc[Tstat].name = "stat";
377         stats->rpc[Twstat].name = "wstat";
378
379         for(n = 0; n < nelem(stats->rpc); n++)
380                 stats->rpc[n].lo = 10000000000LL;
381
382         switch(rspid = rfork(RFPROC|RFMEM)) {
383         case 0:
384                 /* read response from exportfs and pass to mount */
385                 while(!done){
386                         uchar tmp[sizeof(buf)];
387                         Fcall f;
388
389                         n = read(efd[1], buf, sizeof(buf));
390                         if(n < 0)
391                                 break;
392                         if(n == 0)
393                                 continue;
394
395                         /* convert response */
396                         memset(&f, 0, sizeof(f));
397                         memmove(tmp, buf, n);
398                         if(convM2S(tmp, n, &f) != n)
399                                 sysfatal("convM2S: %r");
400
401                         /* find request to this response */
402                         lock(&rqlock);
403                         for(rr = &rqhead; (r = *rr) != nil; rr = &r->next){
404                                 if(r->f.tag == f.tag){
405                                         *rr = r->next;
406                                         r->next = nil;
407                                         break;
408                                 }
409                         }
410                         stats->nproto += n;
411                         unlock(&rqlock);
412
413                         switch(f.type){
414                         case Ropen:
415                         case Rcreate:
416                                 ropen(&r->f, &f);
417                                 break;
418                         case Rclunk:
419                         case Rremove:
420                                 rclunk(&r->f);
421                                 break;
422                         case Rattach:
423                                 rattach(&r->f, &f);
424                                 break;
425                         case Rwalk:
426                                 rwalk(&r->f, &f);
427                                 break;
428                         case Rread:
429                         case Rwrite:
430                                 rio(&r->f, &f);
431                                 break;
432                         }
433
434                         rpc = &stats->rpc[r->f.type];
435                         update(rpc, r->t);
436                         rpc->bout += n;
437                         free(r);
438
439                         if(write(pfd[1], buf, n) != n)
440                                 break;
441                 }
442                 exits(0);
443         default:
444                 /* read request from mount and pass to exportfs */
445                 while(!done){
446                         n = read(pfd[1], buf, sizeof(buf));
447                         if(n < 0)
448                                 break;
449                         if(n == 0)
450                                 continue;
451
452                         r = mallocz(sizeof(*r) + n, 1);
453                         memmove(r->buf, buf, n);
454                         if(convM2S(r->buf, n, &r->f) != n)
455                                 sysfatal("convM2S: %r");
456
457                         rpc = &stats->rpc[r->f.type];
458                         rpc->count++;
459                         rpc->bin += n;
460
461                         lock(&rqlock);
462                         stats->nrpc++;
463                         stats->nproto += n;
464                         r->next = rqhead;
465                         rqhead = r;
466                         unlock(&rqlock);
467
468                         r->t = nsec();
469
470                         if(write(efd[1], buf, n) != n)
471                                 break;
472                 }
473         }
474
475         /* shutdown */
476         done = 1;
477         postnote(PNPROC, rspid, DONESTR);
478         close(pfd[1]);
479         close(efd[1]);
480
481         /* dump statistics */
482         rpc = &stats->rpc[Tread];
483         brpsec = (double)stats->totread / (((float)rpc->time/1e9)+.000001);
484
485         rpc = &stats->rpc[Twrite];
486         bwpsec = (double)stats->totwrite / (((float)rpc->time/1e9)+.000001);
487
488         ttime = 0;
489         for(n = 0; n < nelem(stats->rpc); n++) {
490                 rpc = &stats->rpc[n];
491                 if(rpc->count == 0)
492                         continue;
493                 ttime += rpc->time;
494         }
495
496         bppsec = (double)stats->nproto / ((ttime/1e9)+.000001);
497
498         fprint(2, "\nread      %llud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
499         fprint(2, "write     %llud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
500         fprint(2, "protocol  %llud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
501         fprint(2, "rpc       %lud count\n\n", stats->nrpc);
502
503         fprint(2, "%-10s %5s %5s %5s %5s %5s          T       R\n", 
504               "Message", "Count", "Low", "High", "Time", "Averg");
505
506         for(n = 0; n < nelem(stats->rpc); n++) {
507                 rpc = &stats->rpc[n];
508                 if(rpc->count == 0)
509                         continue;
510                 fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8llud %8llud bytes\n", 
511                         rpc->name, 
512                         rpc->count,
513                         rpc->lo/1000000,
514                         rpc->hi/1000000,
515                         rpc->time/1000000,
516                         rpc->time/1000000/rpc->count,
517                         rpc->bin,
518                         rpc->bout);
519         }
520
521         fprint(2, "\nOpens    Reads  (bytes)   Writes  (bytes) File\n");
522         for(fs = stats->file; fs < &stats->file[Maxfile]; fs++){
523                 if(fs->nopen == 0)
524                         break;
525                 fprint(2, "%5lud %8lud %8llud %8lud %8llud %s\n",
526                         fs->nopen,
527                         fs->nread, fs->bread,
528                         fs->nwrite, fs->bwrite,
529                         fs->path);
530         }
531
532         exits(0);
533 }