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