xref: /original-bsd/usr.bin/systat/vmstat.c (revision 5e36add1)
1 /*
2  * Copyright (c) 1983, 1989 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)vmstat.c	5.12 (Berkeley) 06/24/90";
9 #endif not lint
10 
11 /*
12  * Cursed vmstat -- from Robert Elz.
13  */
14 
15 #include "systat.h"
16 
17 #include <ctype.h>
18 #include <utmp.h>
19 
20 #include <sys/vm.h>
21 #include <sys/buf.h>
22 #include <sys/stat.h>
23 #include <sys/user.h>
24 #include <sys/proc.h>
25 #include <sys/namei.h>
26 
27 #include <machine/pte.h>
28 #include <paths.h>
29 
30 static	int ut;
31 
32 WINDOW *
33 openkre()
34 {
35 
36 	ut = open(_PATH_UTMP, O_RDONLY);
37 	if (ut < 0)
38 		error("No utmp");
39 	return (stdscr);
40 }
41 
42 closekre(w)
43 	WINDOW *w;
44 {
45 
46 	(void) close(ut);
47 	if (w == NULL)
48 		return;
49 	wclear(w);
50 	wrefresh(w);
51 }
52 
53 long	time();
54 float	cputime();
55 struct	utmp utmp;
56 
57 static struct nlist name[] = {
58 	{ "_cp_time" },
59 #define X_CPTIME	0
60 	{ "_rate" },
61 #define X_RATE		1
62 	{ "_total" },
63 #define X_TOTAL		2
64 	{ "_proc" },
65 #define X_PROC		3
66 	{ "_nproc" },
67 #define X_NPROC		4
68 	{ "_sum" },
69 #define X_SUM		5
70 	{ "_dk_busy" },
71 #define	X_DK_BUSY	6
72 	{ "_dk_time" },
73 #define	X_DK_TIME	7
74 	{ "_dk_xfer" },
75 #define	X_DK_XFER	8
76 	{ "_dk_wds" },
77 #define	X_DK_WDS	9
78 	{ "_tk_nin" },
79 #define	X_TK_NIN	10
80 	{ "_tk_nout" },
81 #define	X_TK_NOUT	11
82 	{ "_dk_seek" },
83 #define	X_DK_SEEK	12
84 	{ "_nchstats" },
85 #define	X_NCHSTATS	13
86 	{ "_intrnames" },
87 #define	X_INTRNAMES	14
88 	{ "_eintrnames" },
89 #define	X_EINTRNAMES	15
90 	{ "_intrcnt" },
91 #define	X_INTRCNT	16
92 	{ "_eintrcnt" },
93 #define	X_EINTRCNT	17
94 	{ "" },
95 };
96 
97 static struct Info {
98 	long	time[CPUSTATES];
99 	struct	vmmeter Rate;
100 	struct	vmtotal Total;
101 	struct	vmmeter Sum;
102 	struct	forkstat Forkstat;
103 	long	*dk_time;
104 	long	*dk_wds;
105 	long	*dk_seek;
106 	long	*dk_xfer;
107 	int	dk_busy;
108 	long	tk_nin;
109 	long	tk_nout;
110 	struct	nchstats nchstats;
111 	long	nchcount;
112 	long	*intrcnt;
113 } s, s1, s2, z;
114 
115 #define total s.Total
116 #define sum s.Sum
117 #define sumold s1.Sum
118 #define rate s.Rate
119 #define	nchtotal s.nchstats
120 #define	oldnchtotal s1.nchstats
121 #define oldrate s1.Rate
122 
123 static	char buf[26];
124 static	time_t t;
125 static	double etime;
126 static	float hertz;
127 static	int nintr;
128 static	long *intrloc;
129 static	char **intrname;
130 static	int nextintsrow;
131 
132 static	enum state { BOOT, TIME, RUN } state = TIME;
133 
134 /*
135  * These constants define where the major pieces are laid out
136  */
137 #define STATROW		 0	/* uses 1 row and 68 cols */
138 #define STATCOL		 2
139 #define MEMROW		 2	/* uses 4 rows and 31 cols */
140 #define MEMCOL		 0
141 #define PAGEROW		 2	/* uses 4 rows and 26 cols */
142 #define PAGECOL		36
143 #define INTSROW		 2	/* uses all rows to bottom and 17 cols */
144 #define INTSCOL		63
145 #define PROCSROW	 7	/* uses 2 rows and 20 cols */
146 #define PROCSCOL	 0
147 #define VMSTATROW	 7	/* uses 2 rows and 26 cols */
148 #define VMSTATCOL	25
149 #define FILLSTATROW	 7	/* uses 6 rows and 10 cols */
150 #define FILLSTATCOL	53
151 #define GRAPHROW	10	/* uses 3 rows and 51 cols */
152 #define GRAPHCOL	 0
153 #define NAMEIROW	14	/* uses 3 rows and 38 cols */
154 #define NAMEICOL	 0
155 #define GENSTATROW	14	/* uses 9 rows and 11 cols */
156 #define GENSTATCOL	52
157 #define DISKROW		18	/* uses 5 rows and 50 cols (for 9 drives) */
158 #define DISKCOL		 0
159 
160 #define	DRIVESPACE	 9	/* max # for space */
161 
162 #if DK_NDRIVE > DRIVESPACE
163 #define	MAXDRIVES	DRIVESPACE	 /* max # to display */
164 #else
165 #define	MAXDRIVES	DK_NDRIVE	 /* max # to display */
166 #endif
167 
168 initkre()
169 {
170 	char *intrnamebuf, *cp;
171 	int i;
172 	static int once = 0;
173 
174 	if (name[0].n_type == 0) {
175 		nlist(_PATH_UNIX,name);
176 		if (name[0].n_type == 0) {
177 			error("No namelist");
178 			return(0);
179 		}
180 	}
181 	hertz = phz ? phz : hz;
182 	if (! dkinit())
183 		return(0);
184 	if (dk_ndrive && !once) {
185 #define	allocate(e, t) \
186     s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
187     s1./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
188     s2./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
189     z./**/e = (t *)calloc(dk_ndrive, sizeof (t));
190 		allocate(dk_time, long);
191 		allocate(dk_wds, long);
192 		allocate(dk_seek, long);
193 		allocate(dk_xfer, long);
194 		once = 1;
195 #undef allocate
196 	}
197 	if (nintr == 0) {
198 		nintr = (name[X_EINTRCNT].n_value -
199 			name[X_INTRCNT].n_value) / sizeof (long);
200 		intrloc = (long *) calloc(nintr, sizeof (long));
201 		intrname = (char **) calloc(nintr, sizeof (long));
202 		intrnamebuf = malloc(name[X_EINTRNAMES].n_value -
203 			name[X_INTRNAMES].n_value);
204 		if (intrnamebuf == 0 || intrname == 0 || intrloc == 0) {
205 			error("Out of memory\n");
206 			if (intrnamebuf)
207 				free(intrnamebuf);
208 			if (intrname)
209 				free(intrname);
210 			if (intrloc)
211 				free(intrloc);
212 			nintr = 0;
213 			return(0);
214 		}
215 		lseek(kmem, (long)name[X_INTRNAMES].n_value, L_SET);
216 		read(kmem, intrnamebuf, name[X_EINTRNAMES].n_value -
217 			name[X_INTRNAMES].n_value);
218 		for (cp = intrnamebuf, i = 0; i < nintr; i++) {
219 			intrname[i] = cp;
220 			cp += strlen(cp) + 1;
221 		}
222 		nextintsrow = INTSROW + 2;
223 		allocinfo(&s);
224 		allocinfo(&s1);
225 		allocinfo(&s2);
226 		allocinfo(&z);
227 	}
228 	getinfo(&s2, RUN);
229 	copyinfo(&s2, &s1);
230 	return(1);
231 }
232 
233 fetchkre()
234 {
235 	time_t now;
236 
237 	time(&now);
238 	strcpy(buf, ctime(&now));
239 	buf[16] = '\0';
240 	getinfo(&s, state);
241 }
242 
243 labelkre()
244 {
245 	register i, j;
246 
247 	clear();
248 	mvprintw(STATROW, STATCOL + 4, "users    Load");
249 	mvprintw(MEMROW, MEMCOL, "Mem     REAL     VIRTUAL");
250 	mvprintw(MEMROW + 1, MEMCOL, "      Tot Text   Tot Text");
251 	mvprintw(MEMROW + 2, MEMCOL, "Act");
252 	mvprintw(MEMROW + 3, MEMCOL, "All");
253 
254 	mvprintw(MEMROW + 1, MEMCOL + 28, "Free");
255 
256 	mvprintw(PAGEROW, PAGECOL,     "        PAGING   SWAPPING ");
257 	mvprintw(PAGEROW + 1, PAGECOL, "        in  out   in  out ");
258 	mvprintw(PAGEROW + 2, PAGECOL, "count");
259 	mvprintw(PAGEROW + 3, PAGECOL, "pages");
260 
261 	mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
262 	mvprintw(INTSROW + 1, INTSCOL + 9, "total");
263 
264 	mvprintw(GENSTATROW, GENSTATCOL + 8, "Csw");
265 	mvprintw(GENSTATROW + 1, GENSTATCOL + 8, "Trp");
266 	mvprintw(GENSTATROW + 2, GENSTATCOL + 8, "Sys");
267 	mvprintw(GENSTATROW + 3, GENSTATCOL + 8, "Int");
268 	mvprintw(GENSTATROW + 4, GENSTATCOL + 8, "Pdm");
269 	mvprintw(GENSTATROW + 5, GENSTATCOL + 8, "Sof");
270 	mvprintw(GENSTATROW + 6, GENSTATCOL + 8, "Flt");
271 	mvprintw(GENSTATROW + 7, GENSTATCOL + 8, "Scn");
272 	mvprintw(GENSTATROW + 8, GENSTATCOL + 8, "Rev");
273 
274 	mvprintw(VMSTATROW, VMSTATCOL, "Rec It F/S F/F RFL Fre SFr");
275 
276 	mvprintw(FILLSTATROW, FILLSTATCOL + 7, " zf");
277 	mvprintw(FILLSTATROW + 1, FILLSTATCOL + 7, "nzf");
278 	mvprintw(FILLSTATROW + 2, FILLSTATCOL + 7, "%%zf");
279 	mvprintw(FILLSTATROW + 3, FILLSTATCOL + 7, " xf");
280 	mvprintw(FILLSTATROW + 4, FILLSTATCOL + 7, "nxf");
281 	mvprintw(FILLSTATROW + 5, FILLSTATCOL + 7, "%%xf");
282 
283 	mvprintw(GRAPHROW, GRAPHCOL,
284 		"    . %% Sys    . %% User    . %% Nice    . %% Idle");
285 	mvprintw(PROCSROW, PROCSCOL, "Procs  r  p  d  s  w");
286 	mvprintw(GRAPHROW + 1, GRAPHCOL,
287 		"|    |    |    |    |    |    |    |    |    |    |");
288 
289 	mvprintw(NAMEIROW, NAMEICOL, "Namei         Sys-cache     Proc-cache");
290 	mvprintw(NAMEIROW + 1, NAMEICOL,
291 		"    Calls     hits    %%     hits     %%");
292 	mvprintw(DISKROW, DISKCOL, "Discs");
293 	mvprintw(DISKROW + 1, DISKCOL, "seeks");
294 	mvprintw(DISKROW + 2, DISKCOL, "xfers");
295 	mvprintw(DISKROW + 3, DISKCOL, " blks");
296 	mvprintw(DISKROW + 4, DISKCOL, " msps");
297 	j = 0;
298 	for (i = 0; i < dk_ndrive && j < MAXDRIVES; i++)
299 		if (dk_select[i]) {
300 			mvprintw(DISKROW, DISKCOL + 5 + 5 * j,
301 				"  %3.3s", dr_name[j]);
302 			j++;
303 		}
304 	for (i = 0; i < nintr; i++) {
305 		if (intrloc[i] == 0)
306 			continue;
307 		mvprintw(intrloc[i], INTSCOL + 9, "%-8.8s", intrname[i]);
308 	}
309 }
310 
311 #define X(fld)	{t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;}
312 #define Y(fld)	{t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;}
313 #define Z(fld)	{t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \
314 	if(state == TIME) s1.nchstats.fld = t;}
315 #define MAXFAIL 5
316 
317 static	char cpuchar[CPUSTATES] = { '=' , '>', '-', ' ' };
318 static	char cpuorder[CPUSTATES] = { CP_SYS, CP_USER, CP_NICE, CP_IDLE };
319 
320 showkre()
321 {
322 	float f1, f2;
323 	int psiz, inttotal;
324 	int i, l, c;
325 	static int failcnt = 0;
326 
327 	for (i = 0; i < dk_ndrive; i++) {
328 		X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time);
329 	}
330 	Y(tk_nin); Y(tk_nout);
331 	etime = 0;
332 	for(i = 0; i < CPUSTATES; i++) {
333 		X(time);
334 		etime += s.time[i];
335 	}
336 	if (etime < 5.0) {	/* < 5 ticks - ignore this trash */
337 		if (failcnt++ >= MAXFAIL) {
338 			clear();
339 			mvprintw(2, 10, "The alternate system clock has died!");
340 			mvprintw(3, 10, "Reverting to ``pigs'' display.");
341 			move(CMDLINE, 0);
342 			refresh();
343 			failcnt = 0;
344 			sleep(5);
345 			command("pigs");
346 		}
347 		return;
348 	}
349 	failcnt = 0;
350 	etime /= hertz;
351 	inttotal = 0;
352 	for (i = 0; i < nintr; i++) {
353 		if (s.intrcnt[i] == 0)
354 			continue;
355 		if (intrloc[i] == 0) {
356 			if (nextintsrow == LINES)
357 				continue;
358 			intrloc[i] = nextintsrow++;
359 			mvprintw(intrloc[i], INTSCOL + 9, "%-8.8s",
360 				intrname[i]);
361 		}
362 		X(intrcnt);
363 		l = (int)((float)s.intrcnt[i]/etime + 0.5);
364 		inttotal += l;
365 		putint(l, intrloc[i], INTSCOL, 8);
366 	}
367 	putint(inttotal, INTSROW + 1, INTSCOL, 8);
368 	Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
369 	Z(ncs_long); Z(ncs_pass2); Z(ncs_2passes);
370 	s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
371 	    nchtotal.ncs_miss + nchtotal.ncs_long;
372 	if (state == TIME)
373 		s1.nchcount = s.nchcount;
374 
375 	psiz = 0;
376 	f2 = 0.0;
377 	for (c = 0; c < CPUSTATES; c++) {
378 		i = cpuorder[c];
379 		f1 = cputime(i);
380 		f2 += f1;
381 		l = (int) ((f2 + 1.0) / 2.0) - psiz;
382 		if (c == 0)
383 			putfloat(f1, GRAPHROW, GRAPHCOL + 1, 5, 1, 0);
384 		else
385 			putfloat(f1, GRAPHROW, GRAPHCOL + 12 * c,
386 				5, 1, 0);
387 		move(GRAPHROW + 2, psiz);
388 		psiz += l;
389 		while (l-- > 0)
390 			addch(cpuchar[c]);
391 	}
392 
393 	putint(ucount(), STATROW, STATCOL, 3);
394 	putfloat(avenrun[0], STATROW, STATCOL + 17, 6, 2, 0);
395 	putfloat(avenrun[1], STATROW, STATCOL + 23, 6, 2, 0);
396 	putfloat(avenrun[2], STATROW, STATCOL + 29, 6, 2, 0);
397 	mvaddstr(STATROW, STATCOL + 53, buf);
398 #define pgtokb(pg)	((pg) * NBPG / 1024)
399 	putint(pgtokb(total.t_arm), MEMROW + 2, MEMCOL + 4, 5);
400 	putint(pgtokb(total.t_armtxt), MEMROW + 2, MEMCOL + 9, 5);
401 	putint(pgtokb(total.t_avm), MEMROW + 2, MEMCOL + 14, 6);
402 	putint(pgtokb(total.t_avmtxt), MEMROW + 2, MEMCOL + 20, 5);
403 	putint(pgtokb(total.t_rm), MEMROW + 3, MEMCOL + 4, 5);
404 	putint(pgtokb(total.t_rmtxt), MEMROW + 3, MEMCOL + 9, 5);
405 	putint(pgtokb(total.t_vm), MEMROW + 3, MEMCOL + 14, 6);
406 	putint(pgtokb(total.t_vmtxt), MEMROW + 3, MEMCOL + 20, 5);
407 	putint(pgtokb(total.t_free), MEMROW + 2, MEMCOL + 27, 5);
408 	putint(total.t_rq, PROCSROW + 1, PROCSCOL + 5, 3);
409 	putint(total.t_pw, PROCSROW + 1, PROCSCOL + 8, 3);
410 	putint(total.t_dw, PROCSROW + 1, PROCSCOL + 11, 3);
411 	putint(total.t_sl, PROCSROW + 1, PROCSCOL + 14, 3);
412 	putint(total.t_sw, PROCSROW + 1, PROCSCOL + 17, 3);
413 	putrate(rate.v_swtch, oldrate.v_swtch,
414 		GENSTATROW, GENSTATCOL, 7);
415 	putrate(rate.v_trap, oldrate.v_trap,
416 		GENSTATROW + 1, GENSTATCOL, 7);
417 	putrate(rate.v_syscall, oldrate.v_syscall,
418 		GENSTATROW + 2, GENSTATCOL, 7);
419 	putrate(rate.v_intr, oldrate.v_intr,
420 		GENSTATROW + 3, GENSTATCOL, 7);
421 	putrate(rate.v_pdma, oldrate.v_pdma,
422 		GENSTATROW + 4, GENSTATCOL, 7);
423 	putrate(rate.v_soft, oldrate.v_soft,
424 		GENSTATROW + 5, GENSTATCOL, 7);
425 	putrate(rate.v_faults, oldrate.v_faults,
426 		GENSTATROW + 6, GENSTATCOL, 7);
427 	putrate(rate.v_scan, oldrate.v_scan,
428 		GENSTATROW + 7, GENSTATCOL, 7);
429 	putrate(rate.v_rev, oldrate.v_rev,
430 		GENSTATROW + 8, GENSTATCOL, 7);
431 	putrate(rate.v_pgin, oldrate.v_pgin, PAGEROW + 2,
432 		PAGECOL + 5, 5);
433 	putrate(rate.v_pgout, oldrate.v_pgout, PAGEROW + 2,
434 		PAGECOL + 10, 5);
435 	putrate(rate.v_swpin, oldrate.v_swpin, PAGEROW + 2,
436 		PAGECOL + 15, 5);
437 	putrate(rate.v_swpout, oldrate.v_swpout, PAGEROW + 2,
438 		PAGECOL + 20, 5);
439 	putrate(rate.v_pgpgin, oldrate.v_pgpgin, PAGEROW + 3,
440 		PAGECOL + 5, 5);
441 	putrate(rate.v_pgpgout, oldrate.v_pgpgout, PAGEROW + 3,
442 		PAGECOL + 10, 5);
443 	putrate(rate.v_pswpin, oldrate.v_pswpin, PAGEROW + 3,
444 		PAGECOL + 15, 5);
445 	putrate(rate.v_pswpout, oldrate.v_pswpout, PAGEROW + 3,
446 		PAGECOL + 20, 5);
447 
448 	putrate(rate.v_pgrec, oldrate.v_pgrec, VMSTATROW + 1, VMSTATCOL, 3);
449 	putrate(rate.v_intrans, oldrate.v_intrans, VMSTATROW + 1,
450 		VMSTATCOL + 4, 2);
451 	putrate(rate.v_xsfrec, oldrate.v_xsfrec, VMSTATROW + 1,
452 		VMSTATCOL + 7, 3);
453 	putrate(rate.v_xifrec, oldrate.v_xifrec, VMSTATROW + 1,
454 		VMSTATCOL + 11, 3);
455 	putrate(rate.v_pgfrec, oldrate.v_pgfrec, VMSTATROW + 1,
456 		VMSTATCOL + 15, 3);
457 	putrate(rate.v_dfree, oldrate.v_dfree, VMSTATROW + 1,
458 		VMSTATCOL + 19, 3);
459 	putrate(rate.v_seqfree, oldrate.v_seqfree, VMSTATROW + 1,
460 		VMSTATCOL + 23, 3);
461 
462 	putrate(rate.v_zfod, oldrate.v_zfod, FILLSTATROW, FILLSTATCOL, 6);
463 	putrate(rate.v_nzfod, oldrate.v_nzfod, FILLSTATROW + 1, FILLSTATCOL, 6);
464 	putrate(rate.v_exfod, oldrate.v_exfod, FILLSTATROW + 3,
465 		FILLSTATCOL, 6);
466 	putrate(rate.v_nexfod, oldrate.v_nexfod, FILLSTATROW + 4,
467 		FILLSTATCOL, 6);
468 	putfloat (
469 		rate.v_nzfod == 0 ?
470 			0.0
471 		: state != RUN ?
472 			( 100.0 * rate.v_zfod / rate.v_nzfod )
473 		: rate.v_nzfod == oldrate.v_nzfod ?
474 			0.0
475 		:
476 			( 100.0 * (rate.v_zfod-oldrate.v_zfod)
477 			/ (rate.v_nzfod-oldrate.v_nzfod) )
478 		, FILLSTATROW + 2
479 		, FILLSTATCOL
480 		, 6
481 		, 2
482 		, 1
483 	);
484 	putfloat (
485 		rate.v_nexfod == 0 ?
486 			0.0
487 		: state != RUN ?
488 			( 100.0 * rate.v_exfod / rate.v_nexfod )
489 		: rate.v_nexfod == oldrate.v_nexfod ?
490 			0.0
491 		:
492 			( 100.0 * (rate.v_exfod-oldrate.v_exfod)
493 			/ (rate.v_nexfod-oldrate.v_nexfod) )
494 		, FILLSTATROW + 5
495 		, FILLSTATCOL
496 		, 6
497 		, 2
498 		, 1
499 	);
500 
501 	mvprintw(DISKROW,DISKCOL+5,"                              ");
502 	for (i = 0, c = 0; i < dk_ndrive && c < MAXDRIVES; i++)
503 		if (dk_select[i]) {
504 			mvprintw(DISKROW, DISKCOL + 5 + 5 * c,
505 				"  %3.3s", dr_name[i]);
506 			dinfo(i, ++c);
507 		}
508 	putint(s.nchcount, NAMEIROW + 2, NAMEICOL, 9);
509 	putint(nchtotal.ncs_goodhits, NAMEIROW + 2, NAMEICOL + 9, 9);
510 #define nz(x)	((x) ? (x) : 1)
511 	putfloat(nchtotal.ncs_goodhits * 100.0 / nz(s.nchcount),
512 	   NAMEIROW + 2, NAMEICOL + 19, 4, 0, 1);
513 	putint(nchtotal.ncs_pass2, NAMEIROW + 2, NAMEICOL + 23, 9);
514 	putfloat(nchtotal.ncs_pass2 * 100.0 / nz(s.nchcount),
515 	   NAMEIROW + 2, NAMEICOL + 34, 4, 0, 1);
516 #undef nz
517 }
518 
519 cmdkre(cmd, args)
520 	char *cmd, *args;
521 {
522 
523 	if (prefix(cmd, "run")) {
524 		copyinfo(&s2, &s1);
525 		state = RUN;
526 		return (1);
527 	}
528 	if (prefix(cmd, "boot")) {
529 		state = BOOT;
530 		copyinfo(&z, &s1);
531 		return (1);
532 	}
533 	if (prefix(cmd, "time")) {
534 		state = TIME;
535 		return (1);
536 	}
537 	if (prefix(cmd, "zero")) {
538 		if (state == RUN)
539 			getinfo(&s1, RUN);
540 		return (1);
541 	}
542 	return (dkcmd(cmd, args));
543 }
544 
545 /* calculate number of users on the system */
546 static
547 ucount()
548 {
549 	register int nusers = 0;
550 
551 	if (ut < 0)
552 		return (0);
553 	while (read(ut, &utmp, sizeof(utmp)))
554 		if (utmp.ut_name[0] != '\0')
555 			nusers++;
556 
557 	lseek(ut, 0L, L_SET);
558 	return (nusers);
559 }
560 
561 static float
562 cputime(indx)
563 	int indx;
564 {
565 	double t;
566 	register i;
567 
568 	t = 0;
569 	for (i = 0; i < CPUSTATES; i++)
570 		t += s.time[i];
571 	if (t == 0.0)
572 		t = 1.0;
573 	return (s.time[indx] * 100.0 / t);
574 }
575 
576 static
577 putrate(r, or, l, c, w)
578 {
579 
580 	if (state != TIME) {
581 		if (state == RUN)
582 			r -= or;
583 		putint((int)((float)r/etime + 0.5), l, c, w);
584 	} else
585 		putint(r, l, c, w);
586 }
587 
588 static
589 putint(n, l, c, w)
590 {
591 	char b[128];
592 
593 	move(l, c);
594 	if (n == 0) {
595 		while (w-- > 0)
596 			addch(' ');
597 		return;
598 	}
599 	sprintf(b, "%*d", w, n);
600 	if (strlen(b) > w) {
601 		while (w-- > 0)
602 			addch('*');
603 		return;
604 	}
605 	addstr(b);
606 }
607 
608 static
609 putfloat(f, l, c, w, d, nz)
610 	float f;
611 {
612 	char b[128];
613 
614 	move(l, c);
615 	if (nz && f == 0.0) {
616 		while (w-- > 0)
617 			addch(' ');
618 		return;
619 	}
620 	sprintf(b, "%*.*f", w, d, f);
621 	if (strlen(b) > w) {
622 		while (w-- > 0)
623 			addch('*');
624 		return;
625 	}
626 	addstr(b);
627 }
628 
629 static
630 getinfo(s, st)
631 	struct Info *s;
632 	enum state st;
633 {
634 
635 	lseek(kmem, (long)name[X_CPTIME].n_value,L_SET);
636 	read(kmem, s->time, sizeof s->time);
637 	if (st != TIME) {
638 		lseek(kmem, (long)name[X_SUM].n_value, L_SET);
639 		read(kmem, &s->Rate, sizeof s->Rate);
640 	} else {
641 		lseek(kmem, (long)name[X_RATE].n_value,L_SET);
642 		read(kmem, &s->Rate, sizeof s->Rate);
643 	}
644 	lseek(kmem, (long)name[X_TOTAL].n_value, L_SET);
645 	read(kmem, &s->Total, sizeof s->Total);
646 	s->dk_busy = getw(name[X_DK_BUSY].n_value);
647  	lseek(kmem, (long)name[X_DK_TIME].n_value,  L_SET);
648  	read(kmem, s->dk_time, dk_ndrive * sizeof (long));
649  	lseek(kmem, (long)name[X_DK_XFER].n_value,  L_SET);
650  	read(kmem, s->dk_xfer, dk_ndrive * sizeof (long));
651  	lseek(kmem, (long)name[X_DK_WDS].n_value,  L_SET);
652  	read(kmem, s->dk_wds, dk_ndrive * sizeof (long));
653 	lseek(kmem, (long)name[X_DK_SEEK].n_value,  L_SET);
654 	read(kmem, s->dk_seek, dk_ndrive * sizeof (long));
655 	s->tk_nin = getw(name[X_TK_NIN].n_value);
656 	s->tk_nout = getw(name[X_TK_NOUT].n_value);
657 	lseek(kmem, (long)name[X_NCHSTATS].n_value,  L_SET);
658 	read(kmem, &s->nchstats, sizeof s->nchstats);
659 	lseek(kmem, (long)name[X_INTRCNT].n_value,  L_SET);
660 	read(kmem, s->intrcnt, nintr * sizeof (long));
661 }
662 
663 static
664 allocinfo(s)
665 	struct Info *s;
666 {
667 
668 	s->intrcnt = (long *) malloc(nintr * sizeof(long));
669 	if (s->intrcnt == NULL) {
670 		fprintf(stderr, "systat: out of memory\n");
671 		exit(2);
672 	}
673 }
674 
675 static
676 copyinfo(from, to)
677 	register struct Info *from, *to;
678 {
679 	long *time, *wds, *seek, *xfer;
680 	long *intrcnt;
681 
682 	time = to->dk_time; wds = to->dk_wds; seek = to->dk_seek;
683 	xfer = to->dk_xfer; intrcnt = to->intrcnt;
684 	*to = *from;
685 	bcopy(from->dk_time, to->dk_time = time, dk_ndrive * sizeof (long));
686 	bcopy(from->dk_wds, to->dk_wds = wds, dk_ndrive * sizeof (long));
687 	bcopy(from->dk_seek, to->dk_seek = seek, dk_ndrive * sizeof (long));
688 	bcopy(from->dk_xfer, to->dk_xfer = xfer, dk_ndrive * sizeof (long));
689 	bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int));
690 }
691 
692 static
693 dinfo(dn, c)
694 {
695 	double words, atime, itime, xtime;
696 
697 	c = DISKCOL + c * 5;
698 	atime = s.dk_time[dn];
699 	atime /= hertz;
700 	words = s.dk_wds[dn]*32.0;	/* number of words transferred */
701 	xtime = dk_mspw[dn]*words;	/* transfer time */
702 	itime = atime - xtime;		/* time not transferring */
703 	if (xtime < 0)
704 		itime += xtime, xtime = 0;
705 	if (itime < 0)
706 		xtime += itime, itime = 0;
707 	putint((int)((float)s.dk_seek[dn]/etime+0.5), DISKROW + 1, c, 5);
708 	putint((int)((float)s.dk_xfer[dn]/etime+0.5), DISKROW + 2, c, 5);
709 	putint((int)(words/etime/512.0 + 0.5), DISKROW + 3, c, 5);
710 	if (s.dk_seek[dn])
711 		putfloat(itime*1000.0/s.dk_seek[dn], DISKROW + 4, c, 5, 1, 1);
712 	else
713 		putint(0, DISKROW + 4, c, 5);
714 }
715