stats.c (18479B)
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 <thread.h> 8 #include <mouse.h> 9 #include <keyboard.h> 10 11 typedef struct Graph Graph; 12 typedef struct Machine Machine; 13 14 enum 15 { 16 Ncolor = 6, 17 Ysqueeze = 2, /* vertical squeezing of label text */ 18 Labspace = 2, /* room around label */ 19 Dot = 2, /* height of dot */ 20 Opwid = 5, /* strlen("add ") or strlen("drop ") */ 21 Nlab = 3, /* max number of labels on y axis */ 22 Lablen = 16, /* max length of label */ 23 Lx = 4, /* label tick length */ 24 25 STACK = 8192, 26 XSTACK = 32768 27 }; 28 29 enum 30 { 31 V80211, 32 Vbattery, 33 Vcontext, 34 Vcpu, 35 Vether, 36 Vethererr, 37 Vetherin, 38 Vetherout, 39 Vfault, 40 Vfork, 41 Vidle, 42 Vintr, 43 Vload, 44 Vmem, 45 Vswap, 46 Vsys, 47 Vsyscall, 48 Vuser, 49 Nvalue 50 }; 51 52 char* 53 labels[Nvalue] = 54 { 55 "802.11", 56 "battery", 57 "context", 58 "cpu", 59 "ether", 60 "ethererr", 61 "etherin", 62 "etherout", 63 "fault", 64 "fork", 65 "idle", 66 "intr", 67 "load", 68 "mem", 69 "swap", 70 "sys", 71 "syscall", 72 "user" 73 }; 74 75 struct Graph 76 { 77 int colindex; 78 Rectangle r; 79 int *data; 80 int ndata; 81 char *label; 82 int value; 83 void (*update)(Graph*, long, ulong); 84 Machine *mach; 85 int overflow; 86 Image *overtmp; 87 ulong vmax; 88 }; 89 90 struct Machine 91 { 92 char *name; 93 int fd; 94 int pid; 95 int dead; 96 int absolute[Nvalue]; 97 ulong last[Nvalue]; 98 ulong val[Nvalue][2]; 99 ulong load; 100 ulong nload; 101 }; 102 103 char *menu2str[Nvalue+1]; 104 char xmenu2str[Nvalue+1][40]; 105 106 Menu menu2 = {menu2str, 0}; 107 int present[Nvalue]; 108 Image *cols[Ncolor][3]; 109 Graph *graph; 110 Machine *mach; 111 Font *mediumfont; 112 char *fontname; 113 char *mysysname; 114 char argchars[] = "8bcCeEfiIlmnsw"; 115 int pids[1024]; 116 int parity; /* toggled to avoid patterns in textured background */ 117 int nmach; 118 int ngraph; /* totaly number is ngraph*nmach */ 119 double scale = 1.0; 120 int logscale = 0; 121 int ylabels = 0; 122 int oldsystem = 0; 123 int sleeptime = 1000; 124 int changedvmax; 125 126 Mousectl *mc; 127 Keyboardctl *kc; 128 129 void 130 killall(char *s) 131 { 132 int i; 133 134 for(i=0; i<nmach; i++) 135 if(mach[i].pid) 136 postnote(PNPROC, mach[i].pid, "kill"); 137 threadexitsall(s); 138 } 139 140 void* 141 emalloc(ulong sz) 142 { 143 void *v; 144 v = malloc(sz); 145 if(v == nil) { 146 fprint(2, "stats: out of memory allocating %ld: %r\n", sz); 147 killall("mem"); 148 } 149 memset(v, 0, sz); 150 return v; 151 } 152 153 void* 154 erealloc(void *v, ulong sz) 155 { 156 v = realloc(v, sz); 157 if(v == nil) { 158 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz); 159 killall("mem"); 160 } 161 return v; 162 } 163 164 char* 165 estrdup(char *s) 166 { 167 char *t; 168 if((t = strdup(s)) == nil) { 169 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s); 170 killall("mem"); 171 } 172 return t; 173 } 174 175 void 176 mkcol(int i, int c0, int c1, int c2) 177 { 178 cols[i][0] = allocimagemix(display, c0, DWhite); 179 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1); 180 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2); 181 } 182 183 void 184 colinit(void) 185 { 186 if(fontname) 187 mediumfont = openfont(display, fontname); 188 if(mediumfont == nil) 189 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font"); 190 if(mediumfont == nil) 191 mediumfont = font; 192 193 /* Peach */ 194 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF); 195 /* Aqua */ 196 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue); 197 /* Yellow */ 198 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen); 199 /* Green */ 200 mkcol(3, DPalegreen, DMedgreen, DDarkgreen); 201 /* Blue */ 202 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF); 203 /* Grey */ 204 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF); 205 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF); 206 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF); 207 } 208 209 void 210 label(Point p, int dy, char *text) 211 { 212 char *s; 213 Rune r[2]; 214 int w, maxw, maxy; 215 216 p.x += Labspace; 217 maxy = p.y+dy; 218 maxw = 0; 219 r[1] = '\0'; 220 for(s=text; *s; ){ 221 if(p.y+mediumfont->height-Ysqueeze > maxy) 222 break; 223 w = chartorune(r, s); 224 s += w; 225 w = runestringwidth(mediumfont, r); 226 if(w > maxw) 227 maxw = w; 228 runestring(screen, p, display->black, ZP, mediumfont, r); 229 p.y += mediumfont->height-Ysqueeze; 230 } 231 } 232 233 Point 234 paritypt(int x) 235 { 236 return Pt(x+parity, 0); 237 } 238 239 Point 240 datapoint(Graph *g, int x, ulong v, ulong vmax) 241 { 242 Point p; 243 double y; 244 245 p.x = x; 246 y = ((double)v)/(vmax*scale); 247 if(logscale){ 248 /* 249 * Arrange scale to cover a factor of 1000. 250 * vmax corresponds to the 100 mark. 251 * 10*vmax is the top of the scale. 252 */ 253 if(y <= 0.) 254 y = 0; 255 else{ 256 y = log10(y); 257 /* 1 now corresponds to the top; -2 to the bottom; rescale */ 258 y = (y+2.)/3.; 259 } 260 } 261 p.y = g->r.max.y - Dy(g->r)*y - Dot; 262 if(p.y < g->r.min.y) 263 p.y = g->r.min.y; 264 if(p.y > g->r.max.y-Dot) 265 p.y = g->r.max.y-Dot; 266 return p; 267 } 268 269 void 270 drawdatum(Graph *g, int x, ulong prev, ulong v, ulong vmax) 271 { 272 int c; 273 Point p, q; 274 275 c = g->colindex; 276 p = datapoint(g, x, v, vmax); 277 q = datapoint(g, x, prev, vmax); 278 if(p.y < q.y){ 279 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x)); 280 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP); 281 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP); 282 }else{ 283 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x)); 284 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP); 285 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP); 286 } 287 288 } 289 290 void 291 redraw(Graph *g, int vmax) 292 { 293 int i, c; 294 295 if(vmax != g->vmax){ 296 g->vmax = vmax; 297 changedvmax = 1; 298 } 299 c = g->colindex; 300 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x)); 301 for(i=1; i<Dx(g->r); i++) 302 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax); 303 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax); 304 g->overflow = 0; 305 } 306 307 void 308 update1(Graph *g, long v, ulong vmax) 309 { 310 char buf[32]; 311 int overflow; 312 313 if(v < 0) 314 v = 0; 315 if(vmax != g->vmax){ 316 g->vmax = vmax; 317 changedvmax = 1; 318 } 319 if(g->overflow && g->overtmp!=nil) 320 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min); 321 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y)); 322 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax); 323 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0])); 324 g->data[0] = v; 325 g->overflow = 0; 326 if(logscale) 327 overflow = (v>10*vmax*scale); 328 else 329 overflow = (v>vmax*scale); 330 if(overflow && g->overtmp!=nil){ 331 g->overflow = 1; 332 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min); 333 sprint(buf, "%ld", v); 334 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf); 335 } 336 } 337 338 void 339 usage(void) 340 { 341 fprint(2, "usage: stats [-LY] [-F font] [-O] [-S scale] [-W winsize] [-%s] [machine...]\n", argchars); 342 threadexitsall("usage"); 343 } 344 345 void 346 addgraph(int n) 347 { 348 Graph *g, *ograph; 349 int i, j; 350 static int nadd; 351 352 if(n > Nvalue) 353 abort(); 354 /* avoid two adjacent graphs of same color */ 355 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor) 356 nadd++; 357 ograph = graph; 358 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph)); 359 for(i=0; i<nmach; i++) 360 for(j=0; j<ngraph; j++) 361 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j]; 362 free(ograph); 363 ngraph++; 364 for(i=0; i<nmach; i++){ 365 g = &graph[i*ngraph+(ngraph-1)]; 366 memset(g, 0, sizeof(Graph)); 367 g->value = n; 368 g->label = menu2str[n]+Opwid; 369 g->update = update1; /* no other update functions yet */ 370 g->mach = &mach[i]; 371 g->colindex = nadd%Ncolor; 372 } 373 present[n] = 1; 374 nadd++; 375 } 376 377 void 378 dropgraph(int which) 379 { 380 Graph *ograph; 381 int i, j, n; 382 383 if(which > nelem(menu2str)) 384 abort(); 385 /* convert n to index in graph table */ 386 n = -1; 387 for(i=0; i<ngraph; i++) 388 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){ 389 n = i; 390 break; 391 } 392 if(n < 0){ 393 fprint(2, "stats: internal error can't drop graph\n"); 394 killall("error"); 395 } 396 ograph = graph; 397 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph)); 398 for(i=0; i<nmach; i++){ 399 for(j=0; j<n; j++) 400 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j]; 401 free(ograph[i*ngraph+j].data); 402 freeimage(ograph[i*ngraph+j].overtmp); 403 for(j++; j<ngraph; j++) 404 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j]; 405 } 406 free(ograph); 407 ngraph--; 408 present[which] = 0; 409 } 410 411 int initmach(Machine*, char*); 412 413 int 414 addmachine(char *name) 415 { 416 if(ngraph > 0){ 417 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n"); 418 usage(); 419 } 420 if(mach == nil) 421 nmach = 0; /* a little dance to get us started with local machine by default */ 422 mach = erealloc(mach, (nmach+1)*sizeof(Machine)); 423 memset(mach+nmach, 0, sizeof(Machine)); 424 if (initmach(mach+nmach, name)){ 425 nmach++; 426 return 1; 427 } else 428 return 0; 429 } 430 431 void 432 newvalue(Machine *m, int i, ulong *v, ulong *vmax) 433 { 434 ulong now; 435 436 if(m->last[i] == 0) 437 m->last[i] = m->val[i][0]; 438 439 if(i == Vload){ 440 /* 441 * Invert the ewma to obtain the 5s load statistics. 442 * Ewma is load' = (1884/2048)*load + (164/2048)*last5s, so we do 443 * last5s = (load' - (1884/2048)*load) / (164/2048). 444 */ 445 if(++m->nload%5 == 0){ 446 now = m->val[i][0]; 447 m->load = (now - (((vlong)m->last[i]*1884)/2048)) * 2048 / 164; 448 m->last[i] = now; 449 } 450 *v = m->load; 451 *vmax = m->val[i][1]; 452 }else if(m->absolute[i]){ 453 *v = m->val[i][0]; 454 *vmax = m->val[i][1]; 455 }else{ 456 now = m->val[i][0]; 457 *v = (vlong)((now - m->last[i])*sleeptime)/1000; 458 m->last[i] = now; 459 *vmax = m->val[i][1]; 460 } 461 if(*vmax == 0) 462 *vmax = 1; 463 } 464 465 void 466 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np) 467 { 468 int j; 469 ulong vmax; 470 471 vmax = g->vmax; 472 if(logscale){ 473 for(j=1; j<=2; j++) 474 sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.); 475 *np = 2; 476 }else{ 477 for(j=1; j<=3; j++) 478 sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0); 479 *np = 3; 480 } 481 } 482 483 int 484 labelwidth(void) 485 { 486 int i, j, n, w, maxw; 487 char strs[Nlab][Lablen]; 488 489 maxw = 0; 490 for(i=0; i<ngraph; i++){ 491 /* choose value for rightmost graph */ 492 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n); 493 for(j=0; j<n; j++){ 494 w = stringwidth(mediumfont, strs[j]); 495 if(w > maxw) 496 maxw = w; 497 } 498 } 499 return maxw; 500 } 501 502 void 503 resize(void) 504 { 505 int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab; 506 Graph *g; 507 Rectangle machr, r; 508 ulong v, vmax; 509 char buf[128], labs[Nlab][Lablen]; 510 511 draw(screen, screen->r, display->white, nil, ZP); 512 513 /* label left edge */ 514 x = screen->r.min.x; 515 y = screen->r.min.y + Labspace+mediumfont->height+Labspace; 516 dy = (screen->r.max.y - y)/ngraph; 517 dx = Labspace+stringwidth(mediumfont, "0")+Labspace; 518 startx = x+dx+1; 519 starty = y; 520 for(i=0; i<ngraph; i++,y+=dy){ 521 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP); 522 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x)); 523 label(Pt(x, y), dy, graph[i].label); 524 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP); 525 } 526 527 /* label top edge */ 528 dx = (screen->r.max.x - startx)/nmach; 529 for(x=startx, i=0; i<nmach; i++,x+=dx){ 530 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP); 531 j = dx/stringwidth(mediumfont, "0"); 532 /* n = mach[i].nproc; */ 533 n = 1; 534 if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */ 535 j -= 3+(n>10)+(n>100); 536 if(j <= 0) 537 j = 1; 538 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n); 539 }else 540 snprint(buf, sizeof buf, "%.*s", j, mach[i].name); 541 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf); 542 } 543 544 maxx = screen->r.max.x; 545 546 /* label right, if requested */ 547 if(ylabels && dy>Nlab*(mediumfont->height+1)){ 548 wid = labelwidth(); 549 if(wid < (maxx-startx)-30){ 550 /* else there's not enough room */ 551 maxx -= 1+Lx+wid; 552 draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP); 553 y = starty; 554 for(j=0; j<ngraph; j++, y+=dy){ 555 /* choose value for rightmost graph */ 556 g = &graph[ngraph*(nmach-1)+j]; 557 labelstrs(g, labs, &nlab); 558 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1); 559 if(j == ngraph-1) 560 r.max.y = screen->r.max.y; 561 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x)); 562 for(k=0; k<nlab; k++){ 563 ly = y + (dy*(nlab-k)/(nlab+1)); 564 draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP); 565 ly -= mediumfont->height/2; 566 string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]); 567 } 568 } 569 } 570 } 571 572 /* create graphs */ 573 for(i=0; i<nmach; i++){ 574 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y); 575 if(i < nmach-1) 576 machr.max.x = startx+(i+1)*dx - 1; 577 y = starty; 578 for(j=0; j<ngraph; j++, y+=dy){ 579 g = &graph[i*ngraph+j]; 580 /* allocate data */ 581 ondata = g->ndata; 582 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */ 583 g->data = erealloc(g->data, g->ndata*sizeof(ulong)); 584 if(g->ndata > ondata) 585 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong)); 586 /* set geometry */ 587 g->r = machr; 588 g->r.min.y = y; 589 g->r.max.y = y+dy - 1; 590 if(j == ngraph-1) 591 g->r.max.y = screen->r.max.y; 592 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x)); 593 g->overflow = 0; 594 r = g->r; 595 r.max.y = r.min.y+mediumfont->height; 596 r.max.x = r.min.x+stringwidth(mediumfont, "9999999"); 597 freeimage(g->overtmp); 598 g->overtmp = nil; 599 if(r.max.x <= g->r.max.x) 600 g->overtmp = allocimage(display, r, screen->chan, 0, -1); 601 newvalue(g->mach, g->value, &v, &vmax); 602 redraw(g, vmax); 603 } 604 } 605 606 flushimage(display, 1); 607 } 608 609 void 610 eresized(int new) 611 { 612 lockdisplay(display); 613 if(new && getwindow(display, Refnone) < 0) { 614 fprint(2, "stats: can't reattach to window\n"); 615 killall("reattach"); 616 } 617 resize(); 618 unlockdisplay(display); 619 } 620 621 void 622 mousethread(void *v) 623 { 624 Mouse m; 625 int i; 626 627 USED(v); 628 629 while(readmouse(mc) == 0){ 630 m = mc->m; 631 if(m.buttons == 4){ 632 for(i=0; i<Nvalue; i++) 633 if(present[i]) 634 memmove(menu2str[i], "drop ", Opwid); 635 else 636 memmove(menu2str[i], "add ", Opwid); 637 lockdisplay(display); 638 i = menuhit(3, mc, &menu2, nil); 639 if(i >= 0){ 640 if(!present[i]) 641 addgraph(i); 642 else if(ngraph > 1) 643 dropgraph(i); 644 resize(); 645 } 646 unlockdisplay(display); 647 } 648 } 649 } 650 651 void 652 resizethread(void *v) 653 { 654 USED(v); 655 656 while(recv(mc->resizec, 0) == 1){ 657 lockdisplay(display); 658 if(getwindow(display, Refnone) < 0) 659 sysfatal("attach to window: %r"); 660 resize(); 661 unlockdisplay(display); 662 } 663 } 664 665 void 666 keyboardthread(void *v) 667 { 668 Rune r; 669 670 while(recv(kc->c, &r) == 1) 671 if(r == 0x7F || r == 'q') 672 killall("quit"); 673 } 674 675 void machproc(void*); 676 void updateproc(void*); 677 678 int 679 threadmaybackground(void) 680 { 681 return 1; 682 } 683 684 void 685 threadmain(int argc, char *argv[]) 686 { 687 int i, j; 688 char *s; 689 ulong nargs; 690 char args[100]; 691 692 nmach = 1; 693 mysysname = sysname(); 694 if(mysysname == nil){ 695 fprint(2, "stats: can't find sysname: %r\n"); 696 threadexitsall("sysname"); 697 } 698 699 nargs = 0; 700 ARGBEGIN{ 701 case 'T': 702 s = ARGF(); 703 if(s == nil) 704 usage(); 705 i = atoi(s); 706 if(i > 0) 707 sleeptime = 1000*i; 708 break; 709 case 'S': 710 s = ARGF(); 711 if(s == nil) 712 usage(); 713 scale = atof(s); 714 if(scale <= 0.) 715 usage(); 716 break; 717 case 'L': 718 logscale++; 719 break; 720 case 'F': 721 fontname = EARGF(usage()); 722 break; 723 case 'Y': 724 ylabels++; 725 break; 726 case 'O': 727 oldsystem = 1; 728 break; 729 case 'W': 730 winsize = EARGF(usage()); 731 break; 732 default: 733 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil) 734 usage(); 735 args[nargs++] = ARGC(); 736 }ARGEND 737 738 for(i=0; i<Nvalue; i++){ 739 menu2str[i] = xmenu2str[i]; 740 snprint(xmenu2str[i], sizeof xmenu2str[i], "add %s", labels[i]); 741 } 742 743 if(argc == 0){ 744 mach = emalloc(nmach*sizeof(Machine)); 745 initmach(&mach[0], mysysname); 746 }else{ 747 for(i=j=0; i<argc; i++) 748 addmachine(argv[i]); 749 } 750 751 for(i=0; i<nmach; i++) 752 proccreate(machproc, &mach[i], STACK); 753 754 for(i=0; i<nargs; i++) 755 switch(args[i]){ 756 default: 757 fprint(2, "stats: internal error: unknown arg %c\n", args[i]); 758 usage(); 759 case '8': 760 addgraph(V80211); 761 break; 762 case 'b': 763 addgraph(Vbattery); 764 break; 765 case 'c': 766 addgraph(Vcontext); 767 break; 768 case 'C': 769 addgraph(Vcpu); 770 break; 771 case 'e': 772 addgraph(Vether); 773 break; 774 case 'E': 775 addgraph(Vetherin); 776 addgraph(Vetherout); 777 break; 778 case 'f': 779 addgraph(Vfault); 780 break; 781 case 'i': 782 addgraph(Vintr); 783 break; 784 case 'I': 785 addgraph(Vload); 786 addgraph(Vidle); 787 break; 788 case 'l': 789 addgraph(Vload); 790 break; 791 case 'm': 792 addgraph(Vmem); 793 break; 794 case 'n': 795 addgraph(Vetherin); 796 addgraph(Vetherout); 797 addgraph(Vethererr); 798 break; 799 case 's': 800 addgraph(Vsyscall); 801 break; 802 case 'w': 803 addgraph(Vswap); 804 break; 805 } 806 807 if(ngraph == 0) 808 addgraph(Vload); 809 810 for(i=0; i<nmach; i++) 811 for(j=0; j<ngraph; j++) 812 graph[i*ngraph+j].mach = &mach[i]; 813 814 if(initdraw(0, nil, "stats") < 0) 815 sysfatal("initdraw: %r"); 816 colinit(); 817 if((mc = initmouse(nil, screen)) == nil) 818 sysfatal("initmouse: %r"); 819 if((kc = initkeyboard(nil)) == nil) 820 sysfatal("initkeyboard: %r"); 821 822 display->locking = 1; 823 threadcreate(keyboardthread, nil, XSTACK); 824 threadcreate(mousethread, nil, XSTACK); 825 threadcreate(resizethread, nil, XSTACK); 826 proccreate(updateproc, nil, XSTACK); 827 resize(); 828 unlockdisplay(display); 829 } 830 831 void 832 updateproc(void *z) 833 { 834 int i; 835 ulong v, vmax; 836 837 USED(z); 838 for(;;){ 839 parity = 1-parity; 840 lockdisplay(display); 841 for(i=0; i<nmach*ngraph; i++){ 842 newvalue(graph[i].mach, graph[i].value, &v, &vmax); 843 graph[i].update(&graph[i], v, vmax); 844 } 845 if(changedvmax){ 846 changedvmax = 0; 847 resize(); 848 } 849 flushimage(display, 1); 850 unlockdisplay(display); 851 sleep(sleeptime); 852 } 853 } 854 855 void 856 machproc(void *v) 857 { 858 char buf[256], *f[4], *p; 859 int i, n, t; 860 Machine *m; 861 862 m = v; 863 t = 0; 864 for(;;){ 865 n = read(m->fd, buf+t, sizeof buf-t); 866 m->dead = 0; 867 if(n <= 0) 868 break; 869 t += n; 870 while((p = memchr(buf, '\n', t)) != nil){ 871 *p++ = 0; 872 n = tokenize(buf, f, nelem(f)); 873 if(n >= 3){ 874 for(i=0; i<Nvalue; i++){ 875 if(strcmp(labels[i], f[0]) == 0){ 876 if(*f[1] == '='){ 877 m->absolute[i] = 1; 878 f[1]++; 879 } 880 m->val[i][0] = strtoul(f[1], 0, 0); 881 m->val[i][1] = strtoul(f[2], 0, 0); 882 } 883 } 884 } 885 t -= (p-buf); 886 memmove(buf, p, t); 887 } 888 } 889 if(m->fd){ 890 close(m->fd); 891 m->fd = -1; 892 } 893 if(m->pid){ 894 postnote(PNPROC, m->pid, "kill"); 895 m->pid = 0; 896 } 897 } 898 899 int 900 initmach(Machine *m, char *name) 901 { 902 char *args[5], *q; 903 int p[2], kfd[3], pid; 904 905 m->name = name; 906 if(strcmp(name, mysysname) == 0) 907 name = nil; 908 909 if(pipe(p) < 0) 910 sysfatal("pipe: %r"); 911 912 memset(args, 0, sizeof args); 913 args[0] = "auxstats"; 914 if(name){ 915 args[1] = name; 916 if((q = strchr(name, ':')) != nil){ 917 *q++ = 0; 918 args[2] = q; 919 } 920 } 921 kfd[0] = open("/dev/null", OREAD); 922 kfd[1] = p[1]; 923 kfd[2] = dup(2, -1); 924 if((pid = threadspawn(kfd, "auxstats", args)) < 0){ 925 fprint(2, "spawn: %r\n"); 926 close(kfd[0]); 927 close(p[0]); 928 close(p[1]); 929 return 0; 930 } 931 m->fd = p[0]; 932 m->pid = pid; 933 if((q = strchr(m->name, '.')) != nil) 934 *q = 0; 935 return 1; 936 }