]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/stats.c
fix typo
[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         auth_freeAI(ai);
561
562         n = write(fd, tree, strlen(tree));
563         if(n < 0){
564                 close(fd);
565                 return -1;
566         }
567
568         strcpy(buf, "can't read tree");
569         n = read(fd, buf, sizeof buf - 1);
570         if(n!=2 || buf[0]!='O' || buf[1]!='K'){
571                 buf[sizeof buf - 1] = '\0';
572                 werrstr("bad remote tree: %s\n", buf);
573                 close(fd);
574                 return -1;
575         }
576
577 //      if(strstr(dir, "tcp"))
578 //              fd = filter(fd);
579
580         if(oldsystem)
581                 return old9p(fd);
582
583         return fd;
584 }
585
586 int
587 readswap(Machine *m, uvlong *a)
588 {
589         if(strstr(m->buf, "memory\n")){
590                 /* new /dev/swap - skip first 3 numbers */
591                 if(!readnums(m, 7, a, 1))
592                         return 0;
593                 a[0] = a[3];
594                 a[1] = a[4];
595                 a[2] = a[5];
596                 a[3] = a[6];
597                 return 1;
598         }
599         return readnums(m, nelem(m->devswap), a, 0);
600 }
601
602 char*
603 shortname(char *s)
604 {
605         char *p, *e;
606
607         p = estrdup(s);
608         e = strchr(p, '.');
609         if(e)
610                 *e = 0;
611         return p;
612 }
613
614 int
615 ilog10(uvlong j)
616 {
617         int i;
618
619         for(i = 0; j >= 10; i++)
620                 j /= 10;
621         return i;
622 }
623
624 int
625 initmach(Machine *m, char *name)
626 {
627         int n, fd;
628         uvlong a[MAXNUM];
629         char *p, mpt[256], buf[256];
630
631         p = strchr(name, '!');
632         if(p)
633                 p++;
634         else
635                 p = name;
636         m->name = estrdup(p);
637         m->shortname = shortname(p);
638         m->remote = (strcmp(p, mysysname) != 0);
639         if(m->remote == 0)
640                 strcpy(mpt, "");
641         else{
642                 snprint(mpt, sizeof mpt, "/n/%s", p);
643                 fd = connectexportfs(name);
644                 if(fd < 0){
645                         fprint(2, "can't connect to %s: %r\n", name);
646                         return 0;
647                 }
648                 /* BUG? need to use amount() now? */
649                 if(mount(fd, -1, mpt, MREPL, "") < 0){
650                         fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt);
651                         strcpy(mpt, "/n/sid");
652                         if(mount(fd, -1, mpt, MREPL, "") < 0){
653                                 fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt);
654                                 return 0;
655                         }
656                 }
657         }
658
659         snprint(buf, sizeof buf, "%s/dev/swap", mpt);
660         m->swapfd = open(buf, OREAD);
661         if(loadbuf(m, &m->swapfd) && readswap(m, a))
662                 memmove(m->devswap, a, sizeof m->devswap);
663         else{
664                 m->devswap[Maxswap] = 100;
665                 m->devswap[Maxmem] = 100;
666         }
667
668         snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
669         m->statsfd = open(buf, OREAD);
670         if(loadbuf(m, &m->statsfd)){
671                 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
672                         ;
673                 m->nproc = n;
674         }else
675                 m->nproc = 1;
676         m->lgproc = ilog10(m->nproc);
677
678         snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
679         m->etherfd = open(buf, OREAD);
680         if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
681                 memmove(m->netetherstats, a, sizeof m->netetherstats);
682
683         snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
684         m->ifstatsfd = open(buf, OREAD);
685         if(loadbuf(m, &m->ifstatsfd)){
686                 /* need to check that this is a wavelan interface */
687                 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
688                         memmove(m->netetherifstats, a, sizeof m->netetherifstats);
689         }
690
691         snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
692         m->batteryfd = open(buf, OREAD);
693         m->bitsybatfd = -1;
694         if(m->batteryfd >= 0){
695                 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
696                         memmove(m->batterystats, a, sizeof(m->batterystats));
697         }else{
698                 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
699                 m->bitsybatfd = open(buf, OREAD);
700                 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
701                         memmove(m->batterystats, a, sizeof(m->batterystats));
702         }
703         snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
704         m->tempfd = open(buf, OREAD);
705         if(loadbuf(m, &m->tempfd))
706                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
707                          m->temp[n] = a[0];
708         return 1;
709 }
710
711 jmp_buf catchalarm;
712
713 void
714 alarmed(void *a, char *s)
715 {
716         if(strcmp(s, "alarm") == 0)
717                 notejmp(a, catchalarm, 1);
718         noted(NDFLT);
719 }
720
721 int
722 needswap(int init)
723 {
724         return init | present[Mmem] | present[Mswap];
725 }
726
727
728 int
729 needstat(int init)
730 {
731         return init | present[Mcontext]  | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
732                 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
733 }
734
735
736 int
737 needether(int init)
738 {
739         return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
740 }
741
742 int
743 needbattery(int init)
744 {
745         return init | present[Mbattery];
746 }
747
748 int
749 needsignal(int init)
750 {
751         return init | present[Msignal];
752 }
753
754 int
755 needtemp(int init)
756 {
757         return init | present[Mtemp];
758 }
759
760 void
761 readmach(Machine *m, int init)
762 {
763         int n, i;
764         uvlong a[nelem(m->devsysstat)];
765         char buf[32];
766
767         if(m->remote && (m->disable || setjmp(catchalarm))){
768                 if (m->disable++ >= 5)
769                         m->disable = 0; /* give it another chance */
770                 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
771                 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
772                 return;
773         }
774         snprint(buf, sizeof buf, "%s", m->name);
775         if (strcmp(m->name, buf) != 0){
776                 free(m->name);
777                 m->name = estrdup(buf);
778                 free(m->shortname);
779                 m->shortname = shortname(buf);
780                 if(display != nil)      /* else we're still initializing */
781                         eresized(0);
782         }
783         if(m->remote){
784                 notify(alarmed);
785                 alarm(5000);
786         }
787         if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
788                 memmove(m->devswap, a, sizeof m->devswap);
789         if(needstat(init) && loadbuf(m, &m->statsfd)){
790                 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
791                 memset(m->devsysstat, 0, sizeof m->devsysstat);
792                 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
793                         for(i=0; i<nelem(m->devsysstat); i++)
794                                 m->devsysstat[i] += a[i];
795         }
796         if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
797                 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
798                 memmove(m->netetherstats, a, sizeof m->netetherstats);
799         }
800         if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
801                 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
802         }
803         if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
804                 memmove(m->batterystats, a, sizeof(m->batterystats));
805         if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
806                 memmove(m->batterystats, a, sizeof(m->batterystats));
807         if(needtemp(init) && loadbuf(m, &m->tempfd))
808                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
809                          m->temp[n] = a[0];
810         if(m->remote){
811                 alarm(0);
812                 notify(nil);
813         }
814 }
815
816 void
817 memval(Machine *m, uvlong *v, uvlong *vmax, int)
818 {
819         *v = m->devswap[Mem];
820         *vmax = m->devswap[Maxmem];
821 }
822
823 void
824 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
825 {
826         *v = m->devswap[Swap];
827         *vmax = m->devswap[Maxswap];
828 }
829
830 void
831 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
832 {
833         *v = m->devsysstat[Context]-m->prevsysstat[Context];
834         *vmax = sleeptime*m->nproc;
835         if(init)
836                 *vmax = sleeptime;
837 }
838
839 /*
840  * bug: need to factor in HZ
841  */
842 void
843 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
844 {
845         *v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt];
846         *vmax = sleeptime*m->nproc*10;
847         if(init)
848                 *vmax = sleeptime*10;
849 }
850
851 void
852 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
853 {
854         *v = m->devsysstat[Syscall]-m->prevsysstat[Syscall];
855         *vmax = sleeptime*m->nproc;
856         if(init)
857                 *vmax = sleeptime;
858 }
859
860 void
861 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
862 {
863         *v = m->devsysstat[Fault]-m->prevsysstat[Fault];
864         *vmax = sleeptime*m->nproc;
865         if(init)
866                 *vmax = sleeptime;
867 }
868
869 void
870 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
871 {
872         *v = m->devsysstat[TLBfault]-m->prevsysstat[TLBfault];
873         *vmax = (sleeptime/1000)*10*m->nproc;
874         if(init)
875                 *vmax = (sleeptime/1000)*10;
876 }
877
878 void
879 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
880 {
881         *v = m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge];
882         *vmax = (sleeptime/1000)*10*m->nproc;
883         if(init)
884                 *vmax = (sleeptime/1000)*10;
885 }
886
887 void
888 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
889 {
890         *v = m->devsysstat[Load];
891         *vmax = 1000*m->nproc;
892         if(init)
893                 *vmax = 1000;
894 }
895
896 void
897 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
898 {
899         *v = m->devsysstat[Idle]/m->nproc;
900         *vmax = 100;
901 }
902
903 void
904 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
905 {
906         *v = m->devsysstat[InIntr]/m->nproc;
907         *vmax = 100;
908 }
909
910 void
911 etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
912 {
913         *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
914         *vmax = sleeptime*m->nproc;
915         if(init)
916                 *vmax = sleeptime;
917 }
918
919 void
920 etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
921 {
922         *v = m->netetherstats[In]-m->prevetherstats[In];
923         *vmax = sleeptime*m->nproc;
924         if(init)
925                 *vmax = sleeptime;
926 }
927
928 void
929 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
930 {
931         *v = m->netetherstats[Out]-m->prevetherstats[Out];
932         *vmax = sleeptime*m->nproc;
933         if(init)
934                 *vmax = sleeptime;
935 }
936
937 void
938 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
939 {
940         int i;
941
942         *v = 0;
943         for(i=Err0; i<nelem(m->netetherstats); i++)
944                 *v += m->netetherstats[i];
945         *vmax = (sleeptime/1000)*10*m->nproc;
946         if(init)
947                 *vmax = (sleeptime/1000)*10;
948 }
949
950 void
951 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
952 {
953         *v = m->batterystats[0];
954         if(m->bitsybatfd >= 0)
955                 *vmax = 184;            // at least on my bitsy...
956         else
957                 *vmax = 100;
958 }
959
960 void
961 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
962 {
963         ulong l;
964
965         *vmax = sleeptime;
966         l = m->netetherifstats[0];
967         /*
968          * Range is seen to be from about -45 (strong) to -95 (weak); rescale
969          */
970         if(l == 0){     /* probably not present */
971                 *v = 0;
972                 return;
973         }
974         *v = 20*(l+95);
975 }
976
977 void
978 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
979 {
980         ulong l;
981
982         *vmax = sleeptime;
983         l = m->temp[0];
984         if(l == ~0 || l == 0)
985                 *v = 0;
986         else
987                 *v = (l-20)*27;
988 }
989
990 void
991 usage(void)
992 {
993         fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
994         exits("usage");
995 }
996
997 void
998 addgraph(int n)
999 {
1000         Graph *g, *ograph;
1001         int i, j;
1002         static int nadd;
1003
1004         if(n > nelem(menu2str))
1005                 abort();
1006         /* avoid two adjacent graphs of same color */
1007         if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
1008                 nadd++;
1009         ograph = graph;
1010         graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
1011         for(i=0; i<nmach; i++)
1012                 for(j=0; j<ngraph; j++)
1013                         graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
1014         free(ograph);
1015         ngraph++;
1016         for(i=0; i<nmach; i++){
1017                 g = &graph[i*ngraph+(ngraph-1)];
1018                 memset(g, 0, sizeof(Graph));
1019                 g->label = menu2str[n]+Opwid;
1020                 g->newvalue = newvaluefn[n];
1021                 g->update = update1;    /* no other update functions yet */
1022                 g->mach = &mach[i];
1023                 g->colindex = nadd%Ncolor;
1024         }
1025         present[n] = 1;
1026         nadd++;
1027 }
1028
1029 void
1030 dropgraph(int which)
1031 {
1032         Graph *ograph;
1033         int i, j, n;
1034
1035         if(which > nelem(menu2str))
1036                 abort();
1037         /* convert n to index in graph table */
1038         n = -1;
1039         for(i=0; i<ngraph; i++)
1040                 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
1041                         n = i;
1042                         break;
1043                 }
1044         if(n < 0){
1045                 fprint(2, "stats: internal error can't drop graph\n");
1046                 killall("error");
1047         }
1048         ograph = graph;
1049         graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
1050         for(i=0; i<nmach; i++){
1051                 for(j=0; j<n; j++)
1052                         graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
1053                 free(ograph[i*ngraph+j].data);
1054                 freeimage(ograph[i*ngraph+j].overtmp);
1055                 for(j++; j<ngraph; j++)
1056                         graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1057         }
1058         free(ograph);
1059         ngraph--;
1060         present[which] = 0;
1061 }
1062
1063 int
1064 addmachine(char *name)
1065 {
1066         if(ngraph > 0){
1067                 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1068                 usage();
1069         }
1070         if(mach == nil)
1071                 nmach = 0;      /* a little dance to get us started with local machine by default */
1072         mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1073         memset(mach+nmach, 0, sizeof(Machine));
1074         if (initmach(mach+nmach, name)){
1075                 nmach++;
1076                 return 1;
1077         } else
1078                 return 0;
1079 }
1080
1081 void
1082 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1083 {
1084         int j;
1085         uvlong v, vmax;
1086
1087         g->newvalue(g->mach, &v, &vmax, 1);
1088         if(logscale){
1089                 for(j=1; j<=2; j++)
1090                         sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1091                 *np = 2;
1092         }else{
1093                 for(j=1; j<=3; j++)
1094                         sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1095                 *np = 3;
1096         }
1097 }
1098
1099 int
1100 labelwidth(void)
1101 {
1102         int i, j, n, w, maxw;
1103         char strs[Nlab][Lablen];
1104
1105         maxw = 0;
1106         for(i=0; i<ngraph; i++){
1107                 /* choose value for rightmost graph */
1108                 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1109                 for(j=0; j<n; j++){
1110                         w = stringwidth(font, strs[j]);
1111                         if(w > maxw)
1112                                 maxw = w;
1113                 }
1114         }
1115         return maxw;
1116 }
1117
1118 void
1119 resize(void)
1120 {
1121         int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1122         Graph *g;
1123         Rectangle machr, r;
1124         uvlong v, vmax;
1125         char buf[128], labs[Nlab][Lablen];
1126
1127         draw(screen, screen->r, display->white, nil, ZP);
1128
1129         /* label left edge */
1130         x = screen->r.min.x;
1131         y = screen->r.min.y + Labspace+font->height+Labspace;
1132         dy = (screen->r.max.y - y)/ngraph;
1133         dx = Labspace+stringwidth(font, "0")+Labspace;
1134         startx = x+dx+1;
1135         starty = y;
1136         for(i=0; i<ngraph; i++,y+=dy){
1137                 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1138                 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1139                 label(Pt(x, y), dy, graph[i].label);
1140                 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1141         }
1142
1143         /* label top edge */
1144         dx = (screen->r.max.x - startx)/nmach;
1145         for(x=startx, i=0; i<nmach; i++,x+=dx){
1146                 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1147                 j = dx/stringwidth(font, "0");
1148                 n = mach[i].nproc;
1149                 if(n>1 && j>=1+3+mach[i].lgproc){       /* first char of name + (n) */
1150                         j -= 3+mach[i].lgproc;
1151                         if(j <= 0)
1152                                 j = 1;
1153                         snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1154                 }else
1155                         snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1156                 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
1157         }
1158
1159         maxx = screen->r.max.x;
1160
1161         /* label right, if requested */
1162         if(ylabels && dy>Nlab*(font->height+1)){
1163                 wid = labelwidth();
1164                 if(wid < (maxx-startx)-30){
1165                         /* else there's not enough room */
1166                         maxx -= 1+Lx+wid;
1167                         draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1168                         y = starty;
1169                         for(j=0; j<ngraph; j++, y+=dy){
1170                                 /* choose value for rightmost graph */
1171                                 g = &graph[ngraph*(nmach-1)+j];
1172                                 labelstrs(g, labs, &nlab);
1173                                 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1174                                 if(j == ngraph-1)
1175                                         r.max.y = screen->r.max.y;
1176                                 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1177                                 for(k=0; k<nlab; k++){
1178                                         ly = y + (dy*(nlab-k)/(nlab+1));
1179                                         draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1180                                         ly -= font->height/2;
1181                                         string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
1182                                 }
1183                         }
1184                 }
1185         }
1186
1187         /* create graphs */
1188         for(i=0; i<nmach; i++){
1189                 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
1190                 if(i < nmach-1)
1191                         machr.max.x = startx+(i+1)*dx - 1;
1192                 y = starty;
1193                 for(j=0; j<ngraph; j++, y+=dy){
1194                         g = &graph[i*ngraph+j];
1195                         /* allocate data */
1196                         ondata = g->ndata;
1197                         g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1198                         g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1199                         if(g->ndata > ondata)
1200                                 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1201                         /* set geometry */
1202                         g->r = machr;
1203                         g->r.min.y = y;
1204                         g->r.max.y = y+dy - 1;
1205                         if(j == ngraph-1)
1206                                 g->r.max.y = screen->r.max.y;
1207                         draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1208                         g->overflow = 0;
1209                         r = g->r;
1210                         r.max.y = r.min.y+font->height;
1211                         r.max.x = r.min.x+stringwidth(font, "999999999999");
1212                         freeimage(g->overtmp);
1213                         g->overtmp = nil;
1214                         if(r.max.x <= g->r.max.x)
1215                                 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1216                         g->newvalue(g->mach, &v, &vmax, 0);
1217                         redraw(g, vmax);
1218                 }
1219         }
1220
1221         flushimage(display, 1);
1222 }
1223
1224 void
1225 eresized(int new)
1226 {
1227         lockdisplay(display);
1228         if(new && getwindow(display, Refnone) < 0) {
1229                 fprint(2, "stats: can't reattach to window\n");
1230                 killall("reattach");
1231         }
1232         resize();
1233         unlockdisplay(display);
1234 }
1235
1236 void
1237 mouseproc(void)
1238 {
1239         Mouse mouse;
1240         int i;
1241
1242         for(;;){
1243                 mouse = emouse();
1244                 if(mouse.buttons == 4){
1245                         lockdisplay(display);
1246                         for(i=0; i<Nmenu2; i++)
1247                                 if(present[i])
1248                                         memmove(menu2str[i], "drop ", Opwid);
1249                                 else
1250                                         memmove(menu2str[i], "add  ", Opwid);
1251                         i = emenuhit(3, &mouse, &menu2);
1252                         if(i >= 0){
1253                                 if(!present[i])
1254                                         addgraph(i);
1255                                 else if(ngraph > 1)
1256                                         dropgraph(i);
1257                                 resize();
1258                         }
1259                         unlockdisplay(display);
1260                 }
1261         }
1262 }
1263
1264 void
1265 startproc(void (*f)(void), int index)
1266 {
1267         int pid;
1268
1269         switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1270         case -1:
1271                 fprint(2, "stats: fork failed: %r\n");
1272                 killall("fork failed");
1273         case 0:
1274                 f();
1275                 fprint(2, "stats: %s process exits\n", procnames[index]);
1276                 if(index >= 0)
1277                         killall("process died");
1278                 exits(nil);
1279         }
1280         if(index >= 0)
1281                 pids[index] = pid;
1282 }
1283
1284 void
1285 main(int argc, char *argv[])
1286 {
1287         int i, j;
1288         double secs;
1289         uvlong v, vmax, nargs;
1290         char args[100];
1291
1292         nmach = 1;
1293         mysysname = getenv("sysname");
1294         if(mysysname == nil){
1295                 fprint(2, "stats: can't find $sysname: %r\n");
1296                 exits("sysname");
1297         }
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 }