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