]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/stats.c
stats: handle /dev/sysstat 32bit overflow in delta calculation
[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 char    *mysysname;
196 char    argchars[] = "8bceEfiImlnpstwz";
197 int     pids[NPROC];
198 int     parity; /* toggled to avoid patterns in textured background */
199 int     nmach;
200 int     ngraph; /* totaly number is ngraph*nmach */
201 double  scale = 1.0;
202 int     logscale = 0;
203 int     ylabels = 0;
204 int     oldsystem = 0;
205 int     sleeptime = 1000;
206
207 char    *procnames[NPROC] = {"main", "mouse"};
208
209 void
210 killall(char *s)
211 {
212         int i, pid;
213
214         pid = getpid();
215         for(i=0; i<NPROC; i++)
216                 if(pids[i] && pids[i]!=pid)
217                         postnote(PNPROC, pids[i], "kill");
218         exits(s);
219 }
220
221 void*
222 emalloc(ulong sz)
223 {
224         void *v;
225         v = malloc(sz);
226         if(v == nil) {
227                 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
228                 killall("mem");
229         }
230         memset(v, 0, sz);
231         return v;
232 }
233
234 void*
235 erealloc(void *v, ulong sz)
236 {
237         v = realloc(v, sz);
238         if(v == nil) {
239                 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
240                 killall("mem");
241         }
242         return v;
243 }
244
245 char*
246 estrdup(char *s)
247 {
248         char *t;
249         if((t = strdup(s)) == nil) {
250                 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
251                 killall("mem");
252         }
253         return t;
254 }
255
256 void
257 mkcol(int i, int c0, int c1, int c2)
258 {
259         cols[i][0] = allocimagemix(display, c0, DWhite);
260         cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
261         cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
262 }
263
264 void
265 colinit(void)
266 {
267         /* Peach */
268         mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
269         /* Aqua */
270         mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
271         /* Yellow */
272         mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
273         /* Green */
274         mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
275         /* Blue */
276         mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
277         /* Grey */
278         cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
279         cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
280         cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
281 }
282
283 int
284 loadbuf(Machine *m, int *fd)
285 {
286         int n;
287
288
289         if(*fd < 0)
290                 return 0;
291         seek(*fd, 0, 0);
292         n = read(*fd, m->buf, sizeof m->buf-1);
293         if(n <= 0){
294                 close(*fd);
295                 *fd = -1;
296                 return 0;
297         }
298         m->bufp = m->buf;
299         m->ebufp = m->buf+n;
300         m->buf[n] = 0;
301         return 1;
302 }
303
304 void
305 label(Point p, int dy, char *text)
306 {
307         char *s;
308         Rune r[2];
309         int w, maxw, maxy;
310
311         p.x += Labspace;
312         maxy = p.y+dy;
313         maxw = 0;
314         r[1] = '\0';
315         for(s=text; *s; ){
316                 if(p.y+font->height-Ysqueeze > maxy)
317                         break;
318                 w = chartorune(r, s);
319                 s += w;
320                 w = runestringwidth(font, r);
321                 if(w > maxw)
322                         maxw = w;
323                 runestring(screen, p, display->black, ZP, font, r);
324                 p.y += font->height-Ysqueeze;
325         }
326 }
327
328 Point
329 paritypt(int x)
330 {
331         return Pt(x+parity, 0);
332 }
333
334 Point
335 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
336 {
337         Point p;
338         double y;
339
340         p.x = x;
341         y = ((double)v)/(vmax*scale);
342         if(logscale){
343                 /*
344                  * Arrange scale to cover a factor of 1000.
345                  * vmax corresponds to the 100 mark.
346                  * 10*vmax is the top of the scale.
347                  */
348                 if(y <= 0.)
349                         y = 0;
350                 else{
351                         y = log10(y);
352                         /* 1 now corresponds to the top; -2 to the bottom; rescale */
353                         y = (y+2.)/3.;
354                 }
355         }
356         if(y >= 1.)
357                 y = 1;
358         if(y <= 0.)
359                 y = 0;
360         p.y = g->r.max.y - Dy(g->r)*y - Dot;
361         if(p.y < g->r.min.y)
362                 p.y = g->r.min.y;
363         if(p.y > g->r.max.y-Dot)
364                 p.y = g->r.max.y-Dot;
365         return p;
366 }
367
368 void
369 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
370 {
371         int c;
372         Point p, q;
373
374         c = g->colindex;
375         p = datapoint(g, x, v, vmax);
376         q = datapoint(g, x, prev, vmax);
377         if(p.y < q.y){
378                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
379                 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
380                 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
381         }else{
382                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
383                 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
384                 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
385         }
386
387 }
388
389 void
390 redraw(Graph *g, uvlong vmax)
391 {
392         int i, c;
393
394         c = g->colindex;
395         draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
396         for(i=1; i<Dx(g->r); i++)
397                 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
398         drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
399         g->overflow = 0;
400 }
401
402 void
403 update1(Graph *g, uvlong v, uvlong vmax)
404 {
405         char buf[48];
406         int overflow;
407
408         if(g->overflow && g->overtmp!=nil)
409                 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
410         draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
411         drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
412         memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
413         g->data[0] = v;
414         g->overflow = 0;
415         if(logscale)
416                 overflow = (v>10*vmax*scale);
417         else
418                 overflow = (v>vmax*scale);
419         if(overflow && g->overtmp!=nil){
420                 g->overflow = 1;
421                 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
422                 sprint(buf, "%llud", v);
423                 string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
424         }
425 }
426
427 /* read one line of text from buffer and process integers */
428 int
429 readnums(Machine *m, int n, uvlong *a, int spanlines)
430 {
431         int i;
432         char *p, *ep;
433
434         if(spanlines)
435                 ep = m->ebufp;
436         else
437                 for(ep=m->bufp; ep<m->ebufp; ep++)
438                         if(*ep == '\n')
439                                 break;
440         p = m->bufp;
441         for(i=0; i<n && p<ep; i++){
442                 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
443                         p++;
444                 if(p == ep)
445                         break;
446                 a[i] = strtoull(p, &p, 10);
447         }
448         if(ep < m->ebufp)
449                 ep++;
450         m->bufp = ep;
451         return i == n;
452 }
453
454 /* Network on fd1, mount driver on fd0 */
455 static int
456 filter(int fd)
457 {
458         int p[2];
459
460         if(pipe(p) < 0){
461                 fprint(2, "stats: can't pipe: %r\n");
462                 killall("pipe");
463         }
464
465         switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
466         case -1:
467                 sysfatal("rfork record module");
468         case 0:
469                 dup(fd, 1);
470                 close(fd);
471                 dup(p[0], 0);
472                 close(p[0]);
473                 close(p[1]);
474                 execl("/bin/aux/fcall", "fcall", nil);
475                 fprint(2, "stats: can't exec fcall: %r\n");
476                 killall("fcall");
477         default:
478                 close(fd);
479                 close(p[0]);
480         }
481         return p[1];
482 }
483
484 /*
485  * 9fs
486  */
487 int
488 connect9fs(char *addr)
489 {
490         char dir[256], *na;
491         int fd;
492
493         fprint(2, "connect9fs...");
494         na = netmkaddr(addr, 0, "9fs");
495
496         fprint(2, "dial %s...", na);
497         if((fd = dial(na, 0, dir, 0)) < 0)
498                 return -1;
499
500         fprint(2, "dir %s...", dir);
501 //      if(strstr(dir, "tcp"))
502 //              fd = filter(fd);
503         return fd;
504 }
505
506 int
507 old9p(int fd)
508 {
509         int p[2];
510
511         if(pipe(p) < 0)
512                 return -1;
513
514         switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
515         case -1:
516                 return -1;
517         case 0:
518                 if(fd != 1){
519                         dup(fd, 1);
520                         close(fd);
521                 }
522                 if(p[0] != 0){
523                         dup(p[0], 0);
524                         close(p[0]);
525                 }
526                 close(p[1]);
527                 if(0){
528                         fd = open("/sys/log/cpu", OWRITE);
529                         if(fd != 2){
530                                 dup(fd, 2);
531                                 close(fd);
532                         }
533                         execl("/bin/srvold9p", "srvold9p", "-ds", nil);
534                 } else
535                         execl("/bin/srvold9p", "srvold9p", "-s", nil);
536                 return -1;
537         default:
538                 close(fd);
539                 close(p[0]);
540         }
541         return p[1];
542 }
543
544
545 /*
546  * exportfs
547  */
548 int
549 connectexportfs(char *addr)
550 {
551         char buf[ERRMAX], dir[256], *na;
552         int fd, n;
553         char *tree;
554         AuthInfo *ai;
555
556         tree = "/";
557         na = netmkaddr(addr, 0, "exportfs");
558         if((fd = dial(na, 0, dir, 0)) < 0)
559                 return -1;
560
561         ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client");
562         if(ai == nil)
563                 return -1;
564         auth_freeAI(ai);
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 int
718 alarmed(void *a, char *s)
719 {
720         if(strcmp(s, "alarm") == 0)
721                 notejmp(a, catchalarm, 1);
722         return 0;
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                 atnotify(alarmed, 1);
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                 atnotify(alarmed, 0);
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])&0xffffffff;
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])&0xffffffff;
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])&0xffffffff;
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])&0xffffffff;
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])&0xffffffff;
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])&0xffffffff;
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(font, 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+font->height+Labspace;
1136         dy = (screen->r.max.y - y)/ngraph;
1137         dx = Labspace+stringwidth(font, "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(font, "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, font, buf);
1161         }
1162
1163         maxx = screen->r.max.x;
1164
1165         /* label right, if requested */
1166         if(ylabels && dy>Nlab*(font->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 -= font->height/2;
1185                                         string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, 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+font->height;
1215                         r.max.x = r.min.x+stringwidth(font, "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
1303         nargs = 0;
1304         ARGBEGIN{
1305         case 'T':
1306                 secs = atof(EARGF(usage()));
1307                 if(secs > 0)
1308                         sleeptime = 1000*secs;
1309                 break;
1310         case 'S':
1311                 scale = atof(EARGF(usage()));
1312                 if(scale <= 0)
1313                         usage();
1314                 break;
1315         case 'L':
1316                 logscale++;
1317                 break;
1318         case 'Y':
1319                 ylabels++;
1320                 break;
1321         case 'O':
1322                 oldsystem = 1;
1323                 break;
1324         default:
1325                 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1326                         usage();
1327                 args[nargs++] = ARGC();
1328         }ARGEND
1329
1330         if(argc == 0){
1331                 mach = emalloc(nmach*sizeof(Machine));
1332                 initmach(&mach[0], mysysname);
1333                 readmach(&mach[0], 1);
1334         }else{
1335                 for(i=j=0; i<argc; i++){
1336                         if (addmachine(argv[i]))
1337                                 readmach(&mach[j++], 1);
1338                 }
1339                 if (j == 0)
1340                         exits("connect");
1341         }
1342
1343         for(i=0; i<nargs; i++)
1344         switch(args[i]){
1345         default:
1346                 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1347                 usage();
1348         case 'b':
1349                 addgraph(Mbattery);
1350                 break;
1351         case 'c':
1352                 addgraph(Mcontext);
1353                 break;
1354         case 'e':
1355                 addgraph(Mether);
1356                 break;
1357         case 'E':
1358                 addgraph(Metherin);
1359                 addgraph(Metherout);
1360                 break;
1361         case 'f':
1362                 addgraph(Mfault);
1363                 break;
1364         case 'i':
1365                 addgraph(Mintr);
1366                 break;
1367         case 'I':
1368                 addgraph(Mload);
1369                 addgraph(Midle);
1370                 addgraph(Minintr);
1371                 break;
1372         case 'l':
1373                 addgraph(Mload);
1374                 break;
1375         case 'm':
1376                 addgraph(Mmem);
1377                 break;
1378         case 'n':
1379                 addgraph(Metherin);
1380                 addgraph(Metherout);
1381                 addgraph(Methererr);
1382                 break;
1383         case 'p':
1384                 addgraph(Mtlbpurge);
1385                 break;
1386         case 's':
1387                 addgraph(Msyscall);
1388                 break;
1389         case 't':
1390                 addgraph(Mtlbmiss);
1391                 addgraph(Mtlbpurge);
1392                 break;
1393         case '8':
1394                 addgraph(Msignal);
1395                 break;
1396         case 'w':
1397                 addgraph(Mswap);
1398                 break;
1399         case 'z':
1400                 addgraph(Mtemp);
1401                 break;
1402         }
1403
1404         if(ngraph == 0)
1405                 addgraph(Mload);
1406
1407         for(i=0; i<nmach; i++)
1408                 for(j=0; j<ngraph; j++)
1409                         graph[i*ngraph+j].mach = &mach[i];
1410
1411         if(initdraw(nil, nil, "stats") < 0){
1412                 fprint(2, "stats: initdraw failed: %r\n");
1413                 exits("initdraw");
1414         }
1415         colinit();
1416         einit(Emouse);
1417         startproc(mouseproc, Mouseproc);
1418         pids[Mainproc] = getpid();
1419         display->locking = 1;   /* tell library we're using the display lock */
1420
1421         resize();
1422
1423         unlockdisplay(display); /* display is still locked from initdraw() */
1424         for(;;){
1425                 for(i=0; i<nmach; i++)
1426                         readmach(&mach[i], 0);
1427                 lockdisplay(display);
1428                 parity = 1-parity;
1429                 for(i=0; i<nmach*ngraph; i++){
1430                         graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1431                         graph[i].update(&graph[i], v, vmax);
1432                 }
1433                 flushimage(display, 1);
1434                 unlockdisplay(display);
1435                 sleep(sleeptime);
1436         }
1437 }