plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }