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