Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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