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