xref: /dragonfly/usr.bin/systat/vmstat.c (revision e6d22e9b)
1 /*-
2  * Copyright (c) 1983, 1989, 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /*
31  * Cursed vmstat -- from Robert Elz.
32  */
33 
34 #include <sys/user.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/uio.h>
39 #include <sys/namei.h>
40 #include <sys/sysctl.h>
41 #include <sys/vmmeter.h>
42 
43 #include <vm/vm_param.h>
44 
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <kinfo.h>
49 #include <langinfo.h>
50 #include <nlist.h>
51 #include <paths.h>
52 #include <signal.h>
53 #include <stddef.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 #include "utmpentry.h"
59 #include <devstat.h>
60 #include "systat.h"
61 #include "extern.h"
62 #include "devs.h"
63 
64 #define NKVMSW	16
65 
66 static struct Info {
67 	struct kinfo_cputime cp_time;
68 	struct	vmmeter Vmm;
69 	struct	vmtotal Total;
70 	struct  vmstats Vms;
71 	struct	nchstats nchstats;
72 	long	nchcount;
73 	long	nchpathcount;
74 	long	*intrcnt;
75 	long	bufspace;
76 	int	maxvnodes;
77 	int	cachedvnodes;
78 	int	inactivevnodes;
79 	int	activevnodes;
80 	long	dirtybufspace;
81 	long	physmem;
82 	struct kvm_swap  kvmsw[NKVMSW];
83 } s, s1, s2, z;
84 
85 struct statinfo run;
86 struct kinfo_cputime cp_time;
87 static int kvnsw;
88 
89 #define	vmm s.Vmm
90 #define	vms s.Vms
91 #define oldvmm s1.Vmm
92 #define oldvms s1.Vms
93 #define	total s.Total
94 #define	nchtotal s.nchstats
95 #define	oldnchtotal s1.nchstats
96 
97 static	enum state { BOOT, TIME, RUN } state = TIME;
98 
99 static void allocinfo(struct Info *);
100 static void copyinfo(struct Info *, struct Info *);
101 static void dinfo(int, int, struct statinfo *, struct statinfo *);
102 static void getinfo(struct Info *);
103 static void put64(int64_t, int, int, int, int);
104 static void putfloat(double, int, int, int, int, int);
105 static void putlongdouble(long double, int, int, int, int, int);
106 static void putlongdoublez(long double, int, int, int, int, int);
107 static int ucount(void);
108 
109 static	int ncpu;
110 static	char buf[26];
111 static	time_t t;
112 static	double etime;
113 static	int nintr;
114 static	int  *intralias;
115 static	int  *intrsmp;
116 static	long *intrloc;
117 static	long *lacc;
118 static	char **intrname;
119 static	int nextintsrow;
120 static  int extended_vm_stats;
121 
122 
123 
124 WINDOW *
125 openkre(void)
126 {
127 
128 	return (stdscr);
129 }
130 
131 void
132 closekre(WINDOW *w)
133 {
134 
135 	if (w == NULL)
136 		return;
137 	wclear(w);
138 	wrefresh(w);
139 }
140 
141 
142 static struct nlist namelist[] = {
143 #define	X_BUFFERSPACE	0
144 	{ .n_name = "_bufspace" },
145 #define	X_NCHSTATS	1
146 	{ .n_name = "_nchstats" },
147 #define	X_DESIREDVNODES	2
148 	{ .n_name = "_maxvnodes" },
149 #define	X_CACHEDVNODES	3
150 	{ .n_name = "_cachedvnodes" },
151 #define	X_INACTIVEVNODES 4
152 	{ .n_name = "_inactivevnodes" },
153 #define	X_ACTIVEVNODES	5
154 	{ .n_name = "_activevnodes" },
155 #define X_NUMDIRTYBUFFERS 6
156 	{ .n_name = "_dirtybufspace" },
157 	{ .n_name = "" },
158 };
159 
160 /*
161  * These constants define where the major pieces are laid out
162  */
163 #define STATROW		 0	/* uses 1 row and 68 cols */
164 #define STATCOL		 2
165 #define MEMROW		 2	/* uses 4 rows and 31 cols */
166 #define MEMCOLA		 0
167 #define MEMCOLB		 17
168 #define PAGEROW		 2	/* uses 4 rows and 26 cols */
169 #define PAGECOL		45
170 #define INTSROW		 6	/* uses all rows to bottom and 17 cols */
171 #define INTSCOL		61
172 #define PROCSROW	 7	/* uses 2 rows and 20 cols */
173 #define PROCSCOL	 0
174 #define GENSTATROW	 7	/* uses 2 rows and 30 cols */
175 #define GENSTATCOL	16
176 #define VMSTATROW	 6	/* uses 17 rows and 12 cols */
177 #define VMSTATCOL	50
178 #define GRAPHROW	10	/* uses 3 rows and 51 cols */
179 #define GRAPHCOL	 0
180 #define NAMEIROW	14	/* uses 3 rows and 38 cols */
181 #define NAMEICOL	 0
182 #define EXECROW		14	/* uses 2 rows and 5 cols */
183 #define EXECCOL		38
184 #define DISKROW		17	/* uses 6 rows and 50 cols (for 9 drives) */
185 #define DISKCOL		 0
186 
187 #define	DRIVESPACE	 7	/* max # for space */
188 
189 #define	MAXDRIVES	DRIVESPACE	 /* max # to display */
190 
191 static
192 int
193 findintralias(const char *name, int limit)
194 {
195 	int i;
196 	size_t nlen;
197 	size_t ilen;
198 
199 	nlen = strlen(name);
200 	for (i = 0; i < limit; ++i) {
201 		if (strcmp(name, intrname[i]) == 0)
202 			break;
203 		ilen = strlen(intrname[i]);
204 		if (nlen == ilen &&
205 		    nlen > 1 &&
206 		    strncmp(name, intrname[i], nlen - 1) == 0 &&
207 		    strchr(name, ' ') &&
208 		    isdigit(name[nlen - 1]) &&
209 		    (isdigit(intrname[i][nlen - 1]) ||
210 		     intrname[i][nlen - 1] == '*')) {
211 			intrname[i][nlen - 1] = '*';
212 			break;
213 		}
214 	}
215 	return i;
216 }
217 
218 int
219 initkre(void)
220 {
221 	char *intrnamebuf;
222 	size_t bytes;
223 	size_t b;
224 	size_t i;
225 
226 	if (namelist[0].n_type == 0) {
227 		if (kvm_nlist(kd, namelist)) {
228 			nlisterr(namelist);
229 			return(0);
230 		}
231 		if (namelist[0].n_type == 0) {
232 			error("No namelist");
233 			return(0);
234 		}
235 	}
236 
237 	if ((num_devices = getnumdevs()) < 0) {
238 		warnx("%s", devstat_errbuf);
239 		return(0);
240 	}
241 
242 	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
243 	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
244 	run.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
245 	bzero(cur.dinfo, sizeof(struct devinfo));
246 	bzero(last.dinfo, sizeof(struct devinfo));
247 	bzero(run.dinfo, sizeof(struct devinfo));
248 
249 	if (dsinit(MAXDRIVES, &cur, &last, &run) != 1)
250 		return(0);
251 
252 	if (nintr == 0) {
253 		if (sysctlbyname("hw.intrnames", NULL, &bytes, NULL, 0) == 0) {
254 			intrnamebuf = malloc(bytes);
255 			sysctlbyname("hw.intrnames", intrnamebuf, &bytes,
256 					NULL, 0);
257 			for (i = 0; i < bytes; ++i) {
258 				if (intrnamebuf[i] == 0)
259 					++nintr;
260 			}
261 			intrname = malloc(nintr * sizeof(char *));
262 			intrloc = malloc(nintr * sizeof(*intrloc));
263 			lacc = malloc(nintr * sizeof(*lacc));
264 			intralias = malloc(nintr * sizeof(*intralias));
265 			intrsmp = malloc(nintr * sizeof(*intrsmp));
266 			bzero(intrsmp, nintr * sizeof(*intrsmp));
267 
268 			nintr = 0;
269 			for (b = i = 0; i < bytes; ++i) {
270 				if (intrnamebuf[i] == 0) {
271 					intrname[nintr] = intrnamebuf + b;
272 					intrloc[nintr] = 0;
273 					intralias[nintr] =
274 					  findintralias(intrname[nintr], nintr);
275 					++intrsmp[intralias[nintr]];
276 					b = i + 1;
277 					++nintr;
278 				}
279 			}
280 		}
281 		nextintsrow = INTSROW + 2;
282 		allocinfo(&s);
283 		allocinfo(&s1);
284 		allocinfo(&s2);
285 		allocinfo(&z);
286 	}
287 	getinfo(&s2);
288 	copyinfo(&s2, &s1);
289 	return(1);
290 }
291 
292 void
293 fetchkre(void)
294 {
295 	time_t now;
296 	struct tm *tp;
297 	static int d_first = -1;
298 
299 	if (d_first < 0)
300 		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
301 
302 	time(&now);
303 	tp = localtime(&now);
304 	(void) strftime(buf, sizeof(buf),
305 			d_first ? "%e %b %R" : "%b %e %R", tp);
306 	getinfo(&s);
307 }
308 
309 void
310 labelkre(void)
311 {
312 	int i, j;
313 
314 	clear();
315 	mvprintw(STATROW, STATCOL + 4, "users    Load");
316 	mvprintw(MEMROW + 0, MEMCOLA, "Active ");
317 	mvprintw(MEMROW + 1, MEMCOLA, "Kernel ");
318 	mvprintw(MEMROW + 2, MEMCOLA, "Free   ");
319 	mvprintw(MEMROW + 3, MEMCOLA, "Total  ");
320 
321 	mvprintw(MEMROW + 2, MEMCOLA + 14, "i+c+f");
322 
323 	mvprintw(MEMROW + 0, MEMCOLB, "PMAP");
324 	mvprintw(MEMROW + 0, MEMCOLB + 13, "VMRSS");
325 	mvprintw(MEMROW + 1, MEMCOLB, "SWAP");
326 	mvprintw(MEMROW + 1, MEMCOLB + 13, "SWTOT");
327 
328 	mvprintw(PAGEROW, PAGECOL,     "       VNODE PAGER    SWAP PAGER ");
329 	mvprintw(PAGEROW + 1, PAGECOL, "          in   out      in   out ");
330 	mvprintw(PAGEROW + 2, PAGECOL, "bytes");
331 	mvprintw(PAGEROW + 3, PAGECOL, "count");
332 
333 	mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
334 	mvprintw(INTSROW + 1, INTSCOL + 9, "total");
335 
336 	mvprintw(VMSTATROW + 1, VMSTATCOL + 8, "cow");
337 	mvprintw(VMSTATROW + 2, VMSTATCOL + 8, "wire");
338 	mvprintw(VMSTATROW + 3, VMSTATCOL + 8, "act");
339 	mvprintw(VMSTATROW + 4, VMSTATCOL + 8, "inact");
340 	mvprintw(VMSTATROW + 5, VMSTATCOL + 8, "cache");
341 	mvprintw(VMSTATROW + 6, VMSTATCOL + 8, "free");
342 	mvprintw(VMSTATROW + 7, VMSTATCOL + 8, "daefr");
343 	mvprintw(VMSTATROW + 8, VMSTATCOL + 8, "prcfr");
344 	mvprintw(VMSTATROW + 9, VMSTATCOL + 8, "react");
345 	mvprintw(VMSTATROW + 10, VMSTATCOL + 8, "pdwake");
346 	mvprintw(VMSTATROW + 11, VMSTATCOL + 8, "pdpgs");
347 	mvprintw(VMSTATROW + 12, VMSTATCOL + 8, "intrn");
348 	mvprintw(VMSTATROW + 13, VMSTATCOL + 8, "buf");
349 	mvprintw(VMSTATROW + 14, VMSTATCOL + 8, "dirtybuf");
350 
351 	mvprintw(VMSTATROW + 15, VMSTATCOL + 8, "activ-vp");
352 	mvprintw(VMSTATROW + 16, VMSTATCOL + 8, "cachd-vp");
353 	mvprintw(VMSTATROW + 17, VMSTATCOL + 8, "inact-vp");
354 
355 	mvprintw(GENSTATROW, GENSTATCOL, "  Csw  Trp  Sys  Int  Sof  Flt");
356 
357 	mvprintw(GRAPHROW, GRAPHCOL,
358 		"  . %%Sys    . %%Intr   . %%User   . %%Nice   . %%Idle");
359 	mvprintw(PROCSROW, PROCSCOL, "   r   p   d   s");
360 	mvprintw(GRAPHROW + 1, GRAPHCOL,
361 		"|    |    |    |    |    |    |    |    |    |    |");
362 
363 	mvprintw(NAMEIROW, NAMEICOL, "Path-lookups   hits   %%    Components");
364 	mvprintw(EXECROW, EXECCOL, "Execs");
365 	mvprintw(DISKROW, DISKCOL, "Disks");
366 	mvprintw(DISKROW + 1, DISKCOL, "KB/t");
367 	mvprintw(DISKROW + 2, DISKCOL, "tpr/s");
368 	mvprintw(DISKROW + 3, DISKCOL, "MBr/s");
369 	mvprintw(DISKROW + 4, DISKCOL, "tpw/s");
370 	mvprintw(DISKROW + 5, DISKCOL, "MBw/s");
371 	mvprintw(DISKROW + 6, DISKCOL, "%% busy");
372 
373 	/*
374 	 * For now, we don't support a fourth disk statistic.  So there's
375 	 * no point in providing a label for it.  If someone can think of a
376 	 * fourth useful disk statistic, there is room to add it.
377 	 */
378 	j = 0;
379 	for (i = 0; i < num_devices && j < MAXDRIVES; i++)
380 		if (dev_select[i].selected) {
381 			char tmpstr[80];
382 			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
383 				dev_select[i].unit_number);
384 			mvprintw(DISKROW, DISKCOL + 5 + 6 * j,
385 				" %5.5s", tmpstr);
386 			j++;
387 		}
388 
389 	if (j <= 4) {
390 		/*
391 		 * room for extended VM stats
392 		 */
393 		mvprintw(VMSTATROW + 11, VMSTATCOL - 6, "nzfod");
394 		mvprintw(VMSTATROW + 12, VMSTATCOL - 6, "ozfod");
395 		mvprintw(VMSTATROW + 13, VMSTATCOL - 6, "%%zslo");
396 		mvprintw(VMSTATROW + 14, VMSTATCOL - 6, "pgfre");
397 		extended_vm_stats = 1;
398 	} else {
399 		extended_vm_stats = 0;
400 		mvprintw(VMSTATROW + 0, VMSTATCOL + 8, "zfod");
401 	}
402 
403 	for (i = 0; i < nintr; i++) {
404 		if (intrloc[i] == 0)
405 			continue;
406 		mvprintw(intrloc[i], INTSCOL + 9, "%-10.10s", intrname[i]);
407 	}
408 }
409 
410 #define CP_UPDATE(fld)	do {	\
411 	uint64_t lt;		\
412 	lt=s.fld;		\
413 	s.fld-=s1.fld;		\
414 	if(state==TIME)		\
415 		s1.fld=lt;	\
416 	lt=fld;			\
417 	fld-=old_##fld;		\
418 	if(state==TIME)		\
419 		old_##fld=lt;	\
420 	etime += s.fld;		\
421 } while(0)
422 #define X(fld)	{t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;}
423 #define Y(fld)	{t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;}
424 #define Z(fld)	{t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \
425 	if(state == TIME) s1.nchstats.fld = t;}
426 #define PUTRATE(fld, l, c, w) \
427 	Y(fld); \
428 	put64((int64_t)((float)s.fld/etime + 0.5), l, c, w, 'D')
429 #define PUTRATE_PGTOB(fld, l, c, w) \
430 	Y(fld); \
431 	put64((int64_t)((float)s.fld/etime + 0.5) * PAGE_SIZE, l, c, w, 0)
432 #define MAXFAIL 5
433 
434 #define CPUSTATES 5
435 static	const char cpuchar[5] = { '=' , '+', '>', '-', ' ' };
436 
437 static	const size_t cpuoffsets[] = {
438 	offsetof(struct kinfo_cputime, cp_sys),
439 	offsetof(struct kinfo_cputime, cp_intr),
440 	offsetof(struct kinfo_cputime, cp_user),
441 	offsetof(struct kinfo_cputime, cp_nice),
442 	offsetof(struct kinfo_cputime, cp_idle)
443 };
444 
445 void
446 showkre(void)
447 {
448 	float f1, f2;
449 	int psiz;
450 	int i, j, lc;
451 	long inttotal;
452 	long l;
453 	static int failcnt = 0;
454 	double total_time;
455 
456 	etime = 0;
457 	CP_UPDATE(cp_time.cp_user);
458 	CP_UPDATE(cp_time.cp_nice);
459 	CP_UPDATE(cp_time.cp_sys);
460 	CP_UPDATE(cp_time.cp_intr);
461 	CP_UPDATE(cp_time.cp_idle);
462 
463 	total_time = etime;
464 	if (total_time == 0.0)
465 		total_time = 1.0;
466 
467 	if (etime < 100000.0) {	/* < 100ms ignore this trash */
468 		if (failcnt++ >= MAXFAIL) {
469 			clear();
470 			mvprintw(2, 10, "The alternate system clock has died!");
471 			mvprintw(3, 10, "Reverting to ``pigs'' display.");
472 			move(CMDLINE, 0);
473 			refresh();
474 			failcnt = 0;
475 			sleep(5);
476 			command("pigs");
477 		}
478 		return;
479 	}
480 	failcnt = 0;
481 	etime /= 1000000.0;
482 	etime /= ncpu;
483 	if (etime == 0)
484 		etime = 1;
485 	inttotal = 0;
486 	bzero(lacc, nintr * sizeof(*lacc));
487 
488 	for (i = 0; i < nintr; i++) {
489 		if (s.intrcnt[i] == 0)
490 			continue;
491 		j = intralias[i];
492 		if (intrloc[j] == 0) {
493 			if (nextintsrow == LINES)
494 				continue;
495 			intrloc[j] = nextintsrow++;
496 			mvprintw(intrloc[j], INTSCOL + 9, "%-10.10s",
497 				intrname[j]);
498 		}
499 		X(intrcnt);
500 		l = (long)((float)s.intrcnt[i]/etime + 0.5);
501 		lacc[j] += l;
502 		inttotal += l;
503 		put64(lacc[j], intrloc[j], INTSCOL + 3, 5, 'D');
504 	}
505 	put64(inttotal, INTSROW + 1, INTSCOL + 3, 5, 'D');
506 	Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
507 	Z(ncs_longhits); Z(ncs_longmiss); Z(ncs_neghits);
508 	s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
509 	    nchtotal.ncs_miss + nchtotal.ncs_neghits;
510 	s.nchpathcount = nchtotal.ncs_longhits + nchtotal.ncs_longmiss;
511 	if (state == TIME) {
512 		s1.nchcount = s.nchcount;
513 		s1.nchpathcount = s.nchpathcount;
514 	}
515 
516 #define LOADCOLS	49	/* Don't but into the 'free' value */
517 #define LOADRANGE	(100.0 / LOADCOLS)
518 
519 	psiz = 0;
520 	f2 = 0.0;
521 	for (lc = 0; lc < CPUSTATES; lc++) {
522 		uint64_t val = *(uint64_t *)(((uint8_t *)&s.cp_time) +
523 			       cpuoffsets[lc]);
524 		f1 = 100.0 * val / total_time;
525 		f2 += f1;
526 		l = (int)((f2 + (LOADRANGE / 2.0)) / LOADRANGE) - psiz;
527 		if (f1 > 99.9)
528 			f1 = 99.9;	/* no room to display 100.0 */
529 		putfloat(f1, GRAPHROW, GRAPHCOL + 10 * lc, 4, 1, 0);
530 		move(GRAPHROW + 2, psiz);
531 		psiz += l;
532 		while (l-- > 0)
533 			addch(cpuchar[lc]);
534 	}
535 
536 	put64(ucount(), STATROW, STATCOL, 3, 'D');
537 	putfloat(avenrun[0], STATROW, STATCOL + 18, 6, 2, 0);
538 	putfloat(avenrun[1], STATROW, STATCOL + 25, 6, 2, 0);
539 	putfloat(avenrun[2], STATROW, STATCOL + 32, 6, 2, 0);
540 	mvaddstr(STATROW, STATCOL + 53, buf);
541 #define pgtokb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / 1024)
542 #define pgtomb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / (1024 * 1024))
543 #define pgtob(pg)  (int64_t)((intmax_t)(pg) * vms.v_page_size)
544 
545 	put64(pgtob(vms.v_active_count), MEMROW + 0, MEMCOLA + 7, 6, 0);
546 	put64(pgtob(vms.v_wire_count), MEMROW + 1, MEMCOLA + 7, 6, 0); /*XXX*/
547 	put64(pgtob(vms.v_inactive_count +
548 		    vms.v_cache_count +
549 		    vms.v_free_count), MEMROW + 2, MEMCOLA + 7, 6, 0);
550 	put64(s.physmem, MEMROW + 3, MEMCOLA + 7, 6, 0);
551 	put64(pgtob(total.t_arm),
552 			MEMROW + 0, MEMCOLB + 5, 6, 0);
553 	put64(pgtob(total.t_avm + total.t_avmshr),
554 			MEMROW + 0, MEMCOLB + 19, 6, 0);
555 	put64(pgtob(total.t_vm - total.t_rm),
556 			MEMROW + 1, MEMCOLB + 5, 6, 0);
557 	put64(pgtob(s.kvmsw[kvnsw].ksw_total),
558 			MEMROW + 1, MEMCOLB + 19, 6, 0);
559 
560 #if 0
561 	put64(pgtob(total.t_arm), MEMROW + 2, MEMCOL + 4, 6, 0);
562 	put64(pgtob(total.t_armshr), MEMROW + 2, MEMCOL + 11, 6, 0);
563 	put64(pgtob(total.t_avm), MEMROW + 2, MEMCOL + 19, 6, 0);
564 	put64(pgtob(total.t_avmshr), MEMROW + 2, MEMCOL + 26, 6, 0);
565 	put64(pgtob(total.t_rm), MEMROW + 3, MEMCOL + 4, 6, 0);
566 	put64(pgtob(total.t_rmshr), MEMROW + 3, MEMCOL + 11, 6, 0);
567 	put64(pgtob(total.t_vm), MEMROW + 3, MEMCOL + 19, 6, 0);
568 	put64(pgtob(total.t_vmshr), MEMROW + 3, MEMCOL + 26, 6, 0);
569 	put64(pgtob(total.t_free), MEMROW + 2, MEMCOL + 34, 6, 0);
570 #endif
571 
572 	put64(total.t_rq - 1, PROCSROW + 1, PROCSCOL + 0, 4, 'D');
573 	put64(total.t_pw, PROCSROW + 1, PROCSCOL + 4, 4, 'D');
574 	put64(total.t_dw, PROCSROW + 1, PROCSCOL + 8, 4, 'D');
575 	put64(total.t_sl, PROCSROW + 1, PROCSCOL + 12, 4, 'D');
576 	/*put64(total.t_sw, PROCSROW + 1, PROCSCOL + 12, 3, 'D');*/
577 	if (extended_vm_stats == 0) {
578 		PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 0, VMSTATCOL, 7);
579 	}
580 	PUTRATE_PGTOB(Vmm.v_cow_faults, VMSTATROW + 1, VMSTATCOL, 7);
581 	put64(pgtob(vms.v_wire_count), VMSTATROW + 2, VMSTATCOL, 7, 0);
582 	put64(pgtob(vms.v_active_count), VMSTATROW + 3, VMSTATCOL, 7, 0);
583 	put64(pgtob(vms.v_inactive_count), VMSTATROW + 4, VMSTATCOL, 7, 0);
584 	put64(pgtob(vms.v_cache_count), VMSTATROW + 5, VMSTATCOL, 7, 0);
585 	put64(pgtob(vms.v_free_count), VMSTATROW + 6, VMSTATCOL, 7, 0);
586 	PUTRATE(Vmm.v_dfree, VMSTATROW + 7, VMSTATCOL, 7);
587 	PUTRATE(Vmm.v_pfree, VMSTATROW + 8, VMSTATCOL, 7);
588 	PUTRATE(Vmm.v_reactivated, VMSTATROW + 9, VMSTATCOL, 7);
589 	PUTRATE(Vmm.v_pdwakeups, VMSTATROW + 10, VMSTATCOL, 7);
590 	PUTRATE(Vmm.v_pdpages, VMSTATROW + 11, VMSTATCOL, 7);
591 	PUTRATE(Vmm.v_intrans, VMSTATROW + 12, VMSTATCOL, 7);
592 
593 	if (extended_vm_stats) {
594 		int64_t orig_zfod = s.Vmm.v_zfod;
595 		s.Vmm.v_zfod -= s.Vmm.v_ozfod;
596 		PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 11, VMSTATCOL - 14, 7);
597 		PUTRATE_PGTOB(Vmm.v_ozfod, VMSTATROW + 12, VMSTATCOL - 14, 7);
598 #define nz(x)	((x) ? (x) : 1)
599 		put64((s.Vmm.v_zfod) * 100 / nz(orig_zfod),
600 		    VMSTATROW + 13, VMSTATCOL - 14, 7, 'D');
601 #undef nz
602 		PUTRATE_PGTOB(Vmm.v_tfree, VMSTATROW + 14, VMSTATCOL - 14, 7);
603 	}
604 
605 	put64(s.bufspace, VMSTATROW + 13, VMSTATCOL, 7, 0);
606 	put64(s.dirtybufspace/1024, VMSTATROW + 14, VMSTATCOL, 7, 'K');
607 	put64(s.activevnodes, VMSTATROW + 15, VMSTATCOL, 7, 'D');
608 	put64(s.cachedvnodes, VMSTATROW + 16, VMSTATCOL, 7, 'D');
609 	put64(s.inactivevnodes, VMSTATROW + 17, VMSTATCOL, 7, 'D');
610 	PUTRATE_PGTOB(Vmm.v_vnodepgsin, PAGEROW + 2, PAGECOL + 7, 5);
611 	PUTRATE_PGTOB(Vmm.v_vnodepgsout, PAGEROW + 2, PAGECOL + 13, 5);
612 	PUTRATE_PGTOB(Vmm.v_swappgsin, PAGEROW + 2, PAGECOL + 21, 5);
613 	PUTRATE_PGTOB(Vmm.v_swappgsout, PAGEROW + 2, PAGECOL + 27, 5);
614 	PUTRATE(Vmm.v_vnodein, PAGEROW + 3, PAGECOL + 7, 5);
615 	PUTRATE(Vmm.v_vnodeout, PAGEROW + 3, PAGECOL + 13, 5);
616 	PUTRATE(Vmm.v_swapin, PAGEROW + 3, PAGECOL + 21, 5);
617 	PUTRATE(Vmm.v_swapout, PAGEROW + 3, PAGECOL + 27, 5);
618 	PUTRATE(Vmm.v_swtch, GENSTATROW + 1, GENSTATCOL + 1, 4);
619 	PUTRATE(Vmm.v_trap, GENSTATROW + 1, GENSTATCOL + 6, 4);
620 	PUTRATE(Vmm.v_syscall, GENSTATROW + 1, GENSTATCOL + 11, 4);
621 	PUTRATE(Vmm.v_intr, GENSTATROW + 1, GENSTATCOL + 16, 4);
622 	PUTRATE(Vmm.v_soft, GENSTATROW + 1, GENSTATCOL + 21, 4);
623 	PUTRATE(Vmm.v_vm_faults, GENSTATROW + 1, GENSTATCOL + 26, 4);
624 	mvprintw(DISKROW, DISKCOL + 5, "                              ");
625 	for (i = 0, lc = 0; i < num_devices && lc < MAXDRIVES; i++)
626 		if (dev_select[i].selected) {
627 			char tmpstr[80];
628 			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
629 				dev_select[i].unit_number);
630 			mvprintw(DISKROW, DISKCOL + 5 + 6 * lc,
631 				" %5.5s", tmpstr);
632 			switch(state) {
633 			case TIME:
634 				dinfo(i, ++lc, &cur, &last);
635 				break;
636 			case RUN:
637 				dinfo(i, ++lc, &cur, &run);
638 				break;
639 			case BOOT:
640 				dinfo(i, ++lc, &cur, NULL);
641 				break;
642 			}
643 		}
644 #define nz(x)	((x) ? (x) : 1)
645 	put64(s.nchpathcount, NAMEIROW + 1, NAMEICOL + 6, 6, 'D');
646 	PUTRATE(Vmm.v_exec, EXECROW + 1, EXECCOL, 5);
647 	put64(nchtotal.ncs_longhits, NAMEIROW + 1, NAMEICOL + 13, 6, 'D');
648 	putfloat(nchtotal.ncs_longhits * 100.0 / nz(s.nchpathcount),
649 	    NAMEIROW + 1, NAMEICOL + 19, 4, 0, 0);
650 
651 	putfloat((double)s.nchcount / nz(s.nchpathcount),
652 	    NAMEIROW + 1, NAMEICOL + 27, 5, 2, 1);
653 #undef nz
654 }
655 
656 int
657 cmdkre(const char *cmd, char *args)
658 {
659 	int retval;
660 
661 	if (prefix(cmd, "run")) {
662 		retval = 1;
663 		copyinfo(&s2, &s1);
664 		switch (getdevs(&run)) {
665 		case -1:
666 			errx(1, "%s", devstat_errbuf);
667 			break;
668 		case 1:
669 			num_devices = run.dinfo->numdevs;
670 			generation = run.dinfo->generation;
671 			retval = dscmd("refresh", NULL, MAXDRIVES, &cur);
672 			if (retval == 2)
673 				labelkre();
674 			break;
675 		default:
676 			break;
677 		}
678 		state = RUN;
679 		return (retval);
680 	}
681 	if (prefix(cmd, "boot")) {
682 		state = BOOT;
683 		copyinfo(&z, &s1);
684 		return (1);
685 	}
686 	if (prefix(cmd, "time")) {
687 		state = TIME;
688 		return (1);
689 	}
690 	if (prefix(cmd, "zero")) {
691 		retval = 1;
692 		if (state == RUN) {
693 			getinfo(&s1);
694 			switch (getdevs(&run)) {
695 			case -1:
696 				errx(1, "%s", devstat_errbuf);
697 				break;
698 			case 1:
699 				num_devices = run.dinfo->numdevs;
700 				generation = run.dinfo->generation;
701 				retval = dscmd("refresh",NULL, MAXDRIVES, &cur);
702 				if (retval == 2)
703 					labelkre();
704 				break;
705 			default:
706 				break;
707 			}
708 		}
709 		return (retval);
710 	}
711 	retval = dscmd(cmd, args, MAXDRIVES, &cur);
712 
713 	if (retval == 2)
714 		labelkre();
715 
716 	return(retval);
717 }
718 
719 /* calculate number of users on the system */
720 static int
721 ucount(void)
722 {
723 	struct utmpentry *ep = NULL;	/* avoid gcc warnings */
724 	int nusers = 0;
725 
726 	getutentries(NULL, &ep);
727 	for (; ep; ep = ep->next)
728 		nusers++;
729 
730 	return (nusers);
731 }
732 
733 static void
734 put64(intmax_t n, int l, int lc, int w, int type)
735 {
736 	char b[128];
737 	int isneg;
738 	int i;
739 	int64_t d;
740 	int64_t u;
741 
742 	move(l, lc);
743 	if (n == 0) {
744 		while (w-- > 0)
745 			addch(' ');
746 		return;
747 	}
748 	if (type == 0 || type == 'D')
749 		snprintf(b, sizeof(b), "%*jd", w, n);
750 	else
751 		snprintf(b, sizeof(b), "%*jd%c", w - 1, n, type);
752 	if (strlen(b) <= (size_t)w) {
753 		addstr(b);
754 		return;
755 	}
756 
757 	if (type == 'D')
758 		u = 1000;
759 	else
760 		u = 1024;
761 	if (n < 0) {
762 		n = -n;
763 		isneg = 1;
764 	} else {
765 		isneg = 0;
766 	}
767 
768 	for (d = 1; n / d >= 1000; d *= u) {
769 		switch(type) {
770 		case 'D':
771 		case 0:
772 			type = 'K';
773 			break;
774 		case 'K':
775 			type = 'M';
776 			break;
777 		case 'M':
778 			type = 'G';
779 			break;
780 		case 'G':
781 			type = 'T';
782 			break;
783 		case 'T':
784 			type = 'X';
785 			break;
786 		default:
787 			type = '?';
788 			break;
789 		}
790 	}
791 
792 	i = w - isneg;
793 	if (n / d >= 100)
794 		i -= 3;
795 	else if (n / d >= 10)
796 		i -= 2;
797 	else
798 		i -= 1;
799 	if (i > 4) {
800 		snprintf(b + 64, sizeof(b) - 64, "%jd.%03jd%c",
801 			 n / d, n / (d / 1000) % 1000, type);
802 	} else if (i > 3) {
803 		snprintf(b + 64, sizeof(b) - 64, "%jd.%02jd%c",
804 			 n / d, n / (d / 100) % 100, type);
805 	} else if (i > 2) {
806 		snprintf(b + 64, sizeof(b) - 64, "%jd.%01jd%c",
807 			 n / d, n / (d / 10) % 10, type);
808 	} else {
809 		snprintf(b + 64, sizeof(b) - 64, "%jd%c",
810 			 n / d, type);
811 	}
812 	w -= strlen(b + 64);
813 	i = 64;
814 	if (isneg) {
815 		b[--i] = '-';
816 		--w;
817 	}
818 	while (w > 0) {
819 		--w;
820 		b[--i] = ' ';
821 	}
822 	addstr(b + i);
823 }
824 
825 static void
826 putfloat(double f, int l, int lc, int w, int d, int nz)
827 {
828 	char b[128];
829 
830 	move(l, lc);
831 	if (nz && f == 0.0) {
832 		while (--w >= 0)
833 			addch(' ');
834 		return;
835 	}
836 	snprintf(b, sizeof(b), "%*.*f", w, d, f);
837 	if (strlen(b) > (size_t)w)
838 		snprintf(b, sizeof(b), "%*.0f", w, f);
839 	if (strlen(b) > (size_t)w) {
840 		while (--w >= 0)
841 			addch('*');
842 		return;
843 	}
844 	addstr(b);
845 }
846 
847 static void
848 putlongdouble(long double f, int l, int lc, int w, int d, int nz)
849 {
850 	char b[128];
851 
852 	move(l, lc);
853 	if (nz && f == 0.0) {
854 		while (--w >= 0)
855 			addch(' ');
856 		return;
857 	}
858 	sprintf(b, "%*.*Lf", w, d, f);
859 	if (strlen(b) > (size_t)w)
860 		sprintf(b, "%*.0Lf", w, f);
861 	if (strlen(b) > (size_t)w) {
862 		while (--w >= 0)
863 			addch('*');
864 		return;
865 	}
866 	addstr(b);
867 }
868 
869 static void
870 putlongdoublez(long double f, int l, int lc, int w, int d, int nz)
871 {
872 	char b[128];
873 
874 	if (f == 0.0) {
875 		move(l, lc);
876 		sprintf(b, "%*.*s", w, w, "");
877 		addstr(b);
878 	} else {
879 		putlongdouble(f, l, lc, w, d, nz);
880 	}
881 }
882 
883 static void
884 getinfo(struct Info *ls)
885 {
886 	struct devinfo *tmp_dinfo;
887 	struct nchstats *nch_tmp;
888 	size_t size;
889 	size_t vms_size = sizeof(ls->Vms);
890 	size_t vmm_size = sizeof(ls->Vmm);
891 	size_t nch_size = sizeof(ls->nchstats) * SMP_MAXCPU;
892 	size_t phys_size = sizeof(ls->physmem);
893 
894         kvnsw = kvm_getswapinfo(kd, ls->kvmsw, NKVMSW, 0);
895 
896         if (sysctlbyname("vm.vmstats", &ls->Vms, &vms_size, NULL, 0)) {
897                 perror("sysctlbyname: vm.vmstats");
898                 exit(1);
899         }
900         if (sysctlbyname("vm.vmmeter", &ls->Vmm, &vmm_size, NULL, 0)) {
901                 perror("sysctlbyname: vm.vmstats");
902                 exit(1);
903         }
904         if (sysctlbyname("hw.physmem", &ls->physmem, &phys_size, NULL, 0)) {
905                 perror("sysctlbyname: hw.physmem");
906                 exit(1);
907 	}
908 
909 	if (kinfo_get_sched_cputime(&ls->cp_time))
910 		err(1, "kinfo_get_sched_cputime");
911 	if (kinfo_get_sched_cputime(&cp_time))
912 		err(1, "kinfo_get_sched_cputime");
913 	NREAD(X_BUFFERSPACE, &ls->bufspace, sizeof(ls->bufspace));
914 	NREAD(X_DESIREDVNODES, &ls->maxvnodes, sizeof(ls->maxvnodes));
915 	NREAD(X_CACHEDVNODES, &ls->cachedvnodes, sizeof(ls->cachedvnodes));
916 	NREAD(X_INACTIVEVNODES, &ls->inactivevnodes,
917 						sizeof(ls->inactivevnodes));
918 	NREAD(X_ACTIVEVNODES, &ls->activevnodes, sizeof(ls->activevnodes));
919 	NREAD(X_NUMDIRTYBUFFERS, &ls->dirtybufspace, sizeof(ls->dirtybufspace));
920 
921 	if (nintr) {
922 		size = nintr * sizeof(ls->intrcnt[0]);
923 		sysctlbyname("hw.intrcnt_all", ls->intrcnt, &size, NULL, 0);
924 	}
925 	size = sizeof(ls->Total);
926 	if (sysctlbyname("vm.vmtotal", &ls->Total, &size, NULL, 0) < 0) {
927 		error("Can't get kernel info: %s\n", strerror(errno));
928 		bzero(&ls->Total, sizeof(ls->Total));
929 	}
930 
931 	if ((nch_tmp = malloc(nch_size)) == NULL) {
932 		perror("malloc");
933 		exit(1);
934 	} else {
935 		if (sysctlbyname("vfs.cache.nchstats", nch_tmp, &nch_size, NULL, 0)) {
936 			perror("sysctlbyname vfs.cache.nchstats");
937 			free(nch_tmp);
938 			exit(1);
939 		} else {
940 			if ((nch_tmp = realloc(nch_tmp, nch_size)) == NULL) {
941 				perror("realloc");
942 				exit(1);
943 			}
944 		}
945 	}
946 
947 	if (kinfo_get_cpus(&ncpu))
948 		err(1, "kinfo_get_cpus");
949 	kvm_nch_cpuagg(nch_tmp, &ls->nchstats, ncpu);
950 	free(nch_tmp);
951 
952 	tmp_dinfo = last.dinfo;
953 	last.dinfo = cur.dinfo;
954 	cur.dinfo = tmp_dinfo;
955 
956 	last.busy_time = cur.busy_time;
957 	switch (getdevs(&cur)) {
958 	case -1:
959 		errx(1, "%s", devstat_errbuf);
960 		break;
961 	case 1:
962 		num_devices = cur.dinfo->numdevs;
963 		generation = cur.dinfo->generation;
964 		cmdkre("refresh", NULL);
965 		break;
966 	default:
967 		break;
968 	}
969 }
970 
971 static void
972 allocinfo(struct Info *ls)
973 {
974 	ls->intrcnt = (long *) calloc(nintr, sizeof(long));
975 	if (ls->intrcnt == NULL)
976 		errx(2, "out of memory");
977 }
978 
979 static void
980 copyinfo(struct Info *from, struct Info *to)
981 {
982 	long *intrcnt;
983 
984 	/*
985 	 * time, wds, seek, and xfer are malloc'd so we have to
986 	 * save the pointers before the structure copy and then
987 	 * copy by hand.
988 	 */
989 	intrcnt = to->intrcnt;
990 	*to = *from;
991 
992 	bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int));
993 }
994 
995 static void
996 dinfo(int dn, int lc, struct statinfo *now, struct statinfo *then)
997 {
998 	long double kb_per_transfer;
999 	long double transfers_per_secondr;
1000 	long double transfers_per_secondw;
1001 	long double mb_per_secondr;
1002 	long double mb_per_secondw;
1003 	long double elapsed_time, device_busy;
1004 	int di;
1005 
1006 	di = dev_select[dn].position;
1007 
1008 	elapsed_time = compute_etime(now->busy_time, then ?
1009 				     then->busy_time :
1010 				     now->dinfo->devices[di].dev_creation_time);
1011 
1012 	device_busy =  compute_etime(now->dinfo->devices[di].busy_time, then ?
1013 				     then->dinfo->devices[di].busy_time :
1014 				     now->dinfo->devices[di].dev_creation_time);
1015 
1016 	if (compute_stats(
1017 			  &now->dinfo->devices[di],
1018 			  (then ? &then->dinfo->devices[di] : NULL),
1019 			  elapsed_time,
1020 			  NULL, NULL, NULL,
1021 			  &kb_per_transfer,
1022 			  NULL,
1023 			  NULL,
1024 			  NULL, NULL) != 0)
1025 		errx(1, "%s", devstat_errbuf);
1026 
1027 	if (compute_stats_read(
1028 			  &now->dinfo->devices[di],
1029 			  (then ? &then->dinfo->devices[di] : NULL),
1030 			  elapsed_time,
1031 			  NULL, NULL, NULL,
1032 			  NULL,
1033 			  &transfers_per_secondr,
1034 			  &mb_per_secondr,
1035 			  NULL, NULL) != 0)
1036 		errx(1, "%s", devstat_errbuf);
1037 
1038 	if (compute_stats_write(
1039 			  &now->dinfo->devices[di],
1040 			  (then ? &then->dinfo->devices[di] : NULL),
1041 			  elapsed_time,
1042 			  NULL, NULL, NULL,
1043 			  NULL,
1044 			  &transfers_per_secondw,
1045 			  &mb_per_secondw,
1046 			  NULL, NULL) != 0)
1047 		errx(1, "%s", devstat_errbuf);
1048 
1049 #if 0
1050 	/*
1051 	 * Remove this hack, it no longer works properly and will
1052 	 * report 100% busy in situations where the device is able
1053 	 * to respond to the requests faster than the busy counter's
1054 	 * granularity.
1055 	 */
1056 	if ((device_busy == 0) &&
1057 	    (transfers_per_secondr > 5 || transfers_per_secondw > 5)) {
1058 		/* the device has been 100% busy, fake it because
1059 		 * as long as the device is 100% busy the busy_time
1060 		 * field in the devstat struct is not updated */
1061 		device_busy = elapsed_time;
1062 	}
1063 #endif
1064 	if (device_busy > elapsed_time) {
1065 		/* this normally happens after one or more periods
1066 		 * where the device has been 100% busy, correct it */
1067 		device_busy = elapsed_time;
1068 	}
1069 
1070 	lc = DISKCOL + lc * 6;
1071 	putlongdoublez(kb_per_transfer, DISKROW + 1, lc, 5, 2, 0);
1072 	putlongdoublez(transfers_per_secondr, DISKROW + 2, lc, 5, 0, 0);
1073 	putlongdoublez(mb_per_secondr, DISKROW + 3, lc, 5, 2, 0);
1074 	putlongdoublez(transfers_per_secondw, DISKROW + 4, lc, 5, 0, 0);
1075 	putlongdoublez(mb_per_secondw, DISKROW + 5, lc, 5, 2, 0);
1076 	putlongdouble(device_busy * 100 / elapsed_time,
1077 				      DISKROW + 6, lc, 5, 0, 0);
1078 }
1079