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