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