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