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