xref: /dragonfly/usr.bin/systat/vmstat.c (revision 71990c18)
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 kinfo_cputime cp_time, old_cp_time;
86 struct statinfo cur, last, run;
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		 20
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 + 0, MEMCOLB, "VM-rss");
322 	mvprintw(MEMROW + 1, MEMCOLB, "VM-swp");
323 	mvprintw(MEMROW + 1, MEMCOLB + 15, "/");
324 
325 	mvprintw(PAGEROW, PAGECOL,     "          VN PAGER    SWAP PAGER ");
326 	mvprintw(PAGEROW + 1, PAGECOL, "          in   out      in   out ");
327 	mvprintw(PAGEROW + 2, PAGECOL, "bytes");
328 	mvprintw(PAGEROW + 3, PAGECOL, "count");
329 
330 	mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
331 	mvprintw(INTSROW + 1, INTSCOL + 9, "total");
332 
333 	mvprintw(VMSTATROW + 1, VMSTATCOL + 8, "cow");
334 	mvprintw(VMSTATROW + 2, VMSTATCOL + 8, "wire");
335 	mvprintw(VMSTATROW + 3, VMSTATCOL + 8, "act");
336 	mvprintw(VMSTATROW + 4, VMSTATCOL + 8, "inact");
337 	mvprintw(VMSTATROW + 5, VMSTATCOL + 8, "cache");
338 	mvprintw(VMSTATROW + 6, VMSTATCOL + 8, "free");
339 	mvprintw(VMSTATROW + 7, VMSTATCOL + 8, "daefr");
340 	mvprintw(VMSTATROW + 8, VMSTATCOL + 8, "prcfr");
341 	mvprintw(VMSTATROW + 9, VMSTATCOL + 8, "react");
342 	mvprintw(VMSTATROW + 10, VMSTATCOL + 8, "pdwake");
343 	mvprintw(VMSTATROW + 11, VMSTATCOL + 8, "pdpgs");
344 	mvprintw(VMSTATROW + 12, VMSTATCOL + 8, "intrn");
345 	mvprintw(VMSTATROW + 13, VMSTATCOL + 8, "buf");
346 	mvprintw(VMSTATROW + 14, VMSTATCOL + 8, "dirtybuf");
347 
348 	mvprintw(VMSTATROW + 15, VMSTATCOL + 8, "activ-vp");
349 	mvprintw(VMSTATROW + 16, VMSTATCOL + 8, "cachd-vp");
350 	mvprintw(VMSTATROW + 17, VMSTATCOL + 8, "inact-vp");
351 
352 	mvprintw(GENSTATROW, GENSTATCOL, "  Csw  Trp  Sys  Int  Sof  Flt");
353 
354 	mvprintw(GRAPHROW, GRAPHCOL,
355 		"  . %%Sys    . %%Intr   . %%User   . %%Nice   . %%Idle");
356 	mvprintw(PROCSROW, PROCSCOL, "   r   p   d   s");
357 	mvprintw(GRAPHROW + 1, GRAPHCOL,
358 		"|    |    |    |    |    |    |    |    |    |    |");
359 
360 	mvprintw(NAMEIROW, NAMEICOL, "Path-lookups   hits   %%    Components");
361 	mvprintw(EXECROW, EXECCOL, "Execs");
362 	mvprintw(DISKROW, DISKCOL, "Disks");
363 	mvprintw(DISKROW + 1, DISKCOL, "KB/t");
364 	mvprintw(DISKROW + 2, DISKCOL, "tpr/s");
365 	mvprintw(DISKROW + 3, DISKCOL, "MBr/s");
366 	mvprintw(DISKROW + 4, DISKCOL, "tpw/s");
367 	mvprintw(DISKROW + 5, DISKCOL, "MBw/s");
368 	mvprintw(DISKROW + 6, DISKCOL, "%% busy");
369 
370 	/*
371 	 * For now, we don't support a fourth disk statistic.  So there's
372 	 * no point in providing a label for it.  If someone can think of a
373 	 * fourth useful disk statistic, there is room to add it.
374 	 */
375 	j = 0;
376 	for (i = 0; i < num_devices && j < MAXDRIVES; i++)
377 		if (dev_select[i].selected) {
378 			char tmpstr[80];
379 			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
380 				dev_select[i].unit_number);
381 			mvprintw(DISKROW, DISKCOL + 5 + 6 * j,
382 				" %5.5s", tmpstr);
383 			j++;
384 		}
385 
386 	if (j <= 4) {
387 		/*
388 		 * room for extended VM stats
389 		 */
390 		mvprintw(VMSTATROW + 11, VMSTATCOL - 6, "nzfod");
391 		mvprintw(VMSTATROW + 12, VMSTATCOL - 6, "ozfod");
392 		mvprintw(VMSTATROW + 13, VMSTATCOL - 6, "%%zslo");
393 		mvprintw(VMSTATROW + 14, VMSTATCOL - 6, "pgfre");
394 		extended_vm_stats = 1;
395 	} else {
396 		extended_vm_stats = 0;
397 		mvprintw(VMSTATROW + 0, VMSTATCOL + 8, "zfod");
398 	}
399 
400 	for (i = 0; i < nintr; i++) {
401 		if (intrloc[i] == 0)
402 			continue;
403 		mvprintw(intrloc[i], INTSCOL + 9, "%-10.10s", intrname[i]);
404 	}
405 }
406 
407 #define CP_UPDATE(fld)	do {	\
408 	uint64_t lt;		\
409 	lt=s.fld;		\
410 	s.fld-=s1.fld;		\
411 	if(state==TIME)		\
412 		s1.fld=lt;	\
413 	lt=fld;			\
414 	fld-=old_##fld;		\
415 	if(state==TIME)		\
416 		old_##fld=lt;	\
417 	etime += s.fld;		\
418 } while(0)
419 #define X(fld)	{t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;}
420 #define Y(fld)	{t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;}
421 #define Z(fld)	{t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \
422 	if(state == TIME) s1.nchstats.fld = t;}
423 #define PUTRATE(fld, l, c, w) \
424 	Y(fld); \
425 	put64((int64_t)((float)s.fld/etime + 0.5), l, c, w, 'D')
426 #define PUTRATE_PGTOB(fld, l, c, w) \
427 	Y(fld); \
428 	put64((int64_t)((float)s.fld/etime + 0.5) * PAGE_SIZE, l, c, w, 0)
429 #define MAXFAIL 5
430 
431 #define CPUSTATES 5
432 static	const char cpuchar[5] = { '=' , '+', '>', '-', ' ' };
433 
434 static	const size_t cpuoffsets[] = {
435 	offsetof(struct kinfo_cputime, cp_sys),
436 	offsetof(struct kinfo_cputime, cp_intr),
437 	offsetof(struct kinfo_cputime, cp_user),
438 	offsetof(struct kinfo_cputime, cp_nice),
439 	offsetof(struct kinfo_cputime, cp_idle)
440 };
441 
442 void
443 showkre(void)
444 {
445 	float f1, f2;
446 	int psiz;
447 	int i, j, lc;
448 	long inttotal;
449 	long l;
450 	static int failcnt = 0;
451 	double total_time;
452 
453 	etime = 0;
454 	CP_UPDATE(cp_time.cp_user);
455 	CP_UPDATE(cp_time.cp_nice);
456 	CP_UPDATE(cp_time.cp_sys);
457 	CP_UPDATE(cp_time.cp_intr);
458 	CP_UPDATE(cp_time.cp_idle);
459 
460 	total_time = etime;
461 	if (total_time == 0.0)
462 		total_time = 1.0;
463 
464 	if (etime < 100000.0) {	/* < 100ms ignore this trash */
465 		if (failcnt++ >= MAXFAIL) {
466 			clear();
467 			mvprintw(2, 10, "The alternate system clock has died!");
468 			mvprintw(3, 10, "Reverting to ``pigs'' display.");
469 			move(CMDLINE, 0);
470 			refresh();
471 			failcnt = 0;
472 			sleep(5);
473 			command("pigs");
474 		}
475 		return;
476 	}
477 	failcnt = 0;
478 	etime /= 1000000.0;
479 	etime /= ncpu;
480 	if (etime == 0)
481 		etime = 1;
482 	inttotal = 0;
483 	bzero(lacc, nintr * sizeof(*lacc));
484 
485 	for (i = 0; i < nintr; i++) {
486 		if (s.intrcnt[i] == 0)
487 			continue;
488 		j = intralias[i];
489 		if (intrloc[j] == 0) {
490 			if (nextintsrow == LINES)
491 				continue;
492 			intrloc[j] = nextintsrow++;
493 			mvprintw(intrloc[j], INTSCOL + 9, "%-10.10s",
494 				intrname[j]);
495 		}
496 		X(intrcnt);
497 		l = (long)((float)s.intrcnt[i]/etime + 0.5);
498 		lacc[j] += l;
499 		inttotal += l;
500 		put64(lacc[j], intrloc[j], INTSCOL + 3, 5, 'D');
501 	}
502 	put64(inttotal, INTSROW + 1, INTSCOL + 3, 5, 'D');
503 	Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
504 	Z(ncs_longhits); Z(ncs_longmiss); Z(ncs_neghits);
505 	s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
506 	    nchtotal.ncs_miss + nchtotal.ncs_neghits;
507 	s.nchpathcount = nchtotal.ncs_longhits + nchtotal.ncs_longmiss;
508 	if (state == TIME) {
509 		s1.nchcount = s.nchcount;
510 		s1.nchpathcount = s.nchpathcount;
511 	}
512 
513 #define LOADCOLS	49	/* Don't but into the 'free' value */
514 #define LOADRANGE	(100.0 / LOADCOLS)
515 
516 	psiz = 0;
517 	f2 = 0.0;
518 	for (lc = 0; lc < CPUSTATES; lc++) {
519 		uint64_t val = *(uint64_t *)(((uint8_t *)&s.cp_time) +
520 			       cpuoffsets[lc]);
521 		f1 = 100.0 * val / total_time;
522 		f2 += f1;
523 		l = (int)((f2 + (LOADRANGE / 2.0)) / LOADRANGE) - psiz;
524 		if (f1 > 99.9)
525 			f1 = 99.9;	/* no room to display 100.0 */
526 		putfloat(f1, GRAPHROW, GRAPHCOL + 10 * lc, 4, 1, 0);
527 		move(GRAPHROW + 2, psiz);
528 		psiz += l;
529 		while (l-- > 0)
530 			addch(cpuchar[lc]);
531 	}
532 
533 	put64(ucount(), STATROW, STATCOL, 3, 'D');
534 	putfloat(avenrun[0], STATROW, STATCOL + 18, 6, 2, 0);
535 	putfloat(avenrun[1], STATROW, STATCOL + 25, 6, 2, 0);
536 	putfloat(avenrun[2], STATROW, STATCOL + 32, 6, 2, 0);
537 	mvaddstr(STATROW, STATCOL + 53, buf);
538 #define pgtokb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / 1024)
539 #define pgtomb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / (1024 * 1024))
540 #define pgtob(pg)  (int64_t)((intmax_t)(pg) * vms.v_page_size)
541 
542 	put64(pgtob(vms.v_active_count), MEMROW + 0, MEMCOLA + 7, 6, 0);
543 	put64(pgtob(vms.v_wire_count), MEMROW + 1, MEMCOLA + 7, 6, 0); /*XXX*/
544 	put64(pgtob(vms.v_inactive_count +
545 		    vms.v_cache_count +
546 		    vms.v_free_count), MEMROW + 2, MEMCOLA + 7, 6, 0);
547 	put64(s.physmem, MEMROW + 3, MEMCOLA + 7, 6, 0);
548 	put64(pgtob(total.t_rm), MEMROW + 0, MEMCOLB + 7, 6, 0);
549 	put64(pgtob(total.t_vm - total.t_rm), MEMROW + 1, MEMCOLB + 7, 6, 0);
550 	put64(pgtob(s.kvmsw[kvnsw].ksw_total), MEMROW + 1, MEMCOLB + 17, 6, 0);
551 
552 #if 0
553 	put64(pgtob(total.t_arm), MEMROW + 2, MEMCOL + 4, 6, 0);
554 	put64(pgtob(total.t_armshr), MEMROW + 2, MEMCOL + 11, 6, 0);
555 	put64(pgtob(total.t_avm), MEMROW + 2, MEMCOL + 19, 6, 0);
556 	put64(pgtob(total.t_avmshr), MEMROW + 2, MEMCOL + 26, 6, 0);
557 	put64(pgtob(total.t_rm), MEMROW + 3, MEMCOL + 4, 6, 0);
558 	put64(pgtob(total.t_rmshr), MEMROW + 3, MEMCOL + 11, 6, 0);
559 	put64(pgtob(total.t_vm), MEMROW + 3, MEMCOL + 19, 6, 0);
560 	put64(pgtob(total.t_vmshr), MEMROW + 3, MEMCOL + 26, 6, 0);
561 	put64(pgtob(total.t_free), MEMROW + 2, MEMCOL + 34, 6, 0);
562 #endif
563 
564 	put64(total.t_rq - 1, PROCSROW + 1, PROCSCOL + 0, 4, 'D');
565 	put64(total.t_pw, PROCSROW + 1, PROCSCOL + 4, 4, 'D');
566 	put64(total.t_dw, PROCSROW + 1, PROCSCOL + 8, 4, 'D');
567 	put64(total.t_sl, PROCSROW + 1, PROCSCOL + 12, 4, 'D');
568 	/*put64(total.t_sw, PROCSROW + 1, PROCSCOL + 12, 3, 'D');*/
569 	if (extended_vm_stats == 0) {
570 		PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 0, VMSTATCOL, 7);
571 	}
572 	PUTRATE_PGTOB(Vmm.v_cow_faults, VMSTATROW + 1, VMSTATCOL, 7);
573 	put64(pgtob(vms.v_wire_count), VMSTATROW + 2, VMSTATCOL, 7, 0);
574 	put64(pgtob(vms.v_active_count), VMSTATROW + 3, VMSTATCOL, 7, 0);
575 	put64(pgtob(vms.v_inactive_count), VMSTATROW + 4, VMSTATCOL, 7, 0);
576 	put64(pgtob(vms.v_cache_count), VMSTATROW + 5, VMSTATCOL, 7, 0);
577 	put64(pgtob(vms.v_free_count), VMSTATROW + 6, VMSTATCOL, 7, 0);
578 	PUTRATE(Vmm.v_dfree, VMSTATROW + 7, VMSTATCOL, 7);
579 	PUTRATE(Vmm.v_pfree, VMSTATROW + 8, VMSTATCOL, 7);
580 	PUTRATE(Vmm.v_reactivated, VMSTATROW + 9, VMSTATCOL, 7);
581 	PUTRATE(Vmm.v_pdwakeups, VMSTATROW + 10, VMSTATCOL, 7);
582 	PUTRATE(Vmm.v_pdpages, VMSTATROW + 11, VMSTATCOL, 7);
583 	PUTRATE(Vmm.v_intrans, VMSTATROW + 12, VMSTATCOL, 7);
584 
585 	if (extended_vm_stats) {
586 		int64_t orig_zfod = s.Vmm.v_zfod;
587 		s.Vmm.v_zfod -= s.Vmm.v_ozfod;
588 		PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 11, VMSTATCOL - 14, 7);
589 		PUTRATE_PGTOB(Vmm.v_ozfod, VMSTATROW + 12, VMSTATCOL - 14, 7);
590 #define nz(x)	((x) ? (x) : 1)
591 		put64((s.Vmm.v_zfod) * 100 / nz(orig_zfod),
592 		    VMSTATROW + 13, VMSTATCOL - 14, 7, 'D');
593 #undef nz
594 		PUTRATE_PGTOB(Vmm.v_tfree, VMSTATROW + 14, VMSTATCOL - 14, 7);
595 	}
596 
597 	put64(s.bufspace, VMSTATROW + 13, VMSTATCOL, 7, 0);
598 	put64(s.dirtybufspace/1024, VMSTATROW + 14, VMSTATCOL, 7, 'k');
599 	put64(s.activevnodes, VMSTATROW + 15, VMSTATCOL, 7, 'D');
600 	put64(s.cachedvnodes, VMSTATROW + 16, VMSTATCOL, 7, 'D');
601 	put64(s.inactivevnodes, VMSTATROW + 17, VMSTATCOL, 7, 'D');
602 	PUTRATE_PGTOB(Vmm.v_vnodepgsin, PAGEROW + 2, PAGECOL + 7, 5);
603 	PUTRATE_PGTOB(Vmm.v_vnodepgsout, PAGEROW + 2, PAGECOL + 13, 5);
604 	PUTRATE_PGTOB(Vmm.v_swappgsin, PAGEROW + 2, PAGECOL + 21, 5);
605 	PUTRATE_PGTOB(Vmm.v_swappgsout, PAGEROW + 2, PAGECOL + 27, 5);
606 	PUTRATE(Vmm.v_vnodein, PAGEROW + 3, PAGECOL + 7, 5);
607 	PUTRATE(Vmm.v_vnodeout, PAGEROW + 3, PAGECOL + 13, 5);
608 	PUTRATE(Vmm.v_swapin, PAGEROW + 3, PAGECOL + 21, 5);
609 	PUTRATE(Vmm.v_swapout, PAGEROW + 3, PAGECOL + 27, 5);
610 	PUTRATE(Vmm.v_swtch, GENSTATROW + 1, GENSTATCOL + 1, 4);
611 	PUTRATE(Vmm.v_trap, GENSTATROW + 1, GENSTATCOL + 6, 4);
612 	PUTRATE(Vmm.v_syscall, GENSTATROW + 1, GENSTATCOL + 11, 4);
613 	PUTRATE(Vmm.v_intr, GENSTATROW + 1, GENSTATCOL + 16, 4);
614 	PUTRATE(Vmm.v_soft, GENSTATROW + 1, GENSTATCOL + 21, 4);
615 	PUTRATE(Vmm.v_vm_faults, GENSTATROW + 1, GENSTATCOL + 26, 4);
616 	mvprintw(DISKROW, DISKCOL + 5, "                              ");
617 	for (i = 0, lc = 0; i < num_devices && lc < MAXDRIVES; i++)
618 		if (dev_select[i].selected) {
619 			char tmpstr[80];
620 			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
621 				dev_select[i].unit_number);
622 			mvprintw(DISKROW, DISKCOL + 5 + 6 * lc,
623 				" %5.5s", tmpstr);
624 			switch(state) {
625 			case TIME:
626 				dinfo(i, ++lc, &cur, &last);
627 				break;
628 			case RUN:
629 				dinfo(i, ++lc, &cur, &run);
630 				break;
631 			case BOOT:
632 				dinfo(i, ++lc, &cur, NULL);
633 				break;
634 			}
635 		}
636 #define nz(x)	((x) ? (x) : 1)
637 	put64(s.nchpathcount, NAMEIROW + 1, NAMEICOL + 6, 6, 'D');
638 	PUTRATE(Vmm.v_exec, EXECROW + 1, EXECCOL, 5);
639 	put64(nchtotal.ncs_longhits, NAMEIROW + 1, NAMEICOL + 13, 6, 'D');
640 	putfloat(nchtotal.ncs_longhits * 100.0 / nz(s.nchpathcount),
641 	    NAMEIROW + 1, NAMEICOL + 19, 4, 0, 0);
642 
643 	putfloat((double)s.nchcount / nz(s.nchpathcount),
644 	    NAMEIROW + 1, NAMEICOL + 27, 5, 2, 1);
645 #undef nz
646 }
647 
648 int
649 cmdkre(const char *cmd, char *args)
650 {
651 	int retval;
652 
653 	if (prefix(cmd, "run")) {
654 		retval = 1;
655 		copyinfo(&s2, &s1);
656 		switch (getdevs(&run)) {
657 		case -1:
658 			errx(1, "%s", devstat_errbuf);
659 			break;
660 		case 1:
661 			num_devices = run.dinfo->numdevs;
662 			generation = run.dinfo->generation;
663 			retval = dscmd("refresh", NULL, MAXDRIVES, &cur);
664 			if (retval == 2)
665 				labelkre();
666 			break;
667 		default:
668 			break;
669 		}
670 		state = RUN;
671 		return (retval);
672 	}
673 	if (prefix(cmd, "boot")) {
674 		state = BOOT;
675 		copyinfo(&z, &s1);
676 		return (1);
677 	}
678 	if (prefix(cmd, "time")) {
679 		state = TIME;
680 		return (1);
681 	}
682 	if (prefix(cmd, "zero")) {
683 		retval = 1;
684 		if (state == RUN) {
685 			getinfo(&s1);
686 			switch (getdevs(&run)) {
687 			case -1:
688 				errx(1, "%s", devstat_errbuf);
689 				break;
690 			case 1:
691 				num_devices = run.dinfo->numdevs;
692 				generation = run.dinfo->generation;
693 				retval = dscmd("refresh",NULL, MAXDRIVES, &cur);
694 				if (retval == 2)
695 					labelkre();
696 				break;
697 			default:
698 				break;
699 			}
700 		}
701 		return (retval);
702 	}
703 	retval = dscmd(cmd, args, MAXDRIVES, &cur);
704 
705 	if (retval == 2)
706 		labelkre();
707 
708 	return(retval);
709 }
710 
711 /* calculate number of users on the system */
712 static int
713 ucount(void)
714 {
715 	struct utmpentry *ep = NULL;	/* avoid gcc warnings */
716 	int nusers = 0;
717 
718 	getutentries(NULL, &ep);
719 	for (; ep; ep = ep->next)
720 		nusers++;
721 
722 	return (nusers);
723 }
724 
725 static void
726 put64(intmax_t n, int l, int lc, int w, int type)
727 {
728 	char b[128];
729 	int isneg;
730 	int i;
731 	int64_t d;
732 	int64_t u;
733 
734 	move(l, lc);
735 	if (n == 0) {
736 		while (w-- > 0)
737 			addch(' ');
738 		return;
739 	}
740 	if (type == 0 || type == 'D')
741 		snprintf(b, sizeof(b), "%*jd", w, n);
742 	else
743 		snprintf(b, sizeof(b), "%*jd%c", w - 1, n, type);
744 	if (strlen(b) <= (size_t)w) {
745 		addstr(b);
746 		return;
747 	}
748 
749 	if (type == 'D')
750 		u = 1000;
751 	else
752 		u = 1024;
753 	if (n < 0) {
754 		n = -n;
755 		isneg = 1;
756 	} else {
757 		isneg = 0;
758 	}
759 
760 	for (d = 1; n / d >= 1000; d *= u) {
761 		switch(type) {
762 		case 'D':
763 		case 0:
764 			type = 'K';
765 			break;
766 		case 'K':
767 			type = 'M';
768 			break;
769 		case 'M':
770 			type = 'G';
771 			break;
772 		case 'G':
773 			type = 'T';
774 			break;
775 		case 'T':
776 			type = 'X';
777 			break;
778 		default:
779 			type = '?';
780 			break;
781 		}
782 	}
783 
784 	i = w - isneg;
785 	if (n / d >= 100)
786 		i -= 3;
787 	else if (n / d >= 10)
788 		i -= 2;
789 	else
790 		i -= 1;
791 	if (i > 4) {
792 		snprintf(b + 64, sizeof(b) - 64, "%jd.%03jd%c",
793 			 n / d, n / (d / 1000) % 1000, type);
794 	} else if (i > 3) {
795 		snprintf(b + 64, sizeof(b) - 64, "%jd.%02jd%c",
796 			 n / d, n / (d / 100) % 100, type);
797 	} else if (i > 2) {
798 		snprintf(b + 64, sizeof(b) - 64, "%jd.%01jd%c",
799 			 n / d, n / (d / 10) % 10, type);
800 	} else {
801 		snprintf(b + 64, sizeof(b) - 64, "%jd%c",
802 			 n / d, type);
803 	}
804 	w -= strlen(b + 64);
805 	i = 64;
806 	if (isneg) {
807 		b[--i] = '-';
808 		--w;
809 	}
810 	while (w > 0) {
811 		--w;
812 		b[--i] = ' ';
813 	}
814 	addstr(b + i);
815 }
816 
817 static void
818 putfloat(double f, int l, int lc, int w, int d, int nz)
819 {
820 	char b[128];
821 
822 	move(l, lc);
823 	if (nz && f == 0.0) {
824 		while (--w >= 0)
825 			addch(' ');
826 		return;
827 	}
828 	snprintf(b, sizeof(b), "%*.*f", w, d, f);
829 	if (strlen(b) > (size_t)w)
830 		snprintf(b, sizeof(b), "%*.0f", w, f);
831 	if (strlen(b) > (size_t)w) {
832 		while (--w >= 0)
833 			addch('*');
834 		return;
835 	}
836 	addstr(b);
837 }
838 
839 static void
840 putlongdouble(long double f, int l, int lc, int w, int d, int nz)
841 {
842 	char b[128];
843 
844 	move(l, lc);
845 	if (nz && f == 0.0) {
846 		while (--w >= 0)
847 			addch(' ');
848 		return;
849 	}
850 	sprintf(b, "%*.*Lf", w, d, f);
851 	if (strlen(b) > (size_t)w)
852 		sprintf(b, "%*.0Lf", w, f);
853 	if (strlen(b) > (size_t)w) {
854 		while (--w >= 0)
855 			addch('*');
856 		return;
857 	}
858 	addstr(b);
859 }
860 
861 static void
862 putlongdoublez(long double f, int l, int lc, int w, int d, int nz)
863 {
864 	char b[128];
865 
866 	if (f == 0.0) {
867 		move(l, lc);
868 		sprintf(b, "%*.*s", w, w, "");
869 		addstr(b);
870 	} else {
871 		putlongdouble(f, l, lc, w, d, nz);
872 	}
873 }
874 
875 static void
876 getinfo(struct Info *ls)
877 {
878 	struct devinfo *tmp_dinfo;
879 	struct nchstats *nch_tmp;
880 	size_t size;
881 	size_t vms_size = sizeof(ls->Vms);
882 	size_t vmm_size = sizeof(ls->Vmm);
883 	size_t nch_size = sizeof(ls->nchstats) * SMP_MAXCPU;
884 	size_t phys_size = sizeof(ls->physmem);
885 
886         kvnsw = kvm_getswapinfo(kd, ls->kvmsw, NKVMSW, 0);
887 
888         if (sysctlbyname("vm.vmstats", &ls->Vms, &vms_size, NULL, 0)) {
889                 perror("sysctlbyname: vm.vmstats");
890                 exit(1);
891         }
892         if (sysctlbyname("vm.vmmeter", &ls->Vmm, &vmm_size, NULL, 0)) {
893                 perror("sysctlbyname: vm.vmstats");
894                 exit(1);
895         }
896         if (sysctlbyname("hw.physmem", &ls->physmem, &phys_size, NULL, 0)) {
897                 perror("sysctlbyname: hw.physmem");
898                 exit(1);
899 	}
900 
901 	if (kinfo_get_sched_cputime(&ls->cp_time))
902 		err(1, "kinfo_get_sched_cputime");
903 	if (kinfo_get_sched_cputime(&cp_time))
904 		err(1, "kinfo_get_sched_cputime");
905 	NREAD(X_BUFFERSPACE, &ls->bufspace, sizeof(ls->bufspace));
906 	NREAD(X_DESIREDVNODES, &ls->maxvnodes, sizeof(ls->maxvnodes));
907 	NREAD(X_CACHEDVNODES, &ls->cachedvnodes, sizeof(ls->cachedvnodes));
908 	NREAD(X_INACTIVEVNODES, &ls->inactivevnodes,
909 						sizeof(ls->inactivevnodes));
910 	NREAD(X_ACTIVEVNODES, &ls->activevnodes, sizeof(ls->activevnodes));
911 	NREAD(X_NUMDIRTYBUFFERS, &ls->dirtybufspace, sizeof(ls->dirtybufspace));
912 
913 	if (nintr) {
914 		size = nintr * sizeof(ls->intrcnt[0]);
915 		sysctlbyname("hw.intrcnt_all", ls->intrcnt, &size, NULL, 0);
916 	}
917 	size = sizeof(ls->Total);
918 	if (sysctlbyname("vm.vmtotal", &ls->Total, &size, NULL, 0) < 0) {
919 		error("Can't get kernel info: %s\n", strerror(errno));
920 		bzero(&ls->Total, sizeof(ls->Total));
921 	}
922 
923 	if ((nch_tmp = malloc(nch_size)) == NULL) {
924 		perror("malloc");
925 		exit(1);
926 	} else {
927 		if (sysctlbyname("vfs.cache.nchstats", nch_tmp, &nch_size, NULL, 0)) {
928 			perror("sysctlbyname vfs.cache.nchstats");
929 			free(nch_tmp);
930 			exit(1);
931 		} else {
932 			if ((nch_tmp = realloc(nch_tmp, nch_size)) == NULL) {
933 				perror("realloc");
934 				exit(1);
935 			}
936 		}
937 	}
938 
939 	if (kinfo_get_cpus(&ncpu))
940 		err(1, "kinfo_get_cpus");
941 	kvm_nch_cpuagg(nch_tmp, &ls->nchstats, ncpu);
942 	free(nch_tmp);
943 
944 	tmp_dinfo = last.dinfo;
945 	last.dinfo = cur.dinfo;
946 	cur.dinfo = tmp_dinfo;
947 
948 	last.busy_time = cur.busy_time;
949 	switch (getdevs(&cur)) {
950 	case -1:
951 		errx(1, "%s", devstat_errbuf);
952 		break;
953 	case 1:
954 		num_devices = cur.dinfo->numdevs;
955 		generation = cur.dinfo->generation;
956 		cmdkre("refresh", NULL);
957 		break;
958 	default:
959 		break;
960 	}
961 }
962 
963 static void
964 allocinfo(struct Info *ls)
965 {
966 	ls->intrcnt = (long *) calloc(nintr, sizeof(long));
967 	if (ls->intrcnt == NULL)
968 		errx(2, "out of memory");
969 }
970 
971 static void
972 copyinfo(struct Info *from, struct Info *to)
973 {
974 	long *intrcnt;
975 
976 	/*
977 	 * time, wds, seek, and xfer are malloc'd so we have to
978 	 * save the pointers before the structure copy and then
979 	 * copy by hand.
980 	 */
981 	intrcnt = to->intrcnt;
982 	*to = *from;
983 
984 	bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int));
985 }
986 
987 static void
988 dinfo(int dn, int lc, struct statinfo *now, struct statinfo *then)
989 {
990 	long double kb_per_transfer;
991 	long double transfers_per_secondr;
992 	long double transfers_per_secondw;
993 	long double mb_per_secondr;
994 	long double mb_per_secondw;
995 	long double elapsed_time, device_busy;
996 	int di;
997 
998 	di = dev_select[dn].position;
999 
1000 	elapsed_time = compute_etime(now->busy_time, then ?
1001 				     then->busy_time :
1002 				     now->dinfo->devices[di].dev_creation_time);
1003 
1004 	device_busy =  compute_etime(now->dinfo->devices[di].busy_time, then ?
1005 				     then->dinfo->devices[di].busy_time :
1006 				     now->dinfo->devices[di].dev_creation_time);
1007 
1008 	if (compute_stats(
1009 			  &now->dinfo->devices[di],
1010 			  (then ? &then->dinfo->devices[di] : NULL),
1011 			  elapsed_time,
1012 			  NULL, NULL, NULL,
1013 			  &kb_per_transfer,
1014 			  NULL,
1015 			  NULL,
1016 			  NULL, NULL) != 0)
1017 		errx(1, "%s", devstat_errbuf);
1018 
1019 	if (compute_stats_read(
1020 			  &now->dinfo->devices[di],
1021 			  (then ? &then->dinfo->devices[di] : NULL),
1022 			  elapsed_time,
1023 			  NULL, NULL, NULL,
1024 			  NULL,
1025 			  &transfers_per_secondr,
1026 			  &mb_per_secondr,
1027 			  NULL, NULL) != 0)
1028 		errx(1, "%s", devstat_errbuf);
1029 
1030 	if (compute_stats_write(
1031 			  &now->dinfo->devices[di],
1032 			  (then ? &then->dinfo->devices[di] : NULL),
1033 			  elapsed_time,
1034 			  NULL, NULL, NULL,
1035 			  NULL,
1036 			  &transfers_per_secondw,
1037 			  &mb_per_secondw,
1038 			  NULL, NULL) != 0)
1039 		errx(1, "%s", devstat_errbuf);
1040 
1041 #if 0
1042 	/*
1043 	 * Remove this hack, it no longer works properly and will
1044 	 * report 100% busy in situations where the device is able
1045 	 * to respond to the requests faster than the busy counter's
1046 	 * granularity.
1047 	 */
1048 	if ((device_busy == 0) &&
1049 	    (transfers_per_secondr > 5 || transfers_per_secondw > 5)) {
1050 		/* the device has been 100% busy, fake it because
1051 		 * as long as the device is 100% busy the busy_time
1052 		 * field in the devstat struct is not updated */
1053 		device_busy = elapsed_time;
1054 	}
1055 #endif
1056 	if (device_busy > elapsed_time) {
1057 		/* this normally happens after one or more periods
1058 		 * where the device has been 100% busy, correct it */
1059 		device_busy = elapsed_time;
1060 	}
1061 
1062 	lc = DISKCOL + lc * 6;
1063 	putlongdoublez(kb_per_transfer, DISKROW + 1, lc, 5, 2, 0);
1064 	putlongdoublez(transfers_per_secondr, DISKROW + 2, lc, 5, 0, 0);
1065 	putlongdoublez(mb_per_secondr, DISKROW + 3, lc, 5, 2, 0);
1066 	putlongdoublez(transfers_per_secondw, DISKROW + 4, lc, 5, 0, 0);
1067 	putlongdoublez(mb_per_secondw, DISKROW + 5, lc, 5, 2, 0);
1068 	putlongdouble(device_busy * 100 / elapsed_time,
1069 				      DISKROW + 6, lc, 5, 0, 0);
1070 }
1071