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 int sleeptime = 1000; 1181 1182 nmach = 1; 1183 mysysname = getenv("sysname"); 1184 if(mysysname == nil){ 1185 fprint(2, "stats: can't find $sysname: %r\n"); 1186 exits("sysname"); 1187 } 1188 mysysname = estrdup(mysysname); 1189 1190 nargs = 0; 1191 ARGBEGIN{ 1192 case 'T': 1193 s = ARGF(); 1194 if(s == nil) 1195 usage(); 1196 i = atoi(s); 1197 if(i > 0) 1198 sleeptime = 1000*i; 1199 break; 1200 case 'S': 1201 s = ARGF(); 1202 if(s == nil) 1203 usage(); 1204 scale = atof(s); 1205 if(scale <= 0.) 1206 usage(); 1207 break; 1208 case 'L': 1209 logscale++; 1210 break; 1211 case 'Y': 1212 ylabels++; 1213 break; 1214 case 'O': 1215 oldsystem = 1; 1216 break; 1217 default: 1218 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil) 1219 usage(); 1220 args[nargs++] = ARGC(); 1221 }ARGEND 1222 1223 if(argc == 0){ 1224 mach = emalloc(nmach*sizeof(Machine)); 1225 initmach(&mach[0], mysysname); 1226 readmach(&mach[0], 1); 1227 }else{ 1228 for(i=0; i<argc; i++){ 1229 addmachine(argv[i]); 1230 readmach(&mach[i], 1); 1231 } 1232 } 1233 1234 for(i=0; i<nargs; i++) 1235 switch(args[i]){ 1236 default: 1237 fprint(2, "stats: internal error: unknown arg %c\n", args[i]); 1238 usage(); 1239 case 'b': 1240 addgraph(Mbattery); 1241 break; 1242 case 'c': 1243 addgraph(Mcontext); 1244 break; 1245 case 'e': 1246 addgraph(Mether); 1247 break; 1248 case 'E': 1249 addgraph(Metherin); 1250 addgraph(Metherout); 1251 break; 1252 case 'f': 1253 addgraph(Mfault); 1254 break; 1255 case 'i': 1256 addgraph(Mintr); 1257 break; 1258 case 'l': 1259 addgraph(Mload); 1260 break; 1261 case 'm': 1262 addgraph(Mmem); 1263 break; 1264 case 'n': 1265 addgraph(Metherin); 1266 addgraph(Metherout); 1267 addgraph(Methererr); 1268 break; 1269 case 'p': 1270 addgraph(Mtlbpurge); 1271 break; 1272 case 's': 1273 addgraph(Msyscall); 1274 break; 1275 case 't': 1276 addgraph(Mtlbmiss); 1277 addgraph(Mtlbpurge); 1278 break; 1279 case '8': 1280 addgraph(Msignal); 1281 break; 1282 case 'w': 1283 addgraph(Mswap); 1284 break; 1285 } 1286 1287 if(ngraph == 0) 1288 addgraph(Mload); 1289 1290 for(i=0; i<nmach; i++) 1291 for(j=0; j<ngraph; j++) 1292 graph[i*ngraph+j].mach = &mach[i]; 1293 1294 if(initdraw(nil, nil, "stats") < 0){ 1295 fprint(2, "stats: initdraw failed: %r\n"); 1296 exits("initdraw"); 1297 } 1298 colinit(); 1299 einit(Emouse); 1300 notify(nil); 1301 startproc(mouseproc, Mouseproc); 1302 pids[Mainproc] = getpid(); 1303 display->locking = 1; /* tell library we're using the display lock */ 1304 1305 resize(); 1306 1307 unlockdisplay(display); /* display is still locked from initdraw() */ 1308 for(;;){ 1309 for(i=0; i<nmach; i++) 1310 readmach(&mach[i], 0); 1311 lockdisplay(display); 1312 parity = 1-parity; 1313 for(i=0; i<nmach*ngraph; i++){ 1314 graph[i].newvalue(graph[i].mach, &v, &vmax, 0); 1315 graph[i].update(&graph[i], v, vmax); 1316 } 1317 flushimage(display, 1); 1318 unlockdisplay(display); 1319 sleep(sleeptime); 1320 } 1321 } 1322