]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/stats.c
merge
[plan9front.git] / sys / src / cmd / stats.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <draw.h>
7 #include <event.h>
8
9 #define MAXNUM  10      /* maximum number of numbers on data line */
10
11 typedef struct Graph    Graph;
12 typedef struct Machine  Machine;
13
14 struct Graph
15 {
16         int             colindex;
17         Rectangle       r;
18         int             *data;
19         int             ndata;
20         char            *label;
21         void            (*newvalue)(Machine*, uvlong*, uvlong*, int);
22         void            (*update)(Graph*, uvlong, uvlong);
23         Machine         *mach;
24         int             overflow;
25         Image           *overtmp;
26 };
27
28 enum
29 {
30         /* old /dev/swap */
31         Mem             = 0,
32         Maxmem,
33         Swap,
34         Maxswap,
35
36         /* /dev/sysstats */
37         Procno  = 0,
38         Context,
39         Interrupt,
40         Syscall,
41         Fault,
42         TLBfault,
43         TLBpurge,
44         Load,
45         Idle,
46         InIntr,
47         /* /net/ether0/stats */
48         In              = 0,
49         Link,
50         Out,
51         Err0,
52 };
53
54 struct Machine
55 {
56         char            *name;
57         char            *shortname;
58         int             remote;
59         int             statsfd;
60         int             swapfd;
61         int             etherfd;
62         int             ifstatsfd;
63         int             batteryfd;
64         int             bitsybatfd;
65         int             tempfd;
66         int             disable;
67
68         uvlong          devswap[4];
69         uvlong          devsysstat[10];
70         uvlong          prevsysstat[10];
71         int             nproc;
72         int             lgproc;
73         uvlong          netetherstats[8];
74         uvlong          prevetherstats[8];
75         uvlong          batterystats[2];
76         uvlong          netetherifstats[2];
77         uvlong          temp[10];
78
79         /* big enough to hold /dev/sysstat even with many processors */
80         char            buf[8*1024];
81         char            *bufp;
82         char            *ebufp;
83 };
84
85 enum
86 {
87         Mainproc,
88         Mouseproc,
89         NPROC,
90 };
91
92 enum
93 {
94         Ncolor          = 6,
95         Ysqueeze        = 2,    /* vertical squeezing of label text */
96         Labspace        = 2,    /* room around label */
97         Dot             = 2,    /* height of dot */
98         Opwid           = 5,    /* strlen("add  ") or strlen("drop ") */
99         Nlab            = 3,    /* max number of labels on y axis */
100         Lablen          = 16,   /* max length of label */
101         Lx              = 4,    /* label tick length */
102 };
103
104 enum Menu2
105 {
106         Mbattery,
107         Mcontext,
108         Mether,
109         Methererr,
110         Metherin,
111         Metherout,
112         Mfault,
113         Midle,
114         Minintr,
115         Mintr,
116         Mload,
117         Mmem,
118         Mswap,
119         Msyscall,
120         Mtlbmiss,
121         Mtlbpurge,
122         Msignal,
123         Mtemp,
124         Nmenu2,
125 };
126
127 char    *menu2str[Nmenu2+1] = {
128         "add  battery ",
129         "add  context ",
130         "add  ether   ",
131         "add  ethererr",
132         "add  etherin ",
133         "add  etherout",
134         "add  fault   ",
135         "add  idle    ",
136         "add  inintr  ",
137         "add  intr    ",
138         "add  load    ",
139         "add  mem     ",
140         "add  swap    ",
141         "add  syscall ",
142         "add  tlbmiss ",
143         "add  tlbpurge",
144         "add  802.11b ",
145         "add  temp    ",
146         nil,
147 };
148
149
150 void    contextval(Machine*, uvlong*, uvlong*, int),
151         etherval(Machine*, uvlong*, uvlong*, int),
152         ethererrval(Machine*, uvlong*, uvlong*, int),
153         etherinval(Machine*, uvlong*, uvlong*, int),
154         etheroutval(Machine*, uvlong*, uvlong*, int),
155         faultval(Machine*, uvlong*, uvlong*, int),
156         intrval(Machine*, uvlong*, uvlong*, int),
157         inintrval(Machine*, uvlong*, uvlong*, int),
158         loadval(Machine*, uvlong*, uvlong*, int),
159         idleval(Machine*, uvlong*, uvlong*, int),
160         memval(Machine*, uvlong*, uvlong*, int),
161         swapval(Machine*, uvlong*, uvlong*, int),
162         syscallval(Machine*, uvlong*, uvlong*, int),
163         tlbmissval(Machine*, uvlong*, uvlong*, int),
164         tlbpurgeval(Machine*, uvlong*, uvlong*, int),
165         batteryval(Machine*, uvlong*, uvlong*, int),
166         signalval(Machine*, uvlong*, uvlong*, int),
167         tempval(Machine*, uvlong*, uvlong*, int);
168
169 Menu    menu2 = {menu2str, nil};
170 int     present[Nmenu2];
171 void    (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
172         batteryval,
173         contextval,
174         etherval,
175         ethererrval,
176         etherinval,
177         etheroutval,
178         faultval,
179         idleval,
180         inintrval,
181         intrval,
182         loadval,
183         memval,
184         swapval,
185         syscallval,
186         tlbmissval,
187         tlbpurgeval,
188         signalval,
189         tempval,
190 };
191
192 Image   *cols[Ncolor][3];
193 Graph   *graph;
194 Machine *mach;
195 Font    *mediumfont;
196 char    *mysysname;
197 char    argchars[] = "8bceEfiImlnpstwz";
198 int     pids[NPROC];
199 int     parity; /* toggled to avoid patterns in textured background */
200 int     nmach;
201 int     ngraph; /* totaly number is ngraph*nmach */
202 double  scale = 1.0;
203 int     logscale = 0;
204 int     ylabels = 0;
205 int     oldsystem = 0;
206 int     sleeptime = 1000;
207
208 char    *procnames[NPROC] = {"main", "mouse"};
209
210 void
211 killall(char *s)
212 {
213         int i, pid;
214
215         pid = getpid();
216         for(i=0; i<NPROC; i++)
217                 if(pids[i] && pids[i]!=pid)
218                         postnote(PNPROC, pids[i], "kill");
219         exits(s);
220 }
221
222 void*
223 emalloc(ulong sz)
224 {
225         void *v;
226         v = malloc(sz);
227         if(v == nil) {
228                 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
229                 killall("mem");
230         }
231         memset(v, 0, sz);
232         return v;
233 }
234
235 void*
236 erealloc(void *v, ulong sz)
237 {
238         v = realloc(v, sz);
239         if(v == nil) {
240                 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
241                 killall("mem");
242         }
243         return v;
244 }
245
246 char*
247 estrdup(char *s)
248 {
249         char *t;
250         if((t = strdup(s)) == nil) {
251                 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
252                 killall("mem");
253         }
254         return t;
255 }
256
257 void
258 mkcol(int i, int c0, int c1, int c2)
259 {
260         cols[i][0] = allocimagemix(display, c0, DWhite);
261         cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
262         cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
263 }
264
265 void
266 colinit(void)
267 {
268         mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
269         if(mediumfont == nil)
270                 mediumfont = font;
271
272         /* Peach */
273         mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
274         /* Aqua */
275         mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
276         /* Yellow */
277         mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
278         /* Green */
279         mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
280         /* Blue */
281         mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
282         /* Grey */
283         cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
284         cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
285         cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
286 }
287
288 int
289 loadbuf(Machine *m, int *fd)
290 {
291         int n;
292
293
294         if(*fd < 0)
295                 return 0;
296         seek(*fd, 0, 0);
297         n = read(*fd, m->buf, sizeof m->buf-1);
298         if(n <= 0){
299                 close(*fd);
300                 *fd = -1;
301                 return 0;
302         }
303         m->bufp = m->buf;
304         m->ebufp = m->buf+n;
305         m->buf[n] = 0;
306         return 1;
307 }
308
309 void
310 label(Point p, int dy, char *text)
311 {
312         char *s;
313         Rune r[2];
314         int w, maxw, maxy;
315
316         p.x += Labspace;
317         maxy = p.y+dy;
318         maxw = 0;
319         r[1] = '\0';
320         for(s=text; *s; ){
321                 if(p.y+mediumfont->height-Ysqueeze > maxy)
322                         break;
323                 w = chartorune(r, s);
324                 s += w;
325                 w = runestringwidth(mediumfont, r);
326                 if(w > maxw)
327                         maxw = w;
328                 runestring(screen, p, display->black, ZP, mediumfont, r);
329                 p.y += mediumfont->height-Ysqueeze;
330         }
331 }
332
333 Point
334 paritypt(int x)
335 {
336         return Pt(x+parity, 0);
337 }
338
339 Point
340 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
341 {
342         Point p;
343         double y;
344
345         p.x = x;
346         y = ((double)v)/(vmax*scale);
347         if(logscale){
348                 /*
349                  * Arrange scale to cover a factor of 1000.
350                  * vmax corresponds to the 100 mark.
351                  * 10*vmax is the top of the scale.
352                  */
353                 if(y <= 0.)
354                         y = 0;
355                 else{
356                         y = log10(y);
357                         /* 1 now corresponds to the top; -2 to the bottom; rescale */
358                         y = (y+2.)/3.;
359                 }
360         }
361         p.y = g->r.max.y - Dy(g->r)*y - Dot;
362         if(p.y < g->r.min.y)
363                 p.y = g->r.min.y;
364         if(p.y > g->r.max.y-Dot)
365                 p.y = g->r.max.y-Dot;
366         return p;
367 }
368
369 void
370 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
371 {
372         int c;
373         Point p, q;
374
375         c = g->colindex;
376         p = datapoint(g, x, v, vmax);
377         q = datapoint(g, x, prev, vmax);
378         if(p.y < q.y){
379                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
380                 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
381                 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
382         }else{
383                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
384                 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
385                 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
386         }
387
388 }
389
390 void
391 redraw(Graph *g, uvlong vmax)
392 {
393         int i, c;
394
395         c = g->colindex;
396         draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
397         for(i=1; i<Dx(g->r); i++)
398                 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
399         drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
400         g->overflow = 0;
401 }
402
403 void
404 update1(Graph *g, uvlong v, uvlong vmax)
405 {
406         char buf[48];
407         int overflow;
408
409         if(g->overflow && g->overtmp!=nil)
410                 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
411         draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
412         drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
413         memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
414         g->data[0] = v;
415         g->overflow = 0;
416         if(logscale)
417                 overflow = (v>10*vmax*scale);
418         else
419                 overflow = (v>vmax*scale);
420         if(overflow && g->overtmp!=nil){
421                 g->overflow = 1;
422                 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
423                 sprint(buf, "%llud", v);
424                 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
425         }
426 }
427
428 /* read one line of text from buffer and process integers */
429 int
430 readnums(Machine *m, int n, uvlong *a, int spanlines)
431 {
432         int i;
433         char *p, *ep;
434
435         if(spanlines)
436                 ep = m->ebufp;
437         else
438                 for(ep=m->bufp; ep<m->ebufp; ep++)
439                         if(*ep == '\n')
440                                 break;
441         p = m->bufp;
442         for(i=0; i<n && p<ep; i++){
443                 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
444                         p++;
445                 if(p == ep)
446                         break;
447                 a[i] = strtoull(p, &p, 10);
448         }
449         if(ep < m->ebufp)
450                 ep++;
451         m->bufp = ep;
452         return i == n;
453 }
454
455 /* Network on fd1, mount driver on fd0 */
456 static int
457 filter(int fd)
458 {
459         int p[2];
460
461         if(pipe(p) < 0){
462                 fprint(2, "stats: can't pipe: %r\n");
463                 killall("pipe");
464         }
465
466         switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
467         case -1:
468                 sysfatal("rfork record module");
469         case 0:
470                 dup(fd, 1);
471                 close(fd);
472                 dup(p[0], 0);
473                 close(p[0]);
474                 close(p[1]);
475                 execl("/bin/aux/fcall", "fcall", nil);
476                 fprint(2, "stats: can't exec fcall: %r\n");
477                 killall("fcall");
478         default:
479                 close(fd);
480                 close(p[0]);
481         }
482         return p[1];
483 }
484
485 /*
486  * 9fs
487  */
488 int
489 connect9fs(char *addr)
490 {
491         char dir[256], *na;
492         int fd;
493
494         fprint(2, "connect9fs...");
495         na = netmkaddr(addr, 0, "9fs");
496
497         fprint(2, "dial %s...", na);
498         if((fd = dial(na, 0, dir, 0)) < 0)
499                 return -1;
500
501         fprint(2, "dir %s...", dir);
502 //      if(strstr(dir, "tcp"))
503 //              fd = filter(fd);
504         return fd;
505 }
506
507 int
508 old9p(int fd)
509 {
510         int p[2];
511
512         if(pipe(p) < 0)
513                 return -1;
514
515         switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
516         case -1:
517                 return -1;
518         case 0:
519                 if(fd != 1){
520                         dup(fd, 1);
521                         close(fd);
522                 }
523                 if(p[0] != 0){
524                         dup(p[0], 0);
525                         close(p[0]);
526                 }
527                 close(p[1]);
528                 if(0){
529                         fd = open("/sys/log/cpu", OWRITE);
530                         if(fd != 2){
531                                 dup(fd, 2);
532                                 close(fd);
533                         }
534                         execl("/bin/srvold9p", "srvold9p", "-ds", nil);
535                 } else
536                         execl("/bin/srvold9p", "srvold9p", "-s", nil);
537                 return -1;
538         default:
539                 close(fd);
540                 close(p[0]);
541         }
542         return p[1];
543 }
544
545
546 /*
547  * exportfs
548  */
549 int
550 connectexportfs(char *addr)
551 {
552         char buf[ERRMAX], dir[256], *na;
553         int fd, n;
554         char *tree;
555         AuthInfo *ai;
556
557         tree = "/";
558         na = netmkaddr(addr, 0, "exportfs");
559         if((fd = dial(na, 0, dir, 0)) < 0)
560                 return -1;
561
562         ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client");
563         if(ai == nil)
564                 return -1;
565
566         n = write(fd, tree, strlen(tree));
567         if(n < 0){
568                 close(fd);
569                 return -1;
570         }
571
572         strcpy(buf, "can't read tree");
573         n = read(fd, buf, sizeof buf - 1);
574         if(n!=2 || buf[0]!='O' || buf[1]!='K'){
575                 buf[sizeof buf - 1] = '\0';
576                 werrstr("bad remote tree: %s\n", buf);
577                 close(fd);
578                 return -1;
579         }
580
581 //      if(strstr(dir, "tcp"))
582 //              fd = filter(fd);
583
584         if(oldsystem)
585                 return old9p(fd);
586
587         return fd;
588 }
589
590 int
591 readswap(Machine *m, uvlong *a)
592 {
593         if(strstr(m->buf, "memory\n")){
594                 /* new /dev/swap - skip first 3 numbers */
595                 if(!readnums(m, 7, a, 1))
596                         return 0;
597                 a[0] = a[3];
598                 a[1] = a[4];
599                 a[2] = a[5];
600                 a[3] = a[6];
601                 return 1;
602         }
603         return readnums(m, nelem(m->devswap), a, 0);
604 }
605
606 char*
607 shortname(char *s)
608 {
609         char *p, *e;
610
611         p = estrdup(s);
612         e = strchr(p, '.');
613         if(e)
614                 *e = 0;
615         return p;
616 }
617
618 int
619 ilog10(uvlong j)
620 {
621         int i;
622
623         for(i = 0; j >= 10; i++)
624                 j /= 10;
625         return i;
626 }
627
628 int
629 initmach(Machine *m, char *name)
630 {
631         int n, fd;
632         uvlong a[MAXNUM];
633         char *p, mpt[256], buf[256];
634
635         p = strchr(name, '!');
636         if(p)
637                 p++;
638         else
639                 p = name;
640         m->name = estrdup(p);
641         m->shortname = shortname(p);
642         m->remote = (strcmp(p, mysysname) != 0);
643         if(m->remote == 0)
644                 strcpy(mpt, "");
645         else{
646                 snprint(mpt, sizeof mpt, "/n/%s", p);
647                 fd = connectexportfs(name);
648                 if(fd < 0){
649                         fprint(2, "can't connect to %s: %r\n", name);
650                         return 0;
651                 }
652                 /* BUG? need to use amount() now? */
653                 if(mount(fd, -1, mpt, MREPL, "") < 0){
654                         fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt);
655                         strcpy(mpt, "/n/sid");
656                         if(mount(fd, -1, mpt, MREPL, "") < 0){
657                                 fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt);
658                                 return 0;
659                         }
660                 }
661         }
662
663         snprint(buf, sizeof buf, "%s/dev/swap", mpt);
664         m->swapfd = open(buf, OREAD);
665         if(loadbuf(m, &m->swapfd) && readswap(m, a))
666                 memmove(m->devswap, a, sizeof m->devswap);
667         else{
668                 m->devswap[Maxswap] = 100;
669                 m->devswap[Maxmem] = 100;
670         }
671
672         snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
673         m->statsfd = open(buf, OREAD);
674         if(loadbuf(m, &m->statsfd)){
675                 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
676                         ;
677                 m->nproc = n;
678         }else
679                 m->nproc = 1;
680         m->lgproc = ilog10(m->nproc);
681
682         snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
683         m->etherfd = open(buf, OREAD);
684         if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
685                 memmove(m->netetherstats, a, sizeof m->netetherstats);
686
687         snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
688         m->ifstatsfd = open(buf, OREAD);
689         if(loadbuf(m, &m->ifstatsfd)){
690                 /* need to check that this is a wavelan interface */
691                 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
692                         memmove(m->netetherifstats, a, sizeof m->netetherifstats);
693         }
694
695         snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
696         m->batteryfd = open(buf, OREAD);
697         m->bitsybatfd = -1;
698         if(m->batteryfd >= 0){
699                 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
700                         memmove(m->batterystats, a, sizeof(m->batterystats));
701         }else{
702                 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
703                 m->bitsybatfd = open(buf, OREAD);
704                 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
705                         memmove(m->batterystats, a, sizeof(m->batterystats));
706         }
707         snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
708         m->tempfd = open(buf, OREAD);
709         if(loadbuf(m, &m->tempfd))
710                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
711                          m->temp[n] = a[0];
712         return 1;
713 }
714
715 jmp_buf catchalarm;
716
717 void
718 alarmed(void *a, char *s)
719 {
720         if(strcmp(s, "alarm") == 0)
721                 notejmp(a, catchalarm, 1);
722         noted(NDFLT);
723 }
724
725 int
726 needswap(int init)
727 {
728         return init | present[Mmem] | present[Mswap];
729 }
730
731
732 int
733 needstat(int init)
734 {
735         return init | present[Mcontext]  | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
736                 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
737 }
738
739
740 int
741 needether(int init)
742 {
743         return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
744 }
745
746 int
747 needbattery(int init)
748 {
749         return init | present[Mbattery];
750 }
751
752 int
753 needsignal(int init)
754 {
755         return init | present[Msignal];
756 }
757
758 int
759 needtemp(int init)
760 {
761         return init | present[Mtemp];
762 }
763
764 void
765 readmach(Machine *m, int init)
766 {
767         int n, i;
768         uvlong a[nelem(m->devsysstat)];
769         char buf[32];
770
771         if(m->remote && (m->disable || setjmp(catchalarm))){
772                 if (m->disable++ >= 5)
773                         m->disable = 0; /* give it another chance */
774                 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
775                 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
776                 return;
777         }
778         snprint(buf, sizeof buf, "%s", m->name);
779         if (strcmp(m->name, buf) != 0){
780                 free(m->name);
781                 m->name = estrdup(buf);
782                 free(m->shortname);
783                 m->shortname = shortname(buf);
784                 if(display != nil)      /* else we're still initializing */
785                         eresized(0);
786         }
787         if(m->remote){
788                 notify(alarmed);
789                 alarm(5000);
790         }
791         if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
792                 memmove(m->devswap, a, sizeof m->devswap);
793         if(needstat(init) && loadbuf(m, &m->statsfd)){
794                 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
795                 memset(m->devsysstat, 0, sizeof m->devsysstat);
796                 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
797                         for(i=0; i<nelem(m->devsysstat); i++)
798                                 m->devsysstat[i] += a[i];
799         }
800         if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
801                 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
802                 memmove(m->netetherstats, a, sizeof m->netetherstats);
803         }
804         if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
805                 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
806         }
807         if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
808                 memmove(m->batterystats, a, sizeof(m->batterystats));
809         if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
810                 memmove(m->batterystats, a, sizeof(m->batterystats));
811         if(needtemp(init) && loadbuf(m, &m->tempfd))
812                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
813                          m->temp[n] = a[0];
814         if(m->remote){
815                 alarm(0);
816                 notify(nil);
817         }
818 }
819
820 void
821 memval(Machine *m, uvlong *v, uvlong *vmax, int)
822 {
823         *v = m->devswap[Mem];
824         *vmax = m->devswap[Maxmem];
825 }
826
827 void
828 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
829 {
830         *v = m->devswap[Swap];
831         *vmax = m->devswap[Maxswap];
832 }
833
834 void
835 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
836 {
837         *v = m->devsysstat[Context]-m->prevsysstat[Context];
838         *vmax = sleeptime*m->nproc;
839         if(init)
840                 *vmax = sleeptime;
841 }
842
843 /*
844  * bug: need to factor in HZ
845  */
846 void
847 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
848 {
849         *v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt];
850         *vmax = sleeptime*m->nproc*10;
851         if(init)
852                 *vmax = sleeptime*10;
853 }
854
855 void
856 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
857 {
858         *v = m->devsysstat[Syscall]-m->prevsysstat[Syscall];
859         *vmax = sleeptime*m->nproc;
860         if(init)
861                 *vmax = sleeptime;
862 }
863
864 void
865 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
866 {
867         *v = m->devsysstat[Fault]-m->prevsysstat[Fault];
868         *vmax = sleeptime*m->nproc;
869         if(init)
870                 *vmax = sleeptime;
871 }
872
873 void
874 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
875 {
876         *v = m->devsysstat[TLBfault]-m->prevsysstat[TLBfault];
877         *vmax = (sleeptime/1000)*10*m->nproc;
878         if(init)
879                 *vmax = (sleeptime/1000)*10;
880 }
881
882 void
883 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
884 {
885         *v = m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge];
886         *vmax = (sleeptime/1000)*10*m->nproc;
887         if(init)
888                 *vmax = (sleeptime/1000)*10;
889 }
890
891 void
892 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
893 {
894         *v = m->devsysstat[Load];
895         *vmax = 1000*m->nproc;
896         if(init)
897                 *vmax = 1000;
898 }
899
900 void
901 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
902 {
903         *v = m->devsysstat[Idle]/m->nproc;
904         *vmax = 100;
905 }
906
907 void
908 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
909 {
910         *v = m->devsysstat[InIntr]/m->nproc;
911         *vmax = 100;
912 }
913
914 void
915 etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
916 {
917         *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
918         *vmax = sleeptime*m->nproc;
919         if(init)
920                 *vmax = sleeptime;
921 }
922
923 void
924 etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
925 {
926         *v = m->netetherstats[In]-m->prevetherstats[In];
927         *vmax = sleeptime*m->nproc;
928         if(init)
929                 *vmax = sleeptime;
930 }
931
932 void
933 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
934 {
935         *v = m->netetherstats[Out]-m->prevetherstats[Out];
936         *vmax = sleeptime*m->nproc;
937         if(init)
938                 *vmax = sleeptime;
939 }
940
941 void
942 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
943 {
944         int i;
945
946         *v = 0;
947         for(i=Err0; i<nelem(m->netetherstats); i++)
948                 *v += m->netetherstats[i];
949         *vmax = (sleeptime/1000)*10*m->nproc;
950         if(init)
951                 *vmax = (sleeptime/1000)*10;
952 }
953
954 void
955 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
956 {
957         *v = m->batterystats[0];
958         if(m->bitsybatfd >= 0)
959                 *vmax = 184;            // at least on my bitsy...
960         else
961                 *vmax = 100;
962 }
963
964 void
965 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
966 {
967         ulong l;
968
969         *vmax = sleeptime;
970         l = m->netetherifstats[0];
971         /*
972          * Range is seen to be from about -45 (strong) to -95 (weak); rescale
973          */
974         if(l == 0){     /* probably not present */
975                 *v = 0;
976                 return;
977         }
978         *v = 20*(l+95);
979 }
980
981 void
982 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
983 {
984         ulong l;
985
986         *vmax = sleeptime;
987         l = m->temp[0];
988         if(l == ~0 || l == 0)
989                 *v = 0;
990         else
991                 *v = (l-20)*27;
992 }
993
994 void
995 usage(void)
996 {
997         fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
998         exits("usage");
999 }
1000
1001 void
1002 addgraph(int n)
1003 {
1004         Graph *g, *ograph;
1005         int i, j;
1006         static int nadd;
1007
1008         if(n > nelem(menu2str))
1009                 abort();
1010         /* avoid two adjacent graphs of same color */
1011         if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
1012                 nadd++;
1013         ograph = graph;
1014         graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
1015         for(i=0; i<nmach; i++)
1016                 for(j=0; j<ngraph; j++)
1017                         graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
1018         free(ograph);
1019         ngraph++;
1020         for(i=0; i<nmach; i++){
1021                 g = &graph[i*ngraph+(ngraph-1)];
1022                 memset(g, 0, sizeof(Graph));
1023                 g->label = menu2str[n]+Opwid;
1024                 g->newvalue = newvaluefn[n];
1025                 g->update = update1;    /* no other update functions yet */
1026                 g->mach = &mach[i];
1027                 g->colindex = nadd%Ncolor;
1028         }
1029         present[n] = 1;
1030         nadd++;
1031 }
1032
1033 void
1034 dropgraph(int which)
1035 {
1036         Graph *ograph;
1037         int i, j, n;
1038
1039         if(which > nelem(menu2str))
1040                 abort();
1041         /* convert n to index in graph table */
1042         n = -1;
1043         for(i=0; i<ngraph; i++)
1044                 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
1045                         n = i;
1046                         break;
1047                 }
1048         if(n < 0){
1049                 fprint(2, "stats: internal error can't drop graph\n");
1050                 killall("error");
1051         }
1052         ograph = graph;
1053         graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
1054         for(i=0; i<nmach; i++){
1055                 for(j=0; j<n; j++)
1056                         graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
1057                 free(ograph[i*ngraph+j].data);
1058                 freeimage(ograph[i*ngraph+j].overtmp);
1059                 for(j++; j<ngraph; j++)
1060                         graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1061         }
1062         free(ograph);
1063         ngraph--;
1064         present[which] = 0;
1065 }
1066
1067 int
1068 addmachine(char *name)
1069 {
1070         if(ngraph > 0){
1071                 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1072                 usage();
1073         }
1074         if(mach == nil)
1075                 nmach = 0;      /* a little dance to get us started with local machine by default */
1076         mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1077         memset(mach+nmach, 0, sizeof(Machine));
1078         if (initmach(mach+nmach, name)){
1079                 nmach++;
1080                 return 1;
1081         } else
1082                 return 0;
1083 }
1084
1085 void
1086 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1087 {
1088         int j;
1089         uvlong v, vmax;
1090
1091         g->newvalue(g->mach, &v, &vmax, 1);
1092         if(logscale){
1093                 for(j=1; j<=2; j++)
1094                         sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1095                 *np = 2;
1096         }else{
1097                 for(j=1; j<=3; j++)
1098                         sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1099                 *np = 3;
1100         }
1101 }
1102
1103 int
1104 labelwidth(void)
1105 {
1106         int i, j, n, w, maxw;
1107         char strs[Nlab][Lablen];
1108
1109         maxw = 0;
1110         for(i=0; i<ngraph; i++){
1111                 /* choose value for rightmost graph */
1112                 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1113                 for(j=0; j<n; j++){
1114                         w = stringwidth(mediumfont, strs[j]);
1115                         if(w > maxw)
1116                                 maxw = w;
1117                 }
1118         }
1119         return maxw;
1120 }
1121
1122 void
1123 resize(void)
1124 {
1125         int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1126         Graph *g;
1127         Rectangle machr, r;
1128         uvlong v, vmax;
1129         char buf[128], labs[Nlab][Lablen];
1130
1131         draw(screen, screen->r, display->white, nil, ZP);
1132
1133         /* label left edge */
1134         x = screen->r.min.x;
1135         y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
1136         dy = (screen->r.max.y - y)/ngraph;
1137         dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
1138         startx = x+dx+1;
1139         starty = y;
1140         for(i=0; i<ngraph; i++,y+=dy){
1141                 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1142                 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1143                 label(Pt(x, y), dy, graph[i].label);
1144                 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1145         }
1146
1147         /* label top edge */
1148         dx = (screen->r.max.x - startx)/nmach;
1149         for(x=startx, i=0; i<nmach; i++,x+=dx){
1150                 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1151                 j = dx/stringwidth(mediumfont, "0");
1152                 n = mach[i].nproc;
1153                 if(n>1 && j>=1+3+mach[i].lgproc){       /* first char of name + (n) */
1154                         j -= 3+mach[i].lgproc;
1155                         if(j <= 0)
1156                                 j = 1;
1157                         snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1158                 }else
1159                         snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1160                 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
1161         }
1162
1163         maxx = screen->r.max.x;
1164
1165         /* label right, if requested */
1166         if(ylabels && dy>Nlab*(mediumfont->height+1)){
1167                 wid = labelwidth();
1168                 if(wid < (maxx-startx)-30){
1169                         /* else there's not enough room */
1170                         maxx -= 1+Lx+wid;
1171                         draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1172                         y = starty;
1173                         for(j=0; j<ngraph; j++, y+=dy){
1174                                 /* choose value for rightmost graph */
1175                                 g = &graph[ngraph*(nmach-1)+j];
1176                                 labelstrs(g, labs, &nlab);
1177                                 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1178                                 if(j == ngraph-1)
1179                                         r.max.y = screen->r.max.y;
1180                                 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1181                                 for(k=0; k<nlab; k++){
1182                                         ly = y + (dy*(nlab-k)/(nlab+1));
1183                                         draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1184                                         ly -= mediumfont->height/2;
1185                                         string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
1186                                 }
1187                         }
1188                 }
1189         }
1190
1191         /* create graphs */
1192         for(i=0; i<nmach; i++){
1193                 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
1194                 if(i < nmach-1)
1195                         machr.max.x = startx+(i+1)*dx - 1;
1196                 y = starty;
1197                 for(j=0; j<ngraph; j++, y+=dy){
1198                         g = &graph[i*ngraph+j];
1199                         /* allocate data */
1200                         ondata = g->ndata;
1201                         g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1202                         g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1203                         if(g->ndata > ondata)
1204                                 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1205                         /* set geometry */
1206                         g->r = machr;
1207                         g->r.min.y = y;
1208                         g->r.max.y = y+dy - 1;
1209                         if(j == ngraph-1)
1210                                 g->r.max.y = screen->r.max.y;
1211                         draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1212                         g->overflow = 0;
1213                         r = g->r;
1214                         r.max.y = r.min.y+mediumfont->height;
1215                         r.max.x = r.min.x+stringwidth(mediumfont, "999999999999");
1216                         freeimage(g->overtmp);
1217                         g->overtmp = nil;
1218                         if(r.max.x <= g->r.max.x)
1219                                 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1220                         g->newvalue(g->mach, &v, &vmax, 0);
1221                         redraw(g, vmax);
1222                 }
1223         }
1224
1225         flushimage(display, 1);
1226 }
1227
1228 void
1229 eresized(int new)
1230 {
1231         lockdisplay(display);
1232         if(new && getwindow(display, Refnone) < 0) {
1233                 fprint(2, "stats: can't reattach to window\n");
1234                 killall("reattach");
1235         }
1236         resize();
1237         unlockdisplay(display);
1238 }
1239
1240 void
1241 mouseproc(void)
1242 {
1243         Mouse mouse;
1244         int i;
1245
1246         for(;;){
1247                 mouse = emouse();
1248                 if(mouse.buttons == 4){
1249                         lockdisplay(display);
1250                         for(i=0; i<Nmenu2; i++)
1251                                 if(present[i])
1252                                         memmove(menu2str[i], "drop ", Opwid);
1253                                 else
1254                                         memmove(menu2str[i], "add  ", Opwid);
1255                         i = emenuhit(3, &mouse, &menu2);
1256                         if(i >= 0){
1257                                 if(!present[i])
1258                                         addgraph(i);
1259                                 else if(ngraph > 1)
1260                                         dropgraph(i);
1261                                 resize();
1262                         }
1263                         unlockdisplay(display);
1264                 }
1265         }
1266 }
1267
1268 void
1269 startproc(void (*f)(void), int index)
1270 {
1271         int pid;
1272
1273         switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1274         case -1:
1275                 fprint(2, "stats: fork failed: %r\n");
1276                 killall("fork failed");
1277         case 0:
1278                 f();
1279                 fprint(2, "stats: %s process exits\n", procnames[index]);
1280                 if(index >= 0)
1281                         killall("process died");
1282                 exits(nil);
1283         }
1284         if(index >= 0)
1285                 pids[index] = pid;
1286 }
1287
1288 void
1289 main(int argc, char *argv[])
1290 {
1291         int i, j;
1292         double secs;
1293         uvlong v, vmax, nargs;
1294         char args[100];
1295
1296         nmach = 1;
1297         mysysname = getenv("sysname");
1298         if(mysysname == nil){
1299                 fprint(2, "stats: can't find $sysname: %r\n");
1300                 exits("sysname");
1301         }
1302         mysysname = estrdup(mysysname);
1303
1304         nargs = 0;
1305         ARGBEGIN{
1306         case 'T':
1307                 secs = atof(EARGF(usage()));
1308                 if(secs > 0)
1309                         sleeptime = 1000*secs;
1310                 break;
1311         case 'S':
1312                 scale = atof(EARGF(usage()));
1313                 if(scale <= 0)
1314                         usage();
1315                 break;
1316         case 'L':
1317                 logscale++;
1318                 break;
1319         case 'Y':
1320                 ylabels++;
1321                 break;
1322         case 'O':
1323                 oldsystem = 1;
1324                 break;
1325         default:
1326                 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1327                         usage();
1328                 args[nargs++] = ARGC();
1329         }ARGEND
1330
1331         if(argc == 0){
1332                 mach = emalloc(nmach*sizeof(Machine));
1333                 initmach(&mach[0], mysysname);
1334                 readmach(&mach[0], 1);
1335         }else{
1336                 for(i=j=0; i<argc; i++){
1337                         if (addmachine(argv[i]))
1338                                 readmach(&mach[j++], 1);
1339                 }
1340                 if (j == 0)
1341                         exits("connect");
1342         }
1343
1344         for(i=0; i<nargs; i++)
1345         switch(args[i]){
1346         default:
1347                 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1348                 usage();
1349         case 'b':
1350                 addgraph(Mbattery);
1351                 break;
1352         case 'c':
1353                 addgraph(Mcontext);
1354                 break;
1355         case 'e':
1356                 addgraph(Mether);
1357                 break;
1358         case 'E':
1359                 addgraph(Metherin);
1360                 addgraph(Metherout);
1361                 break;
1362         case 'f':
1363                 addgraph(Mfault);
1364                 break;
1365         case 'i':
1366                 addgraph(Mintr);
1367                 break;
1368         case 'I':
1369                 addgraph(Mload);
1370                 addgraph(Midle);
1371                 addgraph(Minintr);
1372                 break;
1373         case 'l':
1374                 addgraph(Mload);
1375                 break;
1376         case 'm':
1377                 addgraph(Mmem);
1378                 break;
1379         case 'n':
1380                 addgraph(Metherin);
1381                 addgraph(Metherout);
1382                 addgraph(Methererr);
1383                 break;
1384         case 'p':
1385                 addgraph(Mtlbpurge);
1386                 break;
1387         case 's':
1388                 addgraph(Msyscall);
1389                 break;
1390         case 't':
1391                 addgraph(Mtlbmiss);
1392                 addgraph(Mtlbpurge);
1393                 break;
1394         case '8':
1395                 addgraph(Msignal);
1396                 break;
1397         case 'w':
1398                 addgraph(Mswap);
1399                 break;
1400         case 'z':
1401                 addgraph(Mtemp);
1402                 break;
1403         }
1404
1405         if(ngraph == 0)
1406                 addgraph(Mload);
1407
1408         for(i=0; i<nmach; i++)
1409                 for(j=0; j<ngraph; j++)
1410                         graph[i*ngraph+j].mach = &mach[i];
1411
1412         if(initdraw(nil, nil, "stats") < 0){
1413                 fprint(2, "stats: initdraw failed: %r\n");
1414                 exits("initdraw");
1415         }
1416         colinit();
1417         einit(Emouse);
1418         notify(nil);
1419         startproc(mouseproc, Mouseproc);
1420         pids[Mainproc] = getpid();
1421         display->locking = 1;   /* tell library we're using the display lock */
1422
1423         resize();
1424
1425         unlockdisplay(display); /* display is still locked from initdraw() */
1426         for(;;){
1427                 for(i=0; i<nmach; i++)
1428                         readmach(&mach[i], 0);
1429                 lockdisplay(display);
1430                 parity = 1-parity;
1431                 for(i=0; i<nmach*ngraph; i++){
1432                         graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1433                         graph[i].update(&graph[i], v, vmax);
1434                 }
1435                 flushimage(display, 1);
1436                 unlockdisplay(display);
1437                 sleep(sleeptime);
1438         }
1439 }