xref: /openbsd/usr.bin/systat/vmstat.c (revision e5dd7070)
1 /*	$OpenBSD: vmstat.c,v 1.91 2019/06/28 13:35:04 deraadt Exp $	*/
2 /*	$NetBSD: vmstat.c,v 1.5 1996/05/10 23:16:40 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 1983, 1989, 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Cursed vmstat -- from Robert Elz.
35  */
36 
37 #include <sys/param.h>	/* MAXCOMLEN */
38 #include <sys/types.h>
39 #include <sys/namei.h>
40 #include <sys/proc.h>
41 #include <sys/sched.h>
42 #include <sys/stat.h>
43 #include <sys/sysctl.h>
44 #include <sys/time.h>
45 #include <sys/vmmeter.h>
46 
47 #include <ctype.h>
48 #include <errno.h>
49 #include <err.h>
50 #include <paths.h>
51 #include <signal.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 #include "systat.h"
57 #include "dkstats.h"
58 
59 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
60 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
61 
62 static struct Info {
63 	struct	cpustats cpustats;
64 	struct	uvmexp uvmexp;
65 	struct	vmtotal Total;
66 	struct	nchstats nchstats;
67 	long	nchcount;
68 	uint64_t *intrcnt;
69 } s, s1, s2, s3, z;
70 
71 static int ncpu;
72 
73 extern struct _disk	cur;
74 
75 #define	cnt s.Cnt
76 #define oldcnt s1.Cnt
77 #define	total s.Total
78 #define	nchtotal s.nchstats
79 #define	oldnchtotal s1.nchstats
80 
81 static	enum state { BOOT, TIME, RUN } state = TIME;
82 
83 static void allocinfo(struct Info *);
84 static void copyinfo(struct Info *, struct Info *);
85 static float cputime(int);
86 static void dinfo(int, int);
87 static void getinfo(struct Info *);
88 void putint(int, int, int, int);
89 void putintmk(int, int, int, int);
90 void putuint64(u_int64_t, int, int, int);
91 void putfloat(double, int, int, int, int, int);
92 int ucount(void);
93 
94 void print_vm(void);
95 int read_vm(void);
96 int select_vm(void);
97 int vm_keyboard_callback(int);
98 
99 static	time_t t;
100 static	float hertz;
101 static	int nintr;
102 static	long *intrloc;
103 static	char **intrname;
104 static	int ipktsrow;
105 
106 WINDOW *
107 openkre(void)
108 {
109 	return (subwin(stdscr, LINES-1-1, 0, 1, 0));
110 }
111 
112 void
113 closekre(WINDOW *w)
114 {
115 
116 	if (w == NULL)
117 		return;
118 	wclear(w);
119 	wrefresh(w);
120 	delwin(w);
121 }
122 
123 /*
124  * These constants define where the major pieces are laid out
125  */
126 #define STATROW		 0	/* uses 1 row and 68 cols */
127 #define STATCOL		 2
128 #define MEMROW		 2	/* uses 4 rows and 34 cols */
129 #define MEMCOL		 0
130 #define PAGEROW		 2	/* uses 4 rows and 26 cols */
131 #define PAGECOL		37
132 #define INTSROW		 2	/* uses all rows to bottom and 17 cols */
133 #define INTSCOL		63
134 #define PROCSROW	 7	/* uses 2 rows and 20 cols */
135 #define PROCSCOL	 0
136 #define GENSTATROW	 7	/* uses 2 rows and 35 cols */
137 #define GENSTATCOL	16
138 #define VMSTATROW	 7	/* uses 18 rows and 12 cols */
139 #define VMSTATCOL	48
140 #define GRAPHROW	10	/* uses 3 rows and 51 cols */
141 #define GRAPHCOL	 0
142 #define NAMEIROW	14	/* uses 3 rows and 49 cols */
143 #define NAMEICOL	 0
144 #define DISKROW		18	/* uses 5 rows and 50 cols (for 9 drives) */
145 #define DISKCOL		 0
146 
147 #define	DRIVESPACE	45	/* max space for drives */
148 
149 
150 field_def *view_vm_0[] = {
151 	NULL
152 };
153 
154 /* Define view managers */
155 struct view_manager vmstat_mgr = {
156 	"VMstat", select_vm, read_vm, NULL, print_header,
157 	print_vm, vm_keyboard_callback, NULL, NULL
158 };
159 
160 field_view views_vm[] = {
161 	{view_vm_0, "vmstat", '7', &vmstat_mgr},
162 	{NULL, NULL, 0, NULL}
163 };
164 
165 int
166 initvmstat(void)
167 {
168 	field_view *v;
169 	int mib[4], i;
170 	size_t size;
171 
172 	hertz = stathz;
173 	if (!dkinit(1))
174 		return(0);
175 
176 	mib[0] = CTL_HW;
177 	mib[1] = HW_NCPU;
178 	size = sizeof(ncpu);
179 	if (sysctl(mib, 2, &ncpu, &size, NULL, 0) == -1)
180 		return (-1);
181 
182 	mib[0] = CTL_KERN;
183 	mib[1] = KERN_INTRCNT;
184 	mib[2] = KERN_INTRCNT_NUM;
185 	size = sizeof(nintr);
186 	if (sysctl(mib, 3, &nintr, &size, NULL, 0) == -1)
187 		return (-1);
188 
189 	intrloc = calloc(nintr, sizeof(long));
190 	intrname = calloc(nintr, sizeof(char *));
191 	if (intrloc == NULL || intrname == NULL)
192 		err(2, NULL);
193 
194 	for (i = 0; i < nintr; i++) {
195 		char name[128];
196 
197 		mib[0] = CTL_KERN;
198 		mib[1] = KERN_INTRCNT;
199 		mib[2] = KERN_INTRCNT_NAME;
200 		mib[3] = i;
201 		size = sizeof(name);
202 		if (sysctl(mib, 4, name, &size, NULL, 0) == -1)
203 			return (-1);
204 
205 		intrname[i] = strdup(name);
206 		if (intrname[i] == NULL)
207 			return (-1);
208 	}
209 
210 	allocinfo(&s);
211 	allocinfo(&s1);
212 	allocinfo(&s2);
213 	allocinfo(&s3);
214 	allocinfo(&z);
215 
216 	getinfo(&s2);
217 	copyinfo(&z, &s1);
218 
219 	for (v = views_vm; v->name != NULL; v++)
220 		add_view(v);
221 
222 	return(1);
223 }
224 
225 void
226 fetchkre(void)
227 {
228 	getinfo(&s3);
229 }
230 
231 void
232 labelkre(void)
233 {
234 	int i, j, l;
235 
236 	mvprintw(MEMROW, MEMCOL,     "            memory totals (in KB)");
237 	mvprintw(MEMROW + 1, MEMCOL, "           real   virtual     free");
238 	mvprintw(MEMROW + 2, MEMCOL, "Active");
239 	mvprintw(MEMROW + 3, MEMCOL, "All");
240 
241 	mvprintw(PAGEROW, PAGECOL, "        PAGING   SWAPPING ");
242 	mvprintw(PAGEROW + 1, PAGECOL, "        in  out   in  out ");
243 	mvprintw(PAGEROW + 2, PAGECOL, "ops");
244 	mvprintw(PAGEROW + 3, PAGECOL, "pages");
245 
246 	mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
247 	mvprintw(INTSROW + 1, INTSCOL + 9, "total");
248 
249 	j = INTSROW + 2;
250 	for (i = 0; i < nintr; i++) {
251 		intrloc[i] = 0;
252 		if (s.intrcnt[i] == 0 || ipktsrow == LINES)
253 			continue;
254 		intrloc[i] = j++;
255 		mvprintw(intrloc[i], INTSCOL + 9, "%-8.8s", intrname[i]);
256 	}
257 	ipktsrow = MAXIMUM(j, MINIMUM(LINES - 3, VMSTATROW + 17));
258 	if (LINES - 1 > ipktsrow)
259 		mvprintw(ipktsrow, INTSCOL + 9, "IPKTS");
260 	if (LINES - 1 > ipktsrow + 1)
261 		mvprintw(ipktsrow + 1, INTSCOL + 9, "OPKTS");
262 
263 	mvprintw(VMSTATROW + 0, VMSTATCOL + 10, "forks");
264 	mvprintw(VMSTATROW + 1, VMSTATCOL + 10, "fkppw");
265 	mvprintw(VMSTATROW + 2, VMSTATCOL + 10, "fksvm");
266 	mvprintw(VMSTATROW + 3, VMSTATCOL + 10, "pwait");
267 	mvprintw(VMSTATROW + 4, VMSTATCOL + 10, "relck");
268 	mvprintw(VMSTATROW + 5, VMSTATCOL + 10, "rlkok");
269 	mvprintw(VMSTATROW + 6, VMSTATCOL + 10, "noram");
270 	mvprintw(VMSTATROW + 7, VMSTATCOL + 10, "ndcpy");
271 	mvprintw(VMSTATROW + 8, VMSTATCOL + 10, "fltcp");
272 	mvprintw(VMSTATROW + 9, VMSTATCOL + 10, "zfod");
273 	mvprintw(VMSTATROW + 10, VMSTATCOL + 10, "cow");
274 	mvprintw(VMSTATROW + 11, VMSTATCOL + 10, "fmin");
275 	mvprintw(VMSTATROW + 12, VMSTATCOL + 10, "ftarg");
276 	mvprintw(VMSTATROW + 13, VMSTATCOL + 10, "itarg");
277 	mvprintw(VMSTATROW + 14, VMSTATCOL + 10, "wired");
278 	mvprintw(VMSTATROW + 15, VMSTATCOL + 10, "pdfre");
279 	if (LINES - 1 > VMSTATROW + 16)
280 		mvprintw(VMSTATROW + 16, VMSTATCOL + 10, "pdscn");
281 	if (LINES - 1 > VMSTATROW + 17)
282 		mvprintw(VMSTATROW + 17, VMSTATCOL + 10, "pzidl");
283 	if (LINES - 1 > VMSTATROW + 18)
284 		mvprintw(VMSTATROW + 18, VMSTATCOL + 10, "kmape");
285 
286 	mvprintw(GENSTATROW, GENSTATCOL, "   Csw   Trp   Sys   Int   Sof  Flt");
287 
288 	mvprintw(GRAPHROW, GRAPHCOL,
289 	    "    . %%Int    . %%Spn    . %%Sys    . %%Usr    . %%Idle");
290 	mvprintw(PROCSROW, PROCSCOL, "Proc:r  d  s  w");
291 	mvprintw(GRAPHROW + 1, GRAPHCOL,
292 	    "|    |    |    |    |    |    |    |    |    |    |");
293 
294 	mvprintw(NAMEIROW, NAMEICOL,
295 	    "Namei         Sys-cache    Proc-cache    No-cache");
296 	mvprintw(NAMEIROW + 1, NAMEICOL,
297 	    "    Calls     hits    %%    hits     %%    miss   %%");
298 	mvprintw(DISKROW, DISKCOL, "Disks");
299 	mvprintw(DISKROW + 1, DISKCOL, "seeks");
300 	mvprintw(DISKROW + 2, DISKCOL, "xfers");
301 	mvprintw(DISKROW + 3, DISKCOL, "speed");
302 	mvprintw(DISKROW + 4, DISKCOL, "  sec");
303 	for (i = 0, j = 0; i < cur.dk_ndrive && j < DRIVESPACE; i++)
304 		if (cur.dk_select[i] && (j + strlen(dr_name[i])) < DRIVESPACE) {
305 			l = MAXIMUM(5, strlen(dr_name[i]));
306 			mvprintw(DISKROW, DISKCOL + 5 + j,
307 			    " %*s", l, dr_name[i]);
308 			j += 1 + l;
309 		}
310 	for (i = 0; i < nintr; i++) {
311 		if (intrloc[i] == 0)
312 			continue;
313 		mvprintw(intrloc[i], INTSCOL + 9, "%-8.8s", intrname[i]);
314 	}
315 }
316 
317 #define X(fld)	{s.fld[i]-=s1.fld[i];}
318 #define Y(fld)	{s.fld -= s1.fld;}
319 #define Z(fld)	{s.nchstats.fld -= s1.nchstats.fld;}
320 #define PUTRATE(fld, l, c, w) \
321 	do { \
322 		Y(fld); \
323 		putint((int)((float)s.fld/etime + 0.5), l, c, w); \
324 	} while (0)
325 #define MAXFAIL 5
326 
327 static	char cpuchar[] = { '|', '@', '=', '>', ' ' };
328 static	char cpuorder[] = { CP_INTR, CP_SPIN, CP_SYS, CP_USER, CP_IDLE };
329 
330 void
331 showkre(void)
332 {
333 	float f1, f2;
334 	int psiz;
335 	u_int64_t inttotal, intcnt;
336 	int i, l, c;
337 	static int failcnt = 0, first_run = 0;
338 	double etime;
339 
340 	if (state == TIME) {
341 		if (!first_run) {
342 			first_run = 1;
343 			return;
344 		}
345 	}
346 	etime = 0;
347 	for (i = 0; i < CPUSTATES; i++) {
348 		X(cpustats.cs_time);
349 		etime += s.cpustats.cs_time[i];
350 	}
351 	if (etime < 5.0) {	/* < 5 ticks - ignore this trash */
352 		if (failcnt++ >= MAXFAIL) {
353 			error("The alternate system clock has died!");
354 			failcnt = 0;
355 		}
356 		return;
357 	}
358 	failcnt = 0;
359 	etime /= hertz;
360 	inttotal = 0;
361 	for (i = 0; i < nintr; i++) {
362 		t = intcnt = s.intrcnt[i];
363 		s.intrcnt[i] -= s1.intrcnt[i];
364 		intcnt = (u_int64_t)((float)s.intrcnt[i]/etime + 0.5);
365 		inttotal += intcnt;
366 		if (intrloc[i] != 0)
367 			putuint64(intcnt, intrloc[i], INTSCOL, 8);
368 	}
369 	putuint64(inttotal, INTSROW + 1, INTSCOL, 8);
370 	Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
371 	Z(ncs_long); Z(ncs_pass2); Z(ncs_2passes);
372 	s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
373 	    nchtotal.ncs_miss + nchtotal.ncs_long;
374 
375 	if (LINES - 1 > ipktsrow)
376 		putint(sum.ifc_ip, ipktsrow, INTSCOL, 8);
377 	if (LINES - 1 > ipktsrow + 1)
378 		putint(sum.ifc_op, ipktsrow + 1, INTSCOL, 8);
379 
380 	psiz = 0;
381 	f2 = 0.0;
382 
383 	for (c = 0; c < nitems(cpuorder); c++) {
384 		i = cpuorder[c];
385 		f1 = cputime(i);
386 		if (i == CP_USER)
387 			f1 += cputime(CP_NICE);
388 		f2 += f1;
389 		l = (int) ((f2 + 1.0) / 2.0) - psiz;
390 		putfloat(f1, GRAPHROW, GRAPHCOL + 1 + (10 * c), 5, 1, 0);
391 		move(GRAPHROW + 2, psiz);
392 		psiz += l;
393 		while (l-- > 0)
394 			addch(cpuchar[c]);
395 	}
396 
397 #define pgtokb(pg)	((pg) * (s.uvmexp.pagesize / 1024))
398 
399 	putint(pgtokb(s.uvmexp.active), MEMROW + 2, MEMCOL + 7, 8);
400 	putint(pgtokb(s.uvmexp.active + s.uvmexp.swpginuse),    /* XXX */
401 	    MEMROW + 2, MEMCOL + 17, 8);
402 	putint(pgtokb(s.uvmexp.npages - s.uvmexp.free), MEMROW + 3, MEMCOL + 7, 8);
403 	putint(pgtokb(s.uvmexp.npages - s.uvmexp.free + s.uvmexp.swpginuse),
404 	    MEMROW + 3, MEMCOL + 17, 8);
405 	putint(pgtokb(s.uvmexp.free), MEMROW + 2, MEMCOL + 26, 8);
406 	putint(pgtokb(s.uvmexp.free + s.uvmexp.swpages - s.uvmexp.swpginuse),
407 	    MEMROW + 3, MEMCOL + 26, 8);
408 	putint(total.t_rq - 1, PROCSROW + 1, PROCSCOL + 3, 3);
409 
410 	putint(total.t_dw, PROCSROW + 1, PROCSCOL + 6, 3);
411 	putint(total.t_sl, PROCSROW + 1, PROCSCOL + 9, 3);
412 	putint(total.t_sw, PROCSROW + 1, PROCSCOL + 12, 3);
413 	PUTRATE(uvmexp.forks, VMSTATROW + 0, VMSTATCOL + 3, 6);
414 	PUTRATE(uvmexp.forks_ppwait, VMSTATROW + 1, VMSTATCOL + 3, 6);
415 	PUTRATE(uvmexp.forks_sharevm, VMSTATROW + 2, VMSTATCOL + 3, 6);
416 	PUTRATE(uvmexp.fltpgwait, VMSTATROW + 3, VMSTATCOL + 4, 5);
417 	PUTRATE(uvmexp.fltrelck, VMSTATROW + 4, VMSTATCOL + 3, 6);
418 	PUTRATE(uvmexp.fltrelckok, VMSTATROW + 5, VMSTATCOL + 3, 6);
419 	PUTRATE(uvmexp.fltnoram, VMSTATROW + 6, VMSTATCOL + 3, 6);
420 	PUTRATE(uvmexp.fltamcopy, VMSTATROW + 7, VMSTATCOL + 3, 6);
421 	PUTRATE(uvmexp.flt_prcopy, VMSTATROW + 8, VMSTATCOL + 3, 6);
422 	PUTRATE(uvmexp.flt_przero, VMSTATROW + 9, VMSTATCOL + 3, 6);
423 	PUTRATE(uvmexp.flt_acow, VMSTATROW + 10, VMSTATCOL, 9);
424 	putint(s.uvmexp.freemin, VMSTATROW + 11, VMSTATCOL, 9);
425 	putint(s.uvmexp.freetarg, VMSTATROW + 12, VMSTATCOL, 9);
426 	putint(s.uvmexp.inactarg, VMSTATROW + 13, VMSTATCOL, 9);
427 	putint(s.uvmexp.wired, VMSTATROW + 14, VMSTATCOL, 9);
428 	PUTRATE(uvmexp.pdfreed, VMSTATROW + 15, VMSTATCOL, 9);
429 	if (LINES - 1 > VMSTATROW + 16)
430 		PUTRATE(uvmexp.pdscans, VMSTATROW + 16, VMSTATCOL, 9);
431 	if (LINES - 1 > VMSTATROW + 17)
432 		PUTRATE(uvmexp.zeropages, VMSTATROW + 17, VMSTATCOL, 9);
433 	if (LINES - 1 > VMSTATROW + 18)
434 		putint(s.uvmexp.kmapent, VMSTATROW + 18, VMSTATCOL, 9);
435 
436 	PUTRATE(uvmexp.pageins, PAGEROW + 2, PAGECOL + 5, 5);
437 	PUTRATE(uvmexp.pdpageouts, PAGEROW + 2, PAGECOL + 10, 5);
438 	PUTRATE(uvmexp.pgswapin, PAGEROW + 3, PAGECOL + 5, 5);
439 	PUTRATE(uvmexp.pgswapout, PAGEROW + 3, PAGECOL + 10, 5);
440 
441 	PUTRATE(uvmexp.swtch, GENSTATROW + 1, GENSTATCOL, 6);
442 	PUTRATE(uvmexp.traps, GENSTATROW + 1, GENSTATCOL + 6, 6);
443 	PUTRATE(uvmexp.syscalls, GENSTATROW + 1, GENSTATCOL + 12, 6);
444 	PUTRATE(uvmexp.intrs, GENSTATROW + 1, GENSTATCOL + 18, 6);
445 	PUTRATE(uvmexp.softs, GENSTATROW + 1, GENSTATCOL + 24, 6);
446 	PUTRATE(uvmexp.faults, GENSTATROW + 1, GENSTATCOL + 30, 5);
447 	mvprintw(DISKROW, DISKCOL + 5, "                              ");
448 	for (i = 0, c = 0; i < cur.dk_ndrive && c < DRIVESPACE; i++)
449 		if (cur.dk_select[i] && (c + strlen(dr_name[i])) < DRIVESPACE) {
450 			l = MAXIMUM(5, strlen(dr_name[i]));
451 			mvprintw(DISKROW, DISKCOL + 5 + c,
452 			    " %*s", l, dr_name[i]);
453 			c += 1 + l;
454 			dinfo(i, c);
455 		}
456 	/* and pad the DRIVESPACE */
457 	l = DRIVESPACE - c;
458 	for (i = 0; i < 5; i++)
459 		mvprintw(DISKROW + i, DISKCOL + 5 + c, "%*s", l, "");
460 
461 	putint(s.nchcount, NAMEIROW + 2, NAMEICOL, 9);
462 	putint(nchtotal.ncs_goodhits, NAMEIROW + 2, NAMEICOL + 10, 8);
463 #define nz(x)	((x) ? (x) : 1)
464 	putfloat(nchtotal.ncs_goodhits * 100.0 / nz(s.nchcount),
465 	    NAMEIROW + 2, NAMEICOL + 19, 4, 0, 1);
466 	putint(nchtotal.ncs_pass2, NAMEIROW + 2, NAMEICOL + 24, 7);
467 	putfloat(nchtotal.ncs_pass2 * 100.0 / nz(s.nchcount),
468 	    NAMEIROW + 2, NAMEICOL + 33, 4, 0, 1);
469 	putint(nchtotal.ncs_miss + nchtotal.ncs_long - nchtotal.ncs_pass2,
470 	   NAMEIROW + 2, NAMEICOL + 38, 7);
471 	putfloat((nchtotal.ncs_miss + nchtotal.ncs_long - nchtotal.ncs_pass2) *
472 	    100.0 / nz(s.nchcount), NAMEIROW + 2, NAMEICOL + 45, 4, 0, 1);
473 #undef nz
474 
475 }
476 
477 int
478 vm_keyboard_callback(int ch)
479 {
480 	switch(ch) {
481 	case 'r':
482 		copyinfo(&s2, &s1);
483 		state = RUN;
484 		break;
485 	case 'b':
486 		state = BOOT;
487 		copyinfo(&z, &s1);
488 		break;
489 	case 't':
490 		state = TIME;
491 		break;
492 	case 'z':
493 		if (state == RUN)
494 			getinfo(&s1);
495 		break;
496 	}
497 	return (keyboard_callback(ch));
498 }
499 
500 
501 static float
502 cputime(int indx)
503 {
504 	double tm;
505 	int i;
506 
507 	tm = 0;
508 	for (i = 0; i < nitems(s.cpustats.cs_time); i++)
509 		tm += s.cpustats.cs_time[i];
510 	if (tm == 0.0)
511 		tm = 1.0;
512 	return (s.cpustats.cs_time[indx] * 100.0 / tm);
513 }
514 
515 void
516 putint(int n, int l, int c, int w)
517 {
518 	char b[128];
519 
520 	move(l, c);
521 	if (n == 0) {
522 		while (w-- > 0)
523 			addch(' ');
524 		return;
525 	}
526 	snprintf(b, sizeof b, "%*d", w, n);
527 	if (strlen(b) > w) {
528 		while (w-- > 0)
529 			addch('*');
530 		return;
531 	}
532 	addstr(b);
533 }
534 
535 void
536 putintmk(int n, int l, int c, int w)
537 {
538 	char b[128];
539 
540 	move(l, c);
541 	if (n == 0) {
542 		while (w-- > 0)
543 			addch(' ');
544 		return;
545 	}
546 	if (n > 9999 * 1024)
547 		snprintf(b, sizeof b, "%*dG", w - 1, n / 1024 / 1024);
548 	else if (n > 9999)
549 		snprintf(b, sizeof b, "%*dM", w - 1, n / 1024);
550 	else
551 		snprintf(b, sizeof b, "%*dK", w - 1, n);
552 	if (strlen(b) > w) {
553 		while (w-- > 0)
554 			addch('*');
555 		return;
556 	}
557 	addstr(b);
558 }
559 
560 void
561 putuint64(u_int64_t n, int l, int c, int w)
562 {
563 	char b[128];
564 
565 	move(l, c);
566 	if (n == 0) {
567 		while (w-- > 0)
568 			addch(' ');
569 		return;
570 	}
571 	snprintf(b, sizeof b, "%*llu", w, n);
572 	if (strlen(b) > w) {
573 		while (w-- > 0)
574 			addch('*');
575 		return;
576 	}
577 	addstr(b);
578 }
579 
580 void
581 putfloat(double f, int l, int c, int w, int d, int nz)
582 {
583 	char b[128];
584 
585 	move(l, c);
586 	if (nz && f == 0.0) {
587 		while (--w >= 0)
588 			addch(' ');
589 		return;
590 	}
591 	snprintf(b, sizeof b, "%*.*f", w, d, f);
592 	if (strlen(b) > w) {
593 		while (--w >= 0)
594 			addch('*');
595 		return;
596 	}
597 	addstr(b);
598 }
599 
600 static void
601 getinfo(struct Info *si)
602 {
603 	static int cpustats_mib[3] = { CTL_KERN, KERN_CPUSTATS, 0 };
604 	static int nchstats_mib[2] = { CTL_KERN, KERN_NCHSTATS };
605 	static int uvmexp_mib[2] = { CTL_VM, VM_UVMEXP };
606 	static int vmtotal_mib[2] = { CTL_VM, VM_METER };
607 	struct cpustats cs;
608 	int mib[4], i, j;
609 	size_t size;
610 
611 	dkreadstats();
612 
613 	for (i = 0; i < nintr; i++) {
614 		mib[0] = CTL_KERN;
615 		mib[1] = KERN_INTRCNT;
616 		mib[2] = KERN_INTRCNT_CNT;
617 		mib[3] = i;
618 		size = sizeof(si->intrcnt[i]);
619 		if (sysctl(mib, 4, &si->intrcnt[i], &size, NULL, 0) == -1) {
620 			si->intrcnt[i] = 0;
621 		}
622 	}
623 
624 	memset(&si->cpustats.cs_time, 0, sizeof(si->cpustats.cs_time));
625 	for (i = 0; i < ncpu; i++) {
626 		cpustats_mib[2] = i;
627 		size = sizeof(cs);
628 		if (sysctl(cpustats_mib, 3, &cs, &size, NULL, 0) == -1) {
629 			error("Can't get KERN_CPUSTATS: %s\n", strerror(errno));
630 			memset(&si->cpustats, 0, sizeof(si->cpustats));
631 		}
632 		if ((cs.cs_flags & CPUSTATS_ONLINE) == 0)
633 			continue;	/* omit totals for offline CPUs */
634 		for (j = 0; j < nitems(cs.cs_time); j++)
635 			si->cpustats.cs_time[j] += cs.cs_time[j];
636 	}
637 
638 	size = sizeof(si->nchstats);
639 	if (sysctl(nchstats_mib, 2, &si->nchstats, &size, NULL, 0) == -1) {
640 		error("Can't get KERN_NCHSTATS: %s\n", strerror(errno));
641 		memset(&si->nchstats, 0, sizeof(si->nchstats));
642 	}
643 
644 	size = sizeof(si->uvmexp);
645 	if (sysctl(uvmexp_mib, 2, &si->uvmexp, &size, NULL, 0) == -1) {
646 		error("Can't get VM_UVMEXP: %s\n", strerror(errno));
647 		memset(&si->uvmexp, 0, sizeof(si->uvmexp));
648 	}
649 
650 	size = sizeof(si->Total);
651 	if (sysctl(vmtotal_mib, 2, &si->Total, &size, NULL, 0) == -1) {
652 		error("Can't get VM_METER: %s\n", strerror(errno));
653 		memset(&si->Total, 0, sizeof(si->Total));
654 	}
655 }
656 
657 static void
658 allocinfo(struct Info *si)
659 {
660 	memset(si, 0, sizeof(*si));
661 	si->intrcnt = calloc(nintr, sizeof(*si->intrcnt));
662 	if (si->intrcnt == NULL)
663 		err(2, NULL);
664 }
665 
666 static void
667 copyinfo(struct Info *from, struct Info *to)
668 {
669 	uint64_t *intrcnt;
670 
671 	intrcnt = to->intrcnt;
672 	*to = *from;
673 	memcpy(to->intrcnt = intrcnt, from->intrcnt, nintr * sizeof(*intrcnt));
674 }
675 
676 static void
677 dinfo(int dn, int c)
678 {
679 	double words, atime, etime;
680 
681 	etime = naptime;
682 
683 	c += DISKCOL;
684 
685 	/* time busy in disk activity */
686 	atime = (double)cur.dk_time[dn].tv_sec +
687 	    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
688 
689 	/* # of K transferred */
690 	words = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0;
691 
692 	putint((int)((float)cur.dk_seek[dn]/etime+0.5), DISKROW + 1, c, 5);
693 	putint((int)((float)(cur.dk_rxfer[dn] + cur.dk_wxfer[dn])/etime+0.5),
694 	    DISKROW + 2, c, 5);
695 	putintmk((int)(words/etime + 0.5), DISKROW + 3, c, 5);
696 	putfloat(atime/etime, DISKROW + 4, c, 5, 1, 1);
697 }
698 
699 
700 
701 int
702 select_vm(void)
703 {
704 	num_disp = 0;
705 	return (0);
706 }
707 
708 int
709 read_vm(void)
710 {
711 	if (state == TIME)
712 		copyinfo(&s3, &s1);
713 	fetchkre();
714 	fetchifstat();
715 	if (state == TIME)
716 		dkswap();
717 	num_disp = 0;
718 	return 0;
719 }
720 
721 
722 void
723 print_vm(void)
724 {
725 	copyinfo(&s3, &s);
726 	labelkre();
727 	showkre();
728 }
729