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