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