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