1/* $NetBSD: vmstat.c,v 1.258 2023/09/09 20:13:54 ad Exp $ */
2
3/*-
4 * Copyright (c) 1998, 2000, 2001, 2007, 2019, 2020
5 *     The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation by:
9 *	- Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10 *	  NASA Ames Research Center.
11 *	- Simon Burge and Luke Mewburn of Wasabi Systems, Inc.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35/*
36 * Copyright (c) 1980, 1986, 1991, 1993
37 *	The Regents of the University of California.  All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 *    may be used to endorse or promote products derived from this software
49 *    without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64#include <sys/cdefs.h>
65#ifndef lint
66__COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1991, 1993\
67 The Regents of the University of California.  All rights reserved.");
68#endif /* not lint */
69
70#ifndef lint
71#if 0
72static char sccsid[] = "@(#)vmstat.c	8.2 (Berkeley) 3/1/95";
73#else
74__RCSID("$NetBSD: vmstat.c,v 1.258 2023/09/09 20:13:54 ad Exp $");
75#endif
76#endif /* not lint */
77
78#define	__POOL_EXPOSE
79#define __NAMECACHE_PRIVATE
80
81#include <sys/param.h>
82#include <sys/types.h>
83#include <sys/mount.h>
84#include <sys/uio.h>
85
86#include <sys/buf.h>
87#include <sys/evcnt.h>
88#include <sys/ioctl.h>
89#include <sys/malloc.h>
90#include <sys/mallocvar.h>
91#include <sys/namei.h>
92#include <sys/pool.h>
93#include <sys/proc.h>
94#include <sys/sched.h>
95#include <sys/socket.h>
96#include <sys/sysctl.h>
97#include <sys/time.h>
98#include <sys/queue.h>
99#include <sys/kernhist.h>
100#include <sys/vnode.h>
101#include <sys/vnode_impl.h>
102#include <sys/uidinfo.h>
103
104#include <uvm/uvm_extern.h>
105#include <uvm/uvm_stat.h>
106
107#include <net/if.h>
108#include <netinet/in.h>
109#include <netinet/in_var.h>
110
111#include <ufs/ufs/inode.h>
112
113#include <nfs/rpcv2.h>
114#include <nfs/nfsproto.h>
115#include <nfs/nfsnode.h>
116
117#include <assert.h>
118#include <ctype.h>
119#include <err.h>
120#include <errno.h>
121#include <fcntl.h>
122#include <kvm.h>
123#include <limits.h>
124#include <nlist.h>
125#undef n_hash
126#include <paths.h>
127#include <signal.h>
128#include <stdio.h>
129#include <stddef.h>
130#include <stdlib.h>
131#include <string.h>
132#include <time.h>
133#include <unistd.h>
134#include <util.h>
135
136#include "drvstats.h"
137
138/*
139 * All this mess will go away once everything is converted.
140 */
141#ifdef __HAVE_CPU_DATA_FIRST
142
143# include <sys/cpu_data.h>
144struct cpu_info {
145	struct cpu_data ci_data;
146};
147#else
148# include <sys/cpu.h>
149#endif
150
151/*
152 * General namelist
153 */
154struct nlist namelist[] =
155{
156#define	X_HZ		0
157	{ .n_name = "_hz" },
158#define	X_STATHZ	1
159	{ .n_name = "_stathz" },
160#define	X_NCHSTATS	2
161	{ .n_name = "_nchstats" },
162#define	X_ALLEVENTS	3
163	{ .n_name = "_allevents" },
164#define	X_POOLHEAD	4
165	{ .n_name = "_pool_head" },
166#define	X_UVMEXP	5
167	{ .n_name = "_uvmexp" },
168#define X_CPU_INFOS	6
169	{ .n_name = "_cpu_infos" },
170#define	X_NL_SIZE	7
171	{ .n_name = NULL },
172};
173
174/*
175 * Namelist for time data.
176 */
177struct nlist timenl[] =
178{
179#define	X_TIMEBASEBIN	0
180	{ .n_name = "_timebasebin" },
181#define	X_TIME_SECOND	1
182	{ .n_name = "_time_second" },
183#define X_TIME		2
184	{ .n_name = "_time" },
185#define	X_TIMENL_SIZE	3
186	{ .n_name = NULL },
187};
188
189/*
190 * Namelist for pre-evcnt interrupt counters.
191 */
192struct nlist intrnl[] =
193{
194#define	X_INTRNAMES	0
195	{ .n_name = "_intrnames" },
196#define	X_EINTRNAMES	1
197	{ .n_name = "_eintrnames" },
198#define	X_INTRCNT	2
199	{ .n_name = "_intrcnt" },
200#define	X_EINTRCNT	3
201	{ .n_name = "_eintrcnt" },
202#define	X_INTRNL_SIZE	4
203	{ .n_name = NULL },
204};
205
206
207/*
208 * Namelist for hash statistics
209 */
210struct nlist hashnl[] =
211{
212#define	X_BUFHASH	0
213	{ .n_name = "_bufhash" },
214#define	X_BUFHASHTBL	1
215	{ .n_name = "_bufhashtbl" },
216#define	X_UIHASH	2
217	{ .n_name = "_uihash" },
218#define	X_UIHASHTBL	3
219	{ .n_name = "_uihashtbl" },
220#define	X_IFADDRHASH	4
221	{ .n_name = "_in_ifaddrhash" },
222#define	X_IFADDRHASHTBL	5
223	{ .n_name = "_in_ifaddrhashtbl" },
224#define	X_VCACHEHASH	6
225	{ .n_name = "_vcache_hashmask" },
226#define	X_VCACHETBL	7
227	{ .n_name = "_vcache_hashtab" },
228#define X_HASHNL_SIZE	8	/* must be last */
229	{ .n_name = NULL },
230};
231
232/*
233 * Namelist for kernel histories
234 */
235struct nlist histnl[] =
236{
237	{ .n_name = "_kern_histories" },
238#define	X_KERN_HISTORIES		0
239	{ .n_name = NULL },
240};
241
242
243#define KILO	1024
244
245struct cpu_counter {
246	uint64_t nintr;
247	uint64_t nsyscall;
248	uint64_t nswtch;
249	uint64_t nfault;
250	uint64_t ntrap;
251	uint64_t nsoft;
252} cpucounter, ocpucounter;
253
254struct	uvmexp_sysctl uvmexp, ouvmexp;
255int	ndrives;
256
257int	winlines = 20;
258
259kvm_t *kd;
260
261
262#define	FORKSTAT	0x001
263#define	INTRSTAT	0x002
264#define	MEMSTAT		0x004
265#define	SUMSTAT		0x008
266#define	EVCNTSTAT	0x010
267#define	VMSTAT		0x020
268#define	HISTLIST	0x040
269#define	HISTDUMP	0x080
270#define	HASHSTAT	0x100
271#define	HASHLIST	0x200
272#define	VMTOTAL		0x400
273#define	POOLCACHESTAT	0x800
274
275/*
276 * Print single word.  `ovflow' is number of characters didn't fit
277 * on the last word.  `fmt' is a format string to print this word.
278 * It must contain asterisk for field width.  `width' is a width
279 * occupied by this word.  `fixed' is a number of constant chars in
280 * `fmt'.  `val' is a value to be printed using format string `fmt'.
281 */
282#define	PRWORD(ovflw, fmt, width, fixed, val) do {	\
283	(ovflw) += printf((fmt),			\
284	    (width) - (fixed) - (ovflw) > 0 ?		\
285	    (width) - (fixed) - (ovflw) : 0,		\
286	    (val)) - (width);				\
287	if ((ovflw) < 0)				\
288		(ovflw) = 0;				\
289} while (0)
290
291void	cpustats(int *);
292void	cpucounters(struct cpu_counter *);
293void	deref_kptr(const void *, void *, size_t, const char *);
294void	drvstats(int *);
295void	doevcnt(int verbose, int type);
296void	dohashstat(int, int, const char *);
297void	dohashstat_sysctl(int, int, const char *);
298void	dointr(int verbose);
299void	dopool(int, int);
300void	dopoolcache(int);
301void	dosum(void);
302void	dovmstat(struct timespec *, int);
303void	print_total_hdr(void);
304void	dovmtotal(struct timespec *, int);
305void	kread(struct nlist *, int, void *, size_t);
306int	kreadc(struct nlist *, int, void *, size_t);
307void	needhdr(int);
308void	getnlist(int);
309long	getuptime(void);
310void	printhdr(void);
311long	pct(u_long, u_long);
312__dead static void	usage(void);
313void	doforkst(void);
314
315void	hist_traverse(int, const char *);
316void	hist_dodump(struct kern_history *);
317void	hist_traverse_sysctl(int, const char *);
318void	hist_dodump_sysctl(int[], unsigned int);
319
320char	**choosedrives(char **);
321
322/* Namelist and memory file names. */
323char	*nlistf, *memf;
324
325/* allow old usage [vmstat 1] */
326#define	BACKWARD_COMPATIBILITY
327
328static const int clockrate_mib[] = { CTL_KERN, KERN_CLOCKRATE };
329static const int vmmeter_mib[] = { CTL_VM, VM_METER };
330static const int uvmexp2_mib[] = { CTL_VM, VM_UVMEXP2 };
331static const int boottime_mib[] = { CTL_KERN, KERN_BOOTTIME };
332
333static int numdisks = 2;
334
335int
336main(int argc, char *argv[])
337{
338	int c, todo, verbose, wide;
339	struct timespec interval;
340	int reps;
341	const char *histname, *hashname;
342	char errbuf[_POSIX2_LINE_MAX];
343
344	histname = hashname = NULL;
345	memf = nlistf = NULL;
346	reps = todo = verbose = wide = 0;
347	interval.tv_sec = 0;
348	interval.tv_nsec = 0;
349	while ((c = getopt(argc, argv, "Cc:efh:HilLM:mN:n:stu:UvWw:")) != -1) {
350		switch (c) {
351		case 'c':
352			reps = atoi(optarg);
353			break;
354		case 'C':
355			todo |= POOLCACHESTAT;
356			break;
357		case 'e':
358			todo |= EVCNTSTAT;
359			break;
360		case 'f':
361			todo |= FORKSTAT;
362			break;
363		case 'h':
364			hashname = optarg;
365			/* FALLTHROUGH */
366		case 'H':
367			todo |= HASHSTAT;
368			break;
369		case 'i':
370			todo |= INTRSTAT;
371			break;
372		case 'l':
373			todo |= HISTLIST;
374			break;
375		case 'L':
376			todo |= HASHLIST;
377			break;
378		case 'M':
379			memf = optarg;
380			break;
381		case 'm':
382			todo |= MEMSTAT;
383			break;
384		case 'N':
385			nlistf = optarg;
386			break;
387		case 'n':
388			numdisks = atoi(optarg);
389			break;
390		case 's':
391			todo |= SUMSTAT;
392			break;
393		case 't':
394			todo |= VMTOTAL;
395			break;
396		case 'u':
397			histname = optarg;
398			/* FALLTHROUGH */
399		case 'U':
400			todo |= HISTDUMP;
401			break;
402		case 'v':
403			verbose++;
404			break;
405		case 'W':
406			wide++;
407			break;
408		case 'w':
409			interval.tv_sec = atol(optarg);
410			break;
411		case '?':
412		default:
413			usage();
414		}
415	}
416	argc -= optind;
417	argv += optind;
418
419	if (todo == 0)
420		todo = VMSTAT;
421
422	if (memf == NULL) {
423		kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
424	} else {
425		kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
426	}
427
428	if (kd == NULL)
429		errx(EXIT_FAILURE, "%s", errbuf);
430
431	if (memf != NULL)
432		getnlist(todo);	/* Only need this if a core is specified. */
433
434	if (todo & VMSTAT) {
435		struct winsize winsize;
436
437		(void)drvinit(0);/* Initialize disk stats, no disks selected. */
438
439		argv = choosedrives(argv);	/* Select disks. */
440		winsize.ws_row = 0;
441		(void)ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
442		if (winsize.ws_row > 0)
443			winlines = winsize.ws_row;
444
445	}
446
447#ifdef	BACKWARD_COMPATIBILITY
448	if (*argv) {
449		interval.tv_sec = atol(*argv);
450		if (*++argv)
451			reps = atoi(*argv);
452	}
453#endif
454
455	if (interval.tv_sec) {
456		if (!reps)
457			reps = -1;
458	} else if (reps)
459		interval.tv_sec = 1;
460
461	/*
462	 * Statistics dumping is incompatible with the default
463	 * VMSTAT/dovmstat() output. So perform the interval/reps handling
464	 * for it here.
465	 */
466	if ((todo & (VMSTAT|VMTOTAL)) == 0) {
467		for (;;) {
468			if (todo & (HISTLIST|HISTDUMP)) {
469				if ((todo & (HISTLIST|HISTDUMP)) ==
470				    (HISTLIST|HISTDUMP))
471					errx(1, "you may list or dump,"
472					    " but not both!");
473				if (memf != NULL)
474					hist_traverse(todo, histname);
475				else
476					hist_traverse_sysctl(todo, histname);
477				(void)putchar('\n');
478			}
479			if (todo & FORKSTAT) {
480				doforkst();
481				(void)putchar('\n');
482			}
483			if (todo & MEMSTAT) {
484				dopool(verbose, wide);
485				(void)putchar('\n');
486			}
487			if (todo & POOLCACHESTAT) {
488				dopoolcache(verbose);
489				(void)putchar('\n');
490			}
491			if (todo & SUMSTAT) {
492				dosum();
493				(void)putchar('\n');
494			}
495			if (todo & INTRSTAT) {
496				dointr(verbose);
497				(void)putchar('\n');
498			}
499			if (todo & EVCNTSTAT) {
500				doevcnt(verbose, EVCNT_TYPE_ANY);
501				(void)putchar('\n');
502			}
503			if (todo & (HASHLIST|HASHSTAT)) {
504				if ((todo & (HASHLIST|HASHSTAT)) ==
505				    (HASHLIST|HASHSTAT))
506					errx(1, "you may list or display,"
507					    " but not both!");
508				dohashstat(verbose, todo, hashname);
509				(void)putchar('\n');
510			}
511
512			fflush(stdout);
513			if (reps >= 0 && --reps <=0)
514				break;
515			(void)nanosleep(&interval, NULL);
516		}
517	} else {
518		if ((todo & (VMSTAT|VMTOTAL)) == (VMSTAT|VMTOTAL)) {
519			errx(1, "you may not both do vmstat and vmtotal");
520		}
521		if (todo & VMSTAT)
522			dovmstat(&interval, reps);
523		if (todo & VMTOTAL)
524			dovmtotal(&interval, reps);
525	}
526	return 0;
527}
528
529void
530getnlist(int todo)
531{
532	static int done = 0;
533	int c;
534	size_t i;
535
536	if ((c = kvm_nlist(kd, namelist)) != 0) {
537		int doexit = 0;
538		if (c == -1)
539			errx(1, "kvm_nlist: %s %s",
540			    "namelist", kvm_geterr(kd));
541		for (i = 0; i < __arraycount(namelist)-1; i++)
542			if (namelist[i].n_type == 0) {
543				if (doexit++ == 0)
544					(void)fprintf(stderr,
545					    "%s: undefined symbols:",
546					    getprogname());
547				(void)fprintf(stderr, " %s",
548				    namelist[i].n_name);
549			}
550		if (doexit) {
551			(void)fputc('\n', stderr);
552			exit(1);
553		}
554	}
555
556	if ((todo & (VMSTAT|INTRSTAT)) && !(done & (VMSTAT))) {
557		done |= VMSTAT;
558		if ((c = kvm_nlist(kd, timenl)) == -1 || c == X_TIMENL_SIZE)
559			errx(1, "kvm_nlist: %s %s", "timenl", kvm_geterr(kd));
560	}
561	if ((todo & (SUMSTAT|INTRSTAT)) && !(done & (SUMSTAT|INTRSTAT))) {
562		done |= SUMSTAT|INTRSTAT;
563		(void) kvm_nlist(kd, intrnl);
564	}
565	if ((todo & (HASHLIST|HASHSTAT)) && !(done & (HASHLIST|HASHSTAT))) {
566		done |= HASHLIST|HASHSTAT;
567		if ((c = kvm_nlist(kd, hashnl)) == -1 || c == X_HASHNL_SIZE)
568			errx(1, "kvm_nlist: %s %s", "hashnl", kvm_geterr(kd));
569	}
570	if ((todo & (HISTLIST|HISTDUMP)) && !(done & (HISTLIST|HISTDUMP))) {
571		done |= HISTLIST|HISTDUMP;
572		if (kvm_nlist(kd, histnl) == -1)
573			errx(1, "kvm_nlist: %s %s", "histnl", kvm_geterr(kd));
574	}
575}
576
577char **
578choosedrives(char **argv)
579{
580	size_t i, j, k;
581
582	/*
583	 * Choose drives to be displayed.  Priority goes to (in order) drives
584	 * supplied as arguments, default drives.  If everything isn't filled
585	 * in and there are drives not taken care of, display the first few
586	 * that fit.
587	 */
588#define	BACKWARD_COMPATIBILITY
589	for (ndrives = 0; *argv; ++argv) {
590#ifdef	BACKWARD_COMPATIBILITY
591		if (isdigit((unsigned char)**argv))
592			break;
593#endif
594		for (i = 0; i < ndrive; i++) {
595			if (strcmp(dr_name[i], *argv))
596				continue;
597			drv_select[i] = 1;
598			++ndrives;
599			break;
600		}
601	}
602
603	/*
604	 * Pick the most active drives.  Must read the stats once before
605	 * sorting so that there is current IO data, before selecting
606	 * just the first 'numdisks' (default 2) drives.
607	 */
608	drvreadstats();
609	for (i = 0; i < ndrive && ndrives < numdisks; i++) {
610		uint64_t high_bytes = 0, bytes;
611
612		k = ndrive;
613		for (j = 0; j < ndrive; j++) {
614			if (drv_select[j])
615				continue;
616			bytes = cur.rbytes[j] + cur.wbytes[j];
617			if (bytes > high_bytes) {
618				high_bytes = bytes;
619				k = j;
620			}
621		}
622		if (k != ndrive) {
623			drv_select[k] = 1;
624			++ndrives;
625		}
626	}
627
628	return (argv);
629}
630
631long
632getuptime(void)
633{
634	static struct timespec boottime;
635	struct timespec now;
636	time_t uptime, nowsec;
637
638	if (memf == NULL) {
639		if (boottime.tv_sec == 0) {
640			size_t buflen = sizeof(boottime);
641			if (sysctl(boottime_mib, __arraycount(boottime_mib),
642			    &boottime, &buflen, NULL, 0) == -1)
643				warn("Can't get boottime");
644		}
645		clock_gettime(CLOCK_REALTIME, &now);
646	} else {
647		if (boottime.tv_sec == 0) {
648			struct bintime bt;
649
650			kread(timenl, X_TIMEBASEBIN, &bt, sizeof(bt));
651			bintime2timespec(&bt, &boottime);
652		}
653		if (kreadc(timenl, X_TIME_SECOND, &nowsec, sizeof(nowsec))) {
654			/*
655			 * XXX this assignment dance can be removed once
656			 * timeval tv_sec is SUS mandated time_t
657			 */
658			now.tv_sec = nowsec;
659			now.tv_nsec = 0;
660		} else {
661			kread(timenl, X_TIME, &now, sizeof(now));
662		}
663	}
664	uptime = now.tv_sec - boottime.tv_sec;
665	if (uptime <= 0 || uptime > 60*60*24*365*10)
666		errx(1, "time makes no sense; namelist must be wrong.");
667	return (uptime);
668}
669
670int	hz, hdrcnt;
671
672void
673print_total_hdr(void)
674{
675
676	(void)printf("procs         memory\n");
677	(void)printf("ru dw pw sl");
678	(void)printf("   total-v  active-v  active-r");
679	(void)printf(" vm-sh avm-sh rm-sh arm-sh free\n");
680	hdrcnt = winlines - 2;
681}
682
683void
684dovmtotal(struct timespec *interval, int reps)
685{
686	struct vmtotal total;
687	size_t size;
688
689	(void)signal(SIGCONT, needhdr);
690
691	for (hdrcnt = 1;;) {
692		if (!--hdrcnt)
693			print_total_hdr();
694		if (memf != NULL) {
695			warnx("Unable to get vmtotals from crash dump.");
696			(void)memset(&total, 0, sizeof(total));
697		} else {
698			size = sizeof(total);
699			if (sysctl(vmmeter_mib, __arraycount(vmmeter_mib),
700			    &total, &size, NULL, 0) == -1) {
701				warn("Can't get vmtotals");
702				(void)memset(&total, 0, sizeof(total));
703			}
704		}
705		(void)printf("%2d ", total.t_rq);
706		(void)printf("%2d ", total.t_dw);
707		(void)printf("%2d ", total.t_pw);
708		(void)printf("%2d ", total.t_sl);
709
710		(void)printf("%9d ", total.t_vm);
711		(void)printf("%9d ", total.t_avm);
712		(void)printf("%9d ", total.t_arm);
713		(void)printf("%5d ", total.t_vmshr);
714		(void)printf("%6d ", total.t_avmshr);
715		(void)printf("%5d ", total.t_rmshr);
716		(void)printf("%6d ", total.t_armshr);
717		(void)printf("%5d",  total.t_free);
718
719		(void)putchar('\n');
720
721		(void)fflush(stdout);
722		if (reps >= 0 && --reps <= 0)
723			break;
724
725		(void)nanosleep(interval, NULL);
726	}
727}
728
729void
730dovmstat(struct timespec *interval, int reps)
731{
732	struct vmtotal total;
733	time_t uptime, halfuptime;
734	size_t size;
735	int pagesize = getpagesize();
736	int ovflw;
737
738	uptime = getuptime();
739	halfuptime = uptime / 2;
740	(void)signal(SIGCONT, needhdr);
741
742	if (memf != NULL) {
743		if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0)
744			kread(namelist, X_STATHZ, &hz, sizeof(hz));
745		if (!hz)
746			kread(namelist, X_HZ, &hz, sizeof(hz));
747	} else {
748		struct clockinfo clockinfo;
749		size = sizeof(clockinfo);
750		if (sysctl(clockrate_mib, 2, &clockinfo, &size, NULL, 0) == -1)
751			err(1, "sysctl kern.clockrate failed");
752		hz = clockinfo.stathz;
753		if (!hz)
754			hz = clockinfo.hz;
755	}
756
757	for (hdrcnt = 1;;) {
758		if (!--hdrcnt)
759			printhdr();
760		/* Read new disk statistics */
761		cpureadstats();
762		drvreadstats();
763		tkreadstats();
764		if (memf != NULL) {
765			struct uvmexp uvmexp_kernel;
766			/*
767			 * XXX Can't do this if we're reading a crash
768			 * XXX dump because they're lazily-calculated.
769			 */
770			warnx("Unable to get vmtotals from crash dump.");
771			(void)memset(&total, 0, sizeof(total));
772			kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel));
773#define COPY(field) uvmexp.field = uvmexp_kernel.field
774			COPY(pdreact);
775			COPY(pageins);
776			COPY(pgswapout);
777			COPY(pdfreed);
778			COPY(pdscans);
779#undef COPY
780		} else {
781			size = sizeof(total);
782			if (sysctl(vmmeter_mib, __arraycount(vmmeter_mib),
783			    &total, &size, NULL, 0) == -1) {
784				warn("Can't get vmtotals");
785				(void)memset(&total, 0, sizeof(total));
786			}
787			size = sizeof(uvmexp);
788			if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp,
789			    &size, NULL, 0) == -1)
790				warn("sysctl vm.uvmexp2 failed");
791		}
792		cpucounters(&cpucounter);
793		ovflw = 0;
794		PRWORD(ovflw, " %*d", 2, 1, total.t_rq - 1);
795		PRWORD(ovflw, " %*d", 2, 1, total.t_dw + total.t_pw);
796#define	pgtok(a) (long)((a) * ((uint32_t)pagesize >> 10))
797#define	rate(x)	(u_long)(((x) + halfuptime) / uptime)	/* round */
798		PRWORD(ovflw, " %*ld", 9, 1, pgtok(total.t_avm));
799		PRWORD(ovflw, " %*ld", 7, 1, pgtok(total.t_free));
800		PRWORD(ovflw, " %*ld", 5, 1,
801		    rate(cpucounter.nfault - ocpucounter.nfault));
802		PRWORD(ovflw, " %*ld", 4, 1,
803		    rate(uvmexp.pdreact - ouvmexp.pdreact));
804		PRWORD(ovflw, " %*ld", 4, 1,
805		    rate(uvmexp.pageins - ouvmexp.pageins));
806		PRWORD(ovflw, " %*ld", 5, 1,
807		    rate(uvmexp.pgswapout - ouvmexp.pgswapout));
808		PRWORD(ovflw, " %*ld", 5, 1,
809		    rate(uvmexp.pdfreed - ouvmexp.pdfreed));
810		PRWORD(ovflw, " %*ld", 6, 2,
811		    rate(uvmexp.pdscans - ouvmexp.pdscans));
812		drvstats(&ovflw);
813		PRWORD(ovflw, " %*ld", 5, 1,
814		    rate(cpucounter.nintr - ocpucounter.nintr));
815		PRWORD(ovflw, " %*ld", 5, 1,
816		    rate(cpucounter.nsyscall - ocpucounter.nsyscall));
817		PRWORD(ovflw, " %*ld", 4, 1,
818		    rate(cpucounter.nswtch - ocpucounter.nswtch));
819		cpustats(&ovflw);
820		(void)putchar('\n');
821		(void)fflush(stdout);
822		if (reps >= 0 && --reps <= 0)
823			break;
824		ouvmexp = uvmexp;
825		ocpucounter = cpucounter;
826		uptime = interval->tv_sec;
827		/*
828		 * We round upward to avoid losing low-frequency events
829		 * (i.e., >= 1 per interval but < 1 per second).
830		 */
831		halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2;
832		(void)nanosleep(interval, NULL);
833	}
834}
835
836void
837printhdr(void)
838{
839	size_t i;
840
841	(void)printf(" procs    memory      page%*s", 23, "");
842	if (ndrives > 0)
843		(void)printf("%s %*sfaults      cpu\n",
844		    ((ndrives > 1) ? "disks" : "disk"),
845		    ((ndrives > 1) ? ndrives * 3 - 4 : 0), "");
846	else
847		(void)printf("%*s  faults   cpu\n",
848		    ndrives * 3, "");
849
850	(void)printf(" r b      avm    fre  flt  re  pi   po   fr   sr ");
851	for (i = 0; i < ndrive; i++)
852		if (drv_select[i])
853			(void)printf("%c%c ", dr_name[i][0],
854			    dr_name[i][strlen(dr_name[i]) - 1]);
855	(void)printf("  in   sy  cs us sy id\n");
856	hdrcnt = winlines - 2;
857}
858
859/*
860 * Force a header to be prepended to the next output.
861 */
862void
863/*ARGSUSED*/
864needhdr(int dummy)
865{
866
867	hdrcnt = 1;
868}
869
870long
871pct(u_long top, u_long bot)
872{
873	long ans;
874
875	if (bot == 0)
876		return (0);
877	ans = (long)((quad_t)top * 100 / bot);
878	return (ans);
879}
880
881#define	PCT(top, bot) (int)pct((u_long)(top), (u_long)(bot))
882
883void
884dosum(void)
885{
886	struct nchstats nch_stats;
887	uint64_t nchtotal;
888	size_t ssize;
889	int active_kernel;
890	struct cpu_counter cc;
891
892	/*
893	 * The "active" and "inactive" variables
894	 * are now estimated by the kernel and sadly
895	 * can not easily be dug out of a crash dump.
896	 */
897	ssize = sizeof(uvmexp);
898	memset(&uvmexp, 0, ssize);
899	active_kernel = (memf == NULL);
900	if (active_kernel) {
901		/* only on active kernel */
902		if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp,
903		    &ssize, NULL, 0) == -1)
904			warn("sysctl vm.uvmexp2 failed");
905	} else {
906		struct uvmexp uvmexp_kernel;
907		struct pool pool, *pp = &pool;
908		struct pool_allocator pa;
909		TAILQ_HEAD(,pool) pool_head;
910		void *addr;
911		uint64_t bytes;
912
913		kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel));
914#define COPY(field) uvmexp.field = uvmexp_kernel.field
915		COPY(pagesize);
916		COPY(ncolors);
917		COPY(npages);
918		COPY(free);
919		COPY(paging);
920		COPY(wired);
921		COPY(reserve_pagedaemon);
922		COPY(reserve_kernel);
923		COPY(anonpages);
924		COPY(filepages);
925		COPY(execpages);
926		COPY(freemin);
927		COPY(freetarg);
928		COPY(wiredmax);
929		COPY(nswapdev);
930		COPY(swpages);
931		COPY(swpginuse);
932		COPY(nswget);
933		COPY(pageins);
934		COPY(pdpageouts);
935		COPY(pgswapin);
936		COPY(pgswapout);
937		COPY(forks);
938		COPY(forks_ppwait);
939		COPY(forks_sharevm);
940		COPY(colorhit);
941		COPY(colormiss);
942		COPY(cpuhit);
943		COPY(cpumiss);
944		COPY(fltnoram);
945		COPY(fltnoanon);
946		COPY(fltpgwait);
947		COPY(fltpgrele);
948		COPY(fltrelck);
949		COPY(fltrelckok);
950		COPY(fltanget);
951		COPY(fltanretry);
952		COPY(fltamcopy);
953		COPY(fltamcopy);
954		COPY(fltnomap);
955		COPY(fltlget);
956		COPY(fltget);
957		COPY(flt_anon);
958		COPY(flt_acow);
959		COPY(flt_obj);
960		COPY(flt_prcopy);
961		COPY(flt_przero);
962		COPY(pdwoke);
963		COPY(pdrevs);
964		COPY(pdfreed);
965		COPY(pdscans);
966		COPY(pdanscan);
967		COPY(pdobscan);
968		COPY(pdreact);
969		COPY(pdbusy);
970		COPY(pdpending);
971		COPY(pddeact);
972		COPY(bootpages);
973#undef COPY
974		kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head));
975		addr = TAILQ_FIRST(&pool_head);
976		uvmexp.poolpages = 0;
977		for (; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist)) {
978			deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed");
979			deref_kptr(pp->pr_alloc, &pa, sizeof(pa),
980			    "pool allocator trashed");
981			bytes = pp->pr_npages * pa.pa_pagesz;
982			if ((pp->pr_roflags & PR_RECURSIVE) != 0)
983				bytes -= (pp->pr_nout * pp->pr_size);
984			uvmexp.poolpages += bytes / uvmexp.pagesize;
985		}
986	}
987
988
989	(void)printf("%9" PRIu64 " bytes per page\n", uvmexp.pagesize);
990
991	(void)printf("%9" PRIu64 " page color%s\n",
992	    uvmexp.ncolors, uvmexp.ncolors == 1 ? "" : "s");
993
994	(void)printf("%9" PRIu64 " pages managed\n", uvmexp.npages);
995	(void)printf("%9" PRIu64 " pages free\n", uvmexp.free);
996	if (active_kernel) {
997		(void)printf("%9" PRIu64 " pages active\n", uvmexp.active);
998		(void)printf("%9" PRIu64 " pages inactive\n", uvmexp.inactive);
999	}
1000	(void)printf("%9" PRIu64 " pages paging\n", uvmexp.paging);
1001	(void)printf("%9" PRIu64 " pages wired\n", uvmexp.wired);
1002	(void)printf("%9" PRIu64 " reserve pagedaemon pages\n",
1003	    uvmexp.reserve_pagedaemon);
1004	(void)printf("%9" PRIu64 " reserve kernel pages\n", uvmexp.reserve_kernel);
1005	(void)printf("%9" PRIu64 " boot kernel pages\n", uvmexp.bootpages);
1006	(void)printf("%9" PRIu64 " kernel pool pages\n", uvmexp.poolpages);
1007	(void)printf("%9" PRIu64 " anonymous pages\n", uvmexp.anonpages);
1008	(void)printf("%9" PRIu64 " cached file pages\n", uvmexp.filepages);
1009	(void)printf("%9" PRIu64 " cached executable pages\n", uvmexp.execpages);
1010
1011	(void)printf("%9" PRIu64 " minimum free pages\n", uvmexp.freemin);
1012	(void)printf("%9" PRIu64 " target free pages\n", uvmexp.freetarg);
1013	(void)printf("%9" PRIu64 " maximum wired pages\n", uvmexp.wiredmax);
1014
1015	(void)printf("%9" PRIu64 " swap devices\n", uvmexp.nswapdev);
1016	(void)printf("%9" PRIu64 " swap pages\n", uvmexp.swpages);
1017	(void)printf("%9" PRIu64 " swap pages in use\n", uvmexp.swpginuse);
1018	(void)printf("%9" PRIu64 " swap allocations\n", uvmexp.nswget);
1019
1020	cpucounters(&cc);
1021
1022	(void)printf("%9" PRIu64 " total faults taken\n", cc.nfault);
1023	(void)printf("%9" PRIu64 " traps\n", cc.ntrap);
1024	(void)printf("%9" PRIu64 " device interrupts\n", cc.nintr);
1025	(void)printf("%9" PRIu64 " CPU context switches\n", cc.nswtch);
1026	(void)printf("%9" PRIu64 " software interrupts\n", cc.nsoft);
1027	(void)printf("%9" PRIu64 " system calls\n", cc.nsyscall);
1028	(void)printf("%9" PRIu64 " pagein requests\n", uvmexp.pageins);
1029	(void)printf("%9" PRIu64 " pageout requests\n", uvmexp.pdpageouts);
1030	(void)printf("%9" PRIu64 " pages swapped in\n", uvmexp.pgswapin);
1031	(void)printf("%9" PRIu64 " pages swapped out\n", uvmexp.pgswapout);
1032	(void)printf("%9" PRIu64 " forks total\n", uvmexp.forks);
1033	(void)printf("%9" PRIu64 " forks blocked parent\n", uvmexp.forks_ppwait);
1034	(void)printf("%9" PRIu64 " forks shared address space with parent\n",
1035	    uvmexp.forks_sharevm);
1036	(void)printf("%9" PRIu64 " pagealloc desired color avail\n",
1037	    uvmexp.colorhit);
1038	(void)printf("%9" PRIu64 " pagealloc desired color not avail\n",
1039	    uvmexp.colormiss);
1040	(void)printf("%9" PRIu64 " pagealloc local cpu avail\n",
1041	    uvmexp.cpuhit);
1042	(void)printf("%9" PRIu64 " pagealloc local cpu not avail\n",
1043	    uvmexp.cpumiss);
1044
1045	(void)printf("%9" PRIu64 " faults with no memory\n", uvmexp.fltnoram);
1046	(void)printf("%9" PRIu64 " faults with no anons\n", uvmexp.fltnoanon);
1047	(void)printf("%9" PRIu64 " faults had to wait on pages\n", uvmexp.fltpgwait);
1048	(void)printf("%9" PRIu64 " faults found released page\n", uvmexp.fltpgrele);
1049	(void)printf("%9" PRIu64 " faults relock (%" PRIu64 " ok)\n", uvmexp.fltrelck,
1050	    uvmexp.fltrelckok);
1051	(void)printf("%9" PRIu64 " anon page faults\n", uvmexp.fltanget);
1052	(void)printf("%9" PRIu64 " anon retry faults\n", uvmexp.fltanretry);
1053	(void)printf("%9" PRIu64 " amap copy faults\n", uvmexp.fltamcopy);
1054	(void)printf("%9" PRIu64 " neighbour anon page faults\n", uvmexp.fltnamap);
1055	(void)printf("%9" PRIu64 " neighbour object page faults\n", uvmexp.fltnomap);
1056	(void)printf("%9" PRIu64 " locked pager get faults\n", uvmexp.fltlget);
1057	(void)printf("%9" PRIu64 " unlocked pager get faults\n", uvmexp.fltget);
1058	(void)printf("%9" PRIu64 " anon faults\n", uvmexp.flt_anon);
1059	(void)printf("%9" PRIu64 " anon copy on write faults\n", uvmexp.flt_acow);
1060	(void)printf("%9" PRIu64 " object faults\n", uvmexp.flt_obj);
1061	(void)printf("%9" PRIu64 " promote copy faults\n", uvmexp.flt_prcopy);
1062	(void)printf("%9" PRIu64 " promote zero fill faults\n", uvmexp.flt_przero);
1063	(void)printf("%9" PRIu64 " faults upgraded lock\n",
1064	    uvmexp.fltup);
1065	(void)printf("%9" PRIu64 " faults couldn't upgrade lock\n",
1066	    uvmexp.fltnoup);
1067
1068	(void)printf("%9" PRIu64 " times daemon wokeup\n",uvmexp.pdwoke);
1069	(void)printf("%9" PRIu64 " revolutions of the clock hand\n", uvmexp.pdrevs);
1070	(void)printf("%9" PRIu64 " pages freed by daemon\n", uvmexp.pdfreed);
1071	(void)printf("%9" PRIu64 " pages scanned by daemon\n", uvmexp.pdscans);
1072	(void)printf("%9" PRIu64 " anonymous pages scanned by daemon\n",
1073	    uvmexp.pdanscan);
1074	(void)printf("%9" PRIu64 " object pages scanned by daemon\n", uvmexp.pdobscan);
1075	(void)printf("%9" PRIu64 " pages reactivated\n", uvmexp.pdreact);
1076	(void)printf("%9" PRIu64 " pages found busy by daemon\n", uvmexp.pdbusy);
1077	(void)printf("%9" PRIu64 " total pending pageouts\n", uvmexp.pdpending);
1078	(void)printf("%9" PRIu64 " pages deactivated\n", uvmexp.pddeact);
1079	(void)printf("%9" PRIu64 " per-cpu stats synced\n", uvmexp.countsyncall);
1080	(void)printf("%9" PRIu64 " anon pages possibly dirty\n", uvmexp.anonunknown);
1081	(void)printf("%9" PRIu64 " anon pages dirty\n", uvmexp.anondirty);
1082	(void)printf("%9" PRIu64 " anon pages clean\n", uvmexp.anonclean);
1083	(void)printf("%9" PRIu64 " file pages possibly dirty\n", uvmexp.fileunknown);
1084	(void)printf("%9" PRIu64 " file pages dirty\n", uvmexp.filedirty);
1085	(void)printf("%9" PRIu64 " file pages clean\n", uvmexp.fileclean);
1086
1087	if (active_kernel) {
1088		ssize = sizeof(nch_stats);
1089		if (sysctlbyname("vfs.namecache_stats", &nch_stats, &ssize,
1090		    NULL, 0)) {
1091			warn("vfs.namecache_stats failed");
1092			memset(&nch_stats, 0, sizeof(nch_stats));
1093		}
1094	} else {
1095		kread(namelist, X_NCHSTATS, &nch_stats, sizeof(nch_stats));
1096	}
1097
1098	nchtotal = nch_stats.ncs_goodhits + nch_stats.ncs_neghits +
1099	    nch_stats.ncs_badhits + nch_stats.ncs_falsehits +
1100	    nch_stats.ncs_miss + nch_stats.ncs_long;
1101	(void)printf("%9" PRIu64 " total name lookups\n", nchtotal);
1102	(void)printf("%9" PRIu64 " good hits\n", nch_stats.ncs_goodhits);
1103	(void)printf("%9" PRIu64 " negative hits\n", nch_stats.ncs_neghits);
1104	(void)printf("%9" PRIu64 " bad hits\n", nch_stats.ncs_badhits);
1105	(void)printf("%9" PRIu64 " false hits\n", nch_stats.ncs_falsehits);
1106	(void)printf("%9" PRIu64 " miss\n", nch_stats.ncs_miss);
1107	(void)printf("%9" PRIu64 " too long\n", nch_stats.ncs_long);
1108	(void)printf("%9" PRIu64 " pass2 hits\n", nch_stats.ncs_pass2);
1109	(void)printf("%9" PRIu64 " 2passes\n", nch_stats.ncs_2passes);
1110	(void)printf("%9" PRIu64 " reverse hits\n", nch_stats.ncs_revhits);
1111	(void)printf("%9" PRIu64 " reverse miss\n", nch_stats.ncs_revmiss);
1112	(void)printf("%9" PRIu64 " access denied\n", nch_stats.ncs_denied);
1113	(void)printf(
1114	    "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n",
1115	    "", PCT(nch_stats.ncs_goodhits, nchtotal),
1116	    PCT(nch_stats.ncs_neghits, nchtotal),
1117	    PCT(nch_stats.ncs_pass2, nchtotal));
1118	(void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
1119	    PCT(nch_stats.ncs_badhits, nchtotal),
1120	    PCT(nch_stats.ncs_falsehits, nchtotal),
1121	    PCT(nch_stats.ncs_long, nchtotal));
1122}
1123
1124void
1125doforkst(void)
1126{
1127	if (memf != NULL) {
1128		struct uvmexp uvmexp_kernel;
1129		kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel));
1130#define COPY(field) uvmexp.field = uvmexp_kernel.field
1131		COPY(forks);
1132		COPY(forks_ppwait);
1133		COPY(forks_sharevm);
1134#undef COPY
1135	} else {
1136		size_t size = sizeof(uvmexp);
1137		if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp,
1138		    &size, NULL, 0) == -1)
1139			warn("sysctl vm.uvmexp2 failed");
1140	}
1141
1142	(void)printf("%" PRIu64 " forks total\n", uvmexp.forks);
1143	(void)printf("%" PRIu64 " forks blocked parent\n", uvmexp.forks_ppwait);
1144	(void)printf("%" PRIu64 " forks shared address space with parent\n",
1145	    uvmexp.forks_sharevm);
1146}
1147
1148void
1149drvstats(int *ovflwp)
1150{
1151	size_t dn;
1152	double dtime;
1153	int ovflw = *ovflwp;
1154
1155	/* Calculate disk stat deltas. */
1156	cpuswap();
1157	drvswap();
1158	tkswap();
1159
1160	for (dn = 0; dn < ndrive; ++dn) {
1161		/* elapsed time for disk stats */
1162		dtime = cur.cp_etime;
1163		if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
1164			dtime = (double)cur.timestamp[dn].tv_sec +
1165				((double)cur.timestamp[dn].tv_usec / (double)1000000);
1166		}
1167
1168		if (!drv_select[dn])
1169	 		continue;
1170		PRWORD(ovflw, " %*.0f", 3, 1,
1171		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
1172	}
1173	*ovflwp = ovflw;
1174}
1175
1176void
1177cpucounters(struct cpu_counter *cc)
1178{
1179	static struct cpu_info **cpu_infos;
1180	static int initialised;
1181	struct cpu_info **slot;
1182
1183	if (memf == NULL) {
1184		cc->nintr = uvmexp.intrs;
1185		cc->nsyscall = uvmexp.syscalls;
1186		cc->nswtch = uvmexp.swtch;
1187		cc->nfault = uvmexp.faults;
1188		cc->ntrap = uvmexp.traps;
1189		cc->nsoft = uvmexp.softs;
1190		return;
1191	}
1192
1193	if (!initialised) {
1194		kread(namelist, X_CPU_INFOS, &cpu_infos, sizeof(cpu_infos));
1195		initialised = 1;
1196	}
1197
1198	slot = cpu_infos;
1199
1200	memset(cc, 0, sizeof(*cc));
1201
1202	for (;;) {
1203		struct cpu_info tci, *ci = NULL;
1204
1205		deref_kptr(slot++, &ci, sizeof(ci), "CPU array trashed");
1206		if (!ci) {
1207			break;
1208		}
1209
1210		if ((size_t)kvm_read(kd, (u_long)ci, &tci, sizeof(tci))
1211		    != sizeof(tci)) {
1212			warnx("Can't read cpu info from %p (%s)",
1213			    ci, kvm_geterr(kd));
1214			memset(cc, 0, sizeof(*cc));
1215			return;
1216		}
1217		cc->nintr += tci.ci_data.cpu_nintr;
1218		cc->nsyscall += tci.ci_data.cpu_nsyscall;
1219		cc->nswtch = tci.ci_data.cpu_nswtch;
1220		cc->nfault = tci.ci_data.cpu_nfault;
1221		cc->ntrap = tci.ci_data.cpu_ntrap;
1222		cc->nsoft = tci.ci_data.cpu_nsoft;
1223	}
1224}
1225
1226void
1227cpustats(int *ovflwp)
1228{
1229	int state;
1230	double pcnt, total;
1231	double stat_us, stat_sy, stat_id;
1232	int ovflw = *ovflwp;
1233
1234	total = 0;
1235	for (state = 0; state < CPUSTATES; ++state)
1236		total += cur.cp_time[state];
1237	if (total)
1238		pcnt = 100 / total;
1239	else
1240		pcnt = 0;
1241	stat_us = (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * pcnt;
1242	stat_sy = (cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * pcnt;
1243	stat_id = cur.cp_time[CP_IDLE] * pcnt;
1244	PRWORD(ovflw, " %*.0f", ((stat_sy >= 100) ? 2 : 3), 1, stat_us);
1245	PRWORD(ovflw, " %*.0f", ((stat_us >= 100 || stat_id >= 100) ? 2 : 3), 1,
1246	    stat_sy);
1247	PRWORD(ovflw, " %*.0f", 3, 1, stat_id);
1248	*ovflwp = ovflw;
1249}
1250
1251void
1252dointr(int verbose)
1253{
1254	unsigned long *intrcnt, *ointrcnt;
1255	unsigned long long inttotal, uptime;
1256	int nintr, inamlen;
1257	char *intrname, *ointrname;
1258
1259	if (memf == NULL) {
1260		doevcnt(verbose, EVCNT_TYPE_INTR);
1261		return;
1262	}
1263
1264	inttotal = 0;
1265	uptime = getuptime();
1266	nintr = intrnl[X_EINTRCNT].n_value - intrnl[X_INTRCNT].n_value;
1267	inamlen = intrnl[X_EINTRNAMES].n_value - intrnl[X_INTRNAMES].n_value;
1268	if (nintr != 0 && inamlen != 0) {
1269		(void)printf("%-34s %16s %8s\n", "interrupt", "total", "rate");
1270
1271		ointrcnt = intrcnt = malloc((size_t)nintr);
1272		ointrname = intrname = malloc((size_t)inamlen);
1273		if (intrcnt == NULL || intrname == NULL)
1274			errx(1, "%s", "");
1275		kread(intrnl, X_INTRCNT, intrcnt, (size_t)nintr);
1276		kread(intrnl, X_INTRNAMES, intrname, (size_t)inamlen);
1277		nintr /= sizeof(long);
1278		while (--nintr >= 0) {
1279			if (*intrcnt || verbose)
1280				(void)printf("%-34s %16llu %8llu\n", intrname,
1281					     (unsigned long long)*intrcnt,
1282					     (unsigned long long)
1283					     (*intrcnt / uptime));
1284			intrname += strlen(intrname) + 1;
1285			inttotal += *intrcnt++;
1286		}
1287		free(ointrcnt);
1288		free(ointrname);
1289	}
1290
1291	doevcnt(verbose, EVCNT_TYPE_INTR);
1292}
1293
1294void
1295doevcnt(int verbose, int type)
1296{
1297	static const char * const evtypes [] = { "misc", "intr", "trap" };
1298	uint64_t counttotal, uptime;
1299	struct evcntlist allevents;
1300	struct evcnt evcnt, *evptr;
1301	size_t evlen_max, total_max, rate_max;
1302	char evgroup[EVCNT_STRING_MAX], evname[EVCNT_STRING_MAX];
1303
1304	counttotal = 0;
1305	uptime = getuptime();
1306
1307	if (memf == NULL) do {
1308		const int mib[4] = { CTL_KERN, KERN_EVCNT, type,
1309		    verbose ? KERN_EVCNT_COUNT_ANY : KERN_EVCNT_COUNT_NONZERO };
1310		size_t buflen0, buflen = 0;
1311		void *buf0, *buf = NULL;
1312		const struct evcnt_sysctl *evs, *last_evs;
1313		for (;;) {
1314			size_t newlen;
1315			int error;
1316			if (buflen)
1317				buf = malloc(buflen);
1318			error = sysctl(mib, __arraycount(mib),
1319			    buf, &newlen, NULL, 0);
1320			if (error) {
1321				err(1, "kern.evcnt");
1322				if (buf)
1323					free(buf);
1324				return;
1325			}
1326			if (newlen <= buflen) {
1327				buflen = newlen;
1328				break;
1329			}
1330			if (buf)
1331				free(buf);
1332			buflen = newlen;
1333		}
1334		buflen0 = buflen;
1335		evs = buf0 = buf;
1336		last_evs = (void *)((char *)buf + buflen);
1337		buflen /= sizeof(uint64_t);
1338		/* calc columns */
1339		evlen_max = 0;
1340		total_max = sizeof("total") - 1;
1341		rate_max = sizeof("rate") - 1;
1342		while (evs < last_evs
1343		    && buflen >= sizeof(*evs)/sizeof(uint64_t)
1344		    && buflen >= evs->ev_len) {
1345			char cbuf[64];
1346			size_t len;
1347			len = strlen(evs->ev_strings + evs->ev_grouplen + 1);
1348			len += evs->ev_grouplen + 1;
1349			if (evlen_max < len)
1350				evlen_max= len;
1351			len = snprintf(cbuf, sizeof(cbuf), "%"PRIu64,
1352			    evs->ev_count);
1353			if (total_max < len)
1354				total_max = len;
1355			len = snprintf(cbuf, sizeof(cbuf), "%"PRIu64,
1356			    evs->ev_count / uptime);
1357			if (rate_max < len)
1358				rate_max = len;
1359			buflen -= evs->ev_len;
1360			evs = (const void *)
1361			    ((const uint64_t *)evs + evs->ev_len);
1362		}
1363
1364		(void)printf(type == EVCNT_TYPE_ANY ?
1365		    "%-*s  %*s %*s %s\n" :
1366		    "%-*s  %*s %*s\n",
1367		    (int)evlen_max, "interrupt",
1368		    (int)total_max, "total",
1369		    (int)rate_max, "rate",
1370		    "type");
1371
1372		buflen = buflen0;
1373		evs = buf0;
1374		last_evs = (void *)((char *)buf + buflen);
1375		buflen /= sizeof(uint64_t);
1376		while (evs < last_evs
1377		    && buflen >= sizeof(*evs)/sizeof(uint64_t)
1378		    && buflen >= evs->ev_len) {
1379			(void)printf(type == EVCNT_TYPE_ANY ?
1380			    "%s %s%*s  %*"PRIu64" %*"PRIu64" %s\n" :
1381			    "%s %s%*s  %*"PRIu64" %*"PRIu64"\n",
1382			    evs->ev_strings,
1383			    evs->ev_strings + evs->ev_grouplen + 1,
1384			    (int)evlen_max - (evs->ev_grouplen + 1
1385			    + evs->ev_namelen), "",
1386			    (int)total_max, evs->ev_count,
1387			    (int)rate_max, evs->ev_count / uptime,
1388			    (evs->ev_type < __arraycount(evtypes) ?
1389			    evtypes[evs->ev_type] : "?"));
1390			buflen -= evs->ev_len;
1391			counttotal += evs->ev_count;
1392			evs = (const void *)
1393			    ((const uint64_t *)evs + evs->ev_len);
1394		}
1395		free(buf);
1396		if (type != EVCNT_TYPE_ANY)
1397			(void)printf("%-*s  %*"PRIu64" %*"PRIu64"\n",
1398			    (int)evlen_max, "Total",
1399			    (int)total_max, counttotal,
1400			    (int)rate_max, counttotal / uptime);
1401		return;
1402	} while (0);
1403
1404	if (type == EVCNT_TYPE_ANY)
1405		(void)printf("%-34s %16s %8s %s\n", "event", "total", "rate",
1406		    "type");
1407
1408	kread(namelist, X_ALLEVENTS, &allevents, sizeof allevents);
1409	evptr = TAILQ_FIRST(&allevents);
1410	while (evptr) {
1411		deref_kptr(evptr, &evcnt, sizeof(evcnt), "event chain trashed");
1412
1413		evptr = TAILQ_NEXT(&evcnt, ev_list);
1414		if (evcnt.ev_count == 0 && !verbose)
1415			continue;
1416		if (type != EVCNT_TYPE_ANY && evcnt.ev_type != type)
1417			continue;
1418
1419		deref_kptr(evcnt.ev_group, evgroup,
1420		    (size_t)evcnt.ev_grouplen + 1, "event chain trashed");
1421		deref_kptr(evcnt.ev_name, evname,
1422		    (size_t)evcnt.ev_namelen + 1, "event chain trashed");
1423
1424		(void)printf(type == EVCNT_TYPE_ANY ?
1425		    "%s %s%*s %16"PRIu64" %8"PRIu64" %s\n" :
1426		    "%s %s%*s %16"PRIu64" %8"PRIu64"\n",
1427		    evgroup, evname,
1428		    34 - (evcnt.ev_grouplen + 1 + evcnt.ev_namelen), "",
1429		    evcnt.ev_count,
1430		    (evcnt.ev_count / uptime),
1431		    (evcnt.ev_type < __arraycount(evtypes) ?
1432			evtypes[evcnt.ev_type] : "?"));
1433
1434		counttotal += evcnt.ev_count;
1435	}
1436	if (type != EVCNT_TYPE_ANY)
1437		(void)printf("%-34s %16"PRIu64" %8"PRIu64"\n",
1438		    "Total", counttotal, counttotal / uptime);
1439}
1440
1441static void
1442dopool_sysctl(int verbose, int wide)
1443{
1444	uint64_t total, inuse, this_total, this_inuse;
1445	struct {
1446		uint64_t pt_nget;
1447		uint64_t pt_nfail;
1448		uint64_t pt_nput;
1449		uint64_t pt_nout;
1450		uint64_t pt_nitems;
1451		uint64_t pt_npagealloc;
1452		uint64_t pt_npagefree;
1453		uint64_t pt_npages;
1454	} pool_totals;
1455	size_t i, len;
1456	int name_len, ovflw;
1457	struct pool_sysctl *pp, *data;
1458	char maxp[32];
1459
1460	data = asysctlbyname("kern.pool", &len);
1461	if (data == NULL)
1462		err(1, "failed to read kern.pool");
1463
1464	memset(&pool_totals, 0, sizeof pool_totals);
1465	total = inuse = 0;
1466	len /= sizeof(*data);
1467
1468	(void)printf("Memory resource pool statistics\n");
1469	(void)printf(
1470	    "%-*s%*s%*s%*s%*s%s%s%*s%*s%*s%s%*s%6s%*s%*s%s%s%s\n",
1471	    wide ? 16 : 11, "Name",
1472	    wide ? 9 : 5, "Size",
1473	    wide ? 13 : 9, "Requests",
1474	    wide ? 8 : 5, "Fail",
1475	    wide ? 13 : 9, "Releases",
1476	    wide ? "    InUse" : "",
1477	    wide ? "    Avail" : "",
1478	    wide ? 11 : 6, "Pgreq",
1479	    wide ? 11 : 6, "Pgrel",
1480	    wide ? 9 : 6, "Npage",
1481	    wide ? "   PageSz" : "",
1482	    wide ? 8 : 6, "Hiwat",
1483	    "Minpg",
1484	    wide ? 9 : 6, "Maxpg",
1485	    wide ? 8 : 5, "Idle",
1486	    wide ? "   Flags" : "",
1487	    wide ? "   Util" : "",
1488	    wide ? "    TotalKB" : "");
1489
1490	name_len = MIN((int)sizeof(pp->pr_wchan), wide ? 16 : 11);
1491	for (i = 0; i < len; ++i) {
1492		pp = &data[i];
1493		if (pp->pr_nget == 0 && !verbose)
1494			continue;
1495		if (pp->pr_maxpages == UINT_MAX)
1496			(void)snprintf(maxp, sizeof(maxp), "inf");
1497		else
1498			(void)snprintf(maxp, sizeof(maxp), "%" PRIu64,
1499			    pp->pr_maxpages);
1500		ovflw = 0;
1501		PRWORD(ovflw, "%-*s", name_len, 0, pp->pr_wchan);
1502		PRWORD(ovflw, " %*" PRIu64, wide ? 9 : 5, 1, pp->pr_size);
1503		PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pp->pr_nget);
1504		pool_totals.pt_nget += pp->pr_nget;
1505		PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pp->pr_nfail);
1506		pool_totals.pt_nfail += pp->pr_nfail;
1507		PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pp->pr_nput);
1508		pool_totals.pt_nput += pp->pr_nput;
1509		if (wide) {
1510			PRWORD(ovflw, " %*" PRIu64, 9, 1, pp->pr_nout);
1511			pool_totals.pt_nout += pp->pr_nout;
1512			PRWORD(ovflw, " %*" PRIu64, 9, 1, pp->pr_nitems);
1513			pool_totals.pt_nitems += pp->pr_nitems;
1514		}
1515		PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pp->pr_npagealloc);
1516		pool_totals.pt_npagealloc += pp->pr_npagealloc;
1517		PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pp->pr_npagefree);
1518		pool_totals.pt_npagefree += pp->pr_npagefree;
1519		PRWORD(ovflw, " %*" PRIu64, wide ? 9 : 6, 1, pp->pr_npages);
1520		pool_totals.pt_npages += pp->pr_npages;
1521		if (wide)
1522			PRWORD(ovflw, " %*" PRIu64, 9, 1, pp->pr_pagesize);
1523		PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 6, 1, pp->pr_hiwat);
1524		PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_minpages);
1525		PRWORD(ovflw, " %*s", wide ? 9 : 6, 1, maxp);
1526		PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pp->pr_nidle);
1527		if (wide)
1528			PRWORD(ovflw, " 0x%0*" PRIx64, 6, 1,
1529			    pp->pr_flags);
1530
1531		this_inuse = pp->pr_nout * pp->pr_size;
1532		this_total = pp->pr_npages * pp->pr_pagesize;
1533		if (pp->pr_flags & PR_RECURSIVE) {
1534			/*
1535			 * Don't count in-use memory, since it's part
1536			 * of another pool and will be accounted for
1537			 * there.
1538			 */
1539			total += (this_total - this_inuse);
1540		} else {
1541			inuse += this_inuse;
1542			total += this_total;
1543		}
1544		if (wide) {
1545			if (this_total == 0) {
1546				(void)printf("   ---");
1547			} else {
1548				(void)printf(" %5.1f%% %10" PRIu64,
1549				    (100.0 * this_inuse) / this_total,
1550				    this_total / KILO);
1551			}
1552		}
1553		(void)printf("\n");
1554	}
1555	ovflw = 0;
1556	PRWORD(ovflw, "%-*s", name_len, 0, "Totals");
1557	PRWORD(ovflw, " %*s", wide ? 9 : 5, 1, "");
1558	PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pool_totals.pt_nget);
1559	PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pool_totals.pt_nfail);
1560	PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pool_totals.pt_nput);
1561	if (wide) {
1562		PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nout);
1563		PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nitems);
1564	}
1565	PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagealloc);
1566	PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagefree);
1567	PRWORD(ovflw, " %*" PRIu64, wide ? 9 : 6, 1, pool_totals.pt_npages);
1568	(void)printf("\n");
1569
1570	inuse /= KILO;
1571	total /= KILO;
1572	(void)printf(
1573	    "\nIn use %" PRIu64 "K, "
1574	    "total allocated %" PRIu64 "K; utilization %.1f%%\n",
1575	    inuse, total, (100.0 * inuse) / total);
1576
1577	free(data);
1578}
1579
1580void
1581dopool(int verbose, int wide)
1582{
1583	int first, ovflw;
1584	void *addr;
1585	uint64_t total, inuse, this_total, this_inuse;
1586	struct {
1587		uint64_t pt_nget;
1588		uint64_t pt_nfail;
1589		uint64_t pt_nput;
1590		uint64_t pt_nout;
1591		uint64_t pt_nitems;
1592		uint64_t pt_npagealloc;
1593		uint64_t pt_npagefree;
1594		uint64_t pt_npages;
1595	} pool_totals;
1596	TAILQ_HEAD(,pool) pool_head;
1597	struct pool pool, *pp = &pool;
1598	struct pool_allocator pa;
1599	char maxp[32], name[32];
1600
1601	if (memf == NULL) {
1602		dopool_sysctl(verbose, wide);
1603		return;
1604	}
1605
1606	memset(&pool_totals, 0, sizeof pool_totals);
1607	kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head));
1608	addr = TAILQ_FIRST(&pool_head);
1609
1610	total = inuse = 0;
1611
1612	for (first = 1; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist) ) {
1613		deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed");
1614		deref_kptr(pp->pr_alloc, &pa, sizeof(pa),
1615		    "pool allocator trashed");
1616		deref_kptr(pp->pr_wchan, name, sizeof(name),
1617		    "pool wait channel trashed");
1618		name[sizeof(name)-1] = '\0';
1619
1620		if (first) {
1621			(void)printf("Memory resource pool statistics\n");
1622			(void)printf(
1623			    "%-*s%*s%*s%*s%*s%s%s%*s%*s%*s%s%*s%6s%*s%*s%s%s%s\n",
1624			    wide ? 16 : 11, "Name",
1625			    wide ? 9 : 5, "Size",
1626			    wide ? 13 : 9, "Requests",
1627			    wide ? 8 : 5, "Fail",
1628			    wide ? 13 : 9, "Releases",
1629			    wide ? "    InUse" : "",
1630			    wide ? "    Avail" : "",
1631			    wide ? 11 : 6, "Pgreq",
1632			    wide ? 11 : 6, "Pgrel",
1633			    wide ? 9 : 6, "Npage",
1634			    wide ? "   PageSz" : "",
1635			    wide ? 8 : 6, "Hiwat",
1636			    "Minpg",
1637			    wide ? 9 : 6, "Maxpg",
1638			    wide ? 8 : 5, "Idle",
1639			    wide ? "   Flags" : "",
1640			    wide ? "   Util" : "",
1641			    wide ? "    TotalKB" : "");
1642			first = 0;
1643		}
1644		if (pp->pr_nget == 0 && !verbose)
1645			continue;
1646		if (pp->pr_maxpages == UINT_MAX)
1647			(void)snprintf(maxp, sizeof(maxp), "inf");
1648		else
1649			(void)snprintf(maxp, sizeof(maxp), "%u",
1650			    pp->pr_maxpages);
1651		ovflw = 0;
1652		PRWORD(ovflw, "%-*s", wide ? 16 : 11, 0, name);
1653		PRWORD(ovflw, " %*u", wide ? 9 : 5, 1, pp->pr_size);
1654		PRWORD(ovflw, " %*lu", wide ? 13 : 9, 1, pp->pr_nget);
1655		pool_totals.pt_nget += pp->pr_nget;
1656		PRWORD(ovflw, " %*lu", wide ? 8 : 5, 1, pp->pr_nfail);
1657		pool_totals.pt_nfail += pp->pr_nfail;
1658		PRWORD(ovflw, " %*lu", wide ? 13 : 9, 1, pp->pr_nput);
1659		pool_totals.pt_nput += pp->pr_nput;
1660		if (wide) {
1661			PRWORD(ovflw, " %*u", 9, 1, pp->pr_nout);
1662			pool_totals.pt_nout += pp->pr_nout;
1663			PRWORD(ovflw, " %*u", 9, 1, pp->pr_nitems);
1664			pool_totals.pt_nitems += pp->pr_nitems;
1665		}
1666		PRWORD(ovflw, " %*lu", wide ? 11 : 6, 1, pp->pr_npagealloc);
1667		pool_totals.pt_npagealloc += pp->pr_npagealloc;
1668		PRWORD(ovflw, " %*lu", wide ? 11 : 6, 1, pp->pr_npagefree);
1669		pool_totals.pt_npagefree += pp->pr_npagefree;
1670		PRWORD(ovflw, " %*u", wide ? 9 : 6, 1, pp->pr_npages);
1671		pool_totals.pt_npages += pp->pr_npages;
1672		if (wide)
1673			PRWORD(ovflw, " %*u", 9, 1, pa.pa_pagesz);
1674		PRWORD(ovflw, " %*u", wide ? 8 : 6, 1, pp->pr_hiwat);
1675		PRWORD(ovflw, " %*u", 6, 1, pp->pr_minpages);
1676		PRWORD(ovflw, " %*s", wide ? 9 : 6, 1, maxp);
1677		PRWORD(ovflw, " %*lu", wide ? 8 : 5, 1, pp->pr_nidle);
1678		if (wide)
1679			PRWORD(ovflw, " 0x%0*x", 6, 1,
1680			    pp->pr_flags | pp->pr_roflags);
1681
1682		this_inuse = (uint64_t)pp->pr_nout * pp->pr_size;
1683		this_total = (uint64_t)pp->pr_npages * pa.pa_pagesz;
1684		if (pp->pr_roflags & PR_RECURSIVE) {
1685			/*
1686			 * Don't count in-use memory, since it's part
1687			 * of another pool and will be accounted for
1688			 * there.
1689			 */
1690			total += (this_total - this_inuse);
1691		} else {
1692			inuse += this_inuse;
1693			total += this_total;
1694		}
1695		if (wide) {
1696			if (this_total == 0) {
1697				(void)printf("   ---");
1698			} else {
1699				(void)printf(" %5.1f%% %10" PRIu64,
1700				    (100.0 * this_inuse) / this_total,
1701				    this_total / KILO);
1702			}
1703		}
1704		(void)printf("\n");
1705	}
1706	ovflw = 0;
1707	PRWORD(ovflw, "%-*s", wide ? 16 : 11, 0, "Totals");
1708	PRWORD(ovflw, " %*s", wide ? 9 : 5, 1, "");
1709	PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pool_totals.pt_nget);
1710	PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pool_totals.pt_nfail);
1711	PRWORD(ovflw, " %*" PRIu64, wide ? 13 : 9, 1, pool_totals.pt_nput);
1712 	if (wide) {
1713		PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nout);
1714		PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nitems);
1715 	}
1716	PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagealloc);
1717	PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagefree);
1718	PRWORD(ovflw, " %*" PRIu64, wide ? 9 : 6, 1, pool_totals.pt_npages);
1719	(void)printf("\n");
1720
1721	inuse /= KILO;
1722	total /= KILO;
1723	(void)printf(
1724	    "\nIn use %" PRIu64 "K, "
1725	    "total allocated %" PRIu64 "K; utilization %.1f%%\n",
1726	    inuse, total, (100.0 * inuse) / total);
1727}
1728
1729static void
1730dopoolcache_sysctl(int verbose)
1731{
1732	struct pool_sysctl *data, *pp;
1733	size_t i, len;
1734	bool first = true;
1735	int ovflw;
1736	uint64_t tot;
1737	double p;
1738
1739	data = asysctlbyname("kern.pool", &len);
1740	if (data == NULL)
1741		err(1, "failed to read kern.pool");
1742	len /= sizeof(*data);
1743
1744	for (i = 0; i < len; ++i) {
1745		pp = &data[i];
1746		if (pp->pr_cache_meta_size == 0)
1747			continue;
1748
1749		if (pp->pr_cache_nmiss_global == 0 && !verbose)
1750			continue;
1751
1752		if (first) {
1753			(void)printf("Pool cache statistics.\n");
1754			(void)printf("%-*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n",
1755			    12, "Name",
1756			    6, "Spin",
1757			    6, "GrpSz",
1758			    5, "Full",
1759			    5, "Emty",
1760			    10, "PoolLayer",
1761			    11, "CacheLayer",
1762			    6, "Hit%",
1763			    12, "CpuLayer",
1764			    6, "Hit%"
1765			);
1766			first = false;
1767		}
1768
1769		ovflw = 0;
1770		PRWORD(ovflw, "%-*s", MIN((int)sizeof(pp->pr_wchan), 13), 1,
1771		    pp->pr_wchan);
1772		PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_cache_ncontended);
1773		PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_cache_meta_size);
1774		PRWORD(ovflw, " %*" PRIu64, 5, 1, pp->pr_cache_nfull);
1775		PRWORD(ovflw, " %*" PRIu64, 5, 1, pp->pr_cache_nempty);
1776		PRWORD(ovflw, " %*" PRIu64, 10, 1, pp->pr_cache_nmiss_global);
1777
1778		tot = pp->pr_cache_nhit_global + pp->pr_cache_nmiss_global;
1779		p = pp->pr_cache_nhit_global * 100.0 / tot;
1780		PRWORD(ovflw, " %*" PRIu64, 11, 1, tot);
1781		PRWORD(ovflw, " %*.1f", 6, 1, p);
1782
1783		tot = pp->pr_cache_nhit_pcpu + pp->pr_cache_nmiss_pcpu;
1784		p = pp->pr_cache_nhit_pcpu * 100.0 / tot;
1785		PRWORD(ovflw, " %*" PRIu64, 12, 1, tot);
1786		PRWORD(ovflw, " %*.1f", 6, 1, p);
1787		printf("\n");
1788	}
1789}
1790
1791void
1792dopoolcache(int verbose)
1793{
1794	struct pool_cache pool_cache, *pc = &pool_cache;
1795	pool_cache_cpu_t cache_cpu, *cc = &cache_cpu;
1796	TAILQ_HEAD(,pool) pool_head;
1797	struct pool pool, *pp = &pool;
1798	char name[32];
1799	uint64_t cpuhit, cpumiss, pchit, pcmiss, contended, tot;
1800	uint32_t nfull;
1801	void *addr;
1802	int first, ovflw;
1803	size_t i;
1804	double p;
1805
1806	if (memf == NULL) {
1807		dopoolcache_sysctl(verbose);
1808		return;
1809	}
1810
1811	kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head));
1812	addr = TAILQ_FIRST(&pool_head);
1813
1814	for (first = 1; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist) ) {
1815		deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed");
1816		if (pp->pr_cache == NULL)
1817			continue;
1818		deref_kptr(pp->pr_wchan, name, sizeof(name),
1819		    "pool wait channel trashed");
1820		deref_kptr(pp->pr_cache, pc, sizeof(*pc), "pool cache trashed");
1821		name[sizeof(name)-1] = '\0';
1822
1823		cpuhit = 0;
1824		cpumiss = 0;
1825		pcmiss = 0;
1826		contended = 0;
1827		nfull = 0;
1828		for (i = 0; i < __arraycount(pc->pc_cpus); i++) {
1829		    	if ((addr = pc->pc_cpus[i]) == NULL)
1830		    		continue;
1831			deref_kptr(addr, cc, sizeof(*cc),
1832			    "pool cache cpu trashed");
1833			cpuhit += cc->cc_hits;
1834			cpumiss += cc->cc_misses;
1835			pcmiss += cc->cc_pcmisses;
1836			nfull += cc->cc_nfull;
1837			contended += cc->cc_contended;
1838		}
1839		pchit = cpumiss - pcmiss;
1840
1841		if (pcmiss == 0 && !verbose)
1842			continue;
1843
1844		if (first) {
1845			(void)printf("Pool cache statistics.\n");
1846			(void)printf("%-*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n",
1847			    12, "Name",
1848			    6, "Spin",
1849			    6, "GrpSz",
1850			    5, "Full",
1851			    5, "Emty",
1852			    10, "PoolLayer",
1853			    11, "CacheLayer",
1854			    6, "Hit%",
1855			    12, "CpuLayer",
1856			    6, "Hit%"
1857			);
1858			first = 0;
1859		}
1860
1861		ovflw = 0;
1862		PRWORD(ovflw, "%-*s", 13, 1, name);
1863		PRWORD(ovflw, " %*llu", 6, 1, (long long)contended);
1864		PRWORD(ovflw, " %*u", 6, 1, pc->pc_pcgsize);
1865		PRWORD(ovflw, " %*u", 5, 1, nfull);
1866		PRWORD(ovflw, " %*u", 5, 1, 0);
1867		PRWORD(ovflw, " %*llu", 10, 1, (long long)pcmiss);
1868
1869		tot = pchit + pcmiss;
1870		p = pchit * 100.0 / (tot);
1871		PRWORD(ovflw, " %*llu", 11, 1, (long long)tot);
1872		PRWORD(ovflw, " %*.1f", 6, 1, p);
1873
1874		tot = cpuhit + cpumiss;
1875		p = cpuhit * 100.0 / (tot);
1876		PRWORD(ovflw, " %*llu", 12, 1, (long long)tot);
1877		PRWORD(ovflw, " %*.1f", 6, 1, p);
1878		printf("\n");
1879	}
1880}
1881
1882enum hashtype {			/* from <sys/systm.h> */
1883	HASH_LIST,
1884	HASH_SLIST,
1885	HASH_TAILQ,
1886	HASH_PSLIST
1887};
1888
1889struct kernel_hash {
1890	const char *	description;	/* description */
1891	int		hashsize;	/* nlist index for hash size */
1892	int		hashtbl;	/* nlist index for hash table */
1893	enum hashtype	type;		/* type of hash table */
1894	size_t		offset;		/* offset of {LIST,TAILQ}_NEXT */
1895} khashes[] =
1896{
1897	{
1898		"buffer hash",
1899		X_BUFHASH, X_BUFHASHTBL,
1900		HASH_LIST, offsetof(struct buf, b_hash)
1901	}, {
1902		"ipv4 address -> interface hash",
1903		X_IFADDRHASH, X_IFADDRHASHTBL,
1904		HASH_LIST, offsetof(struct in_ifaddr, ia_hash),
1905	}, {
1906		"user info (uid -> used processes) hash",
1907		X_UIHASH, X_UIHASHTBL,
1908		HASH_SLIST, offsetof(struct uidinfo, ui_hash),
1909	}, {
1910		"vnode cache hash",
1911		X_VCACHEHASH, X_VCACHETBL,
1912		HASH_SLIST, offsetof(struct vnode_impl, vi_hash),
1913	}, {
1914		NULL, -1, -1, 0, 0,
1915	}
1916};
1917
1918void
1919dohashstat(int verbose, int todo, const char *hashname)
1920{
1921	LIST_HEAD(, generic)	*hashtbl_list;
1922	SLIST_HEAD(, generic)	*hashtbl_slist;
1923	TAILQ_HEAD(, generic)	*hashtbl_tailq;
1924	struct kernel_hash	*curhash;
1925	void	*hashaddr, *hashbuf, *nhashbuf, *nextaddr;
1926	size_t	elemsize, hashbufsize, thissize;
1927	u_long	hashsize, i;
1928	int	used, items, chain, maxchain;
1929
1930	if (memf == NULL) {
1931		dohashstat_sysctl(verbose, todo, hashname);
1932		return;
1933	}
1934
1935	hashbuf = NULL;
1936	hashbufsize = 0;
1937
1938	if (todo & HASHLIST) {
1939		(void)printf("Supported hashes:\n");
1940		for (curhash = khashes; curhash->description; curhash++) {
1941			if (hashnl[curhash->hashsize].n_value == 0 ||
1942			    hashnl[curhash->hashtbl].n_value == 0)
1943				continue;
1944			(void)printf("\t%-16s%s\n",
1945			    hashnl[curhash->hashsize].n_name + 1,
1946			    curhash->description);
1947		}
1948		return;
1949	}
1950
1951	if (hashname != NULL) {
1952		for (curhash = khashes; curhash->description; curhash++) {
1953			if (strcmp(hashnl[curhash->hashsize].n_name + 1,
1954			    hashname) == 0 &&
1955			    hashnl[curhash->hashsize].n_value != 0 &&
1956			    hashnl[curhash->hashtbl].n_value != 0)
1957				break;
1958		}
1959		if (curhash->description == NULL) {
1960			warnx("%s: no such hash", hashname);
1961			return;
1962		}
1963	}
1964
1965	(void)printf(
1966	    "%-16s %8s %8s %8s %8s %8s %8s\n"
1967	    "%-16s %8s %8s %8s %8s %8s %8s\n",
1968	    "", "total", "used", "util", "num", "average", "maximum",
1969	    "hash table", "buckets", "buckets", "%", "items", "chain",
1970	    "chain");
1971
1972	for (curhash = khashes; curhash->description; curhash++) {
1973		if (hashnl[curhash->hashsize].n_value == 0 ||
1974		    hashnl[curhash->hashtbl].n_value == 0)
1975			continue;
1976		if (hashname != NULL &&
1977		    strcmp(hashnl[curhash->hashsize].n_name + 1, hashname))
1978			continue;
1979		switch (curhash->type) {
1980		case HASH_LIST:
1981			elemsize = sizeof(*hashtbl_list);
1982			break;
1983		case HASH_SLIST:
1984			elemsize = sizeof(*hashtbl_slist);
1985			break;
1986		case HASH_TAILQ:
1987			elemsize = sizeof(*hashtbl_tailq);
1988			break;
1989		default:
1990			/* shouldn't get here */
1991			continue;
1992		}
1993		deref_kptr((void *)hashnl[curhash->hashsize].n_value,
1994		    &hashsize, sizeof(hashsize),
1995		    hashnl[curhash->hashsize].n_name);
1996		hashsize++;
1997		deref_kptr((void *)hashnl[curhash->hashtbl].n_value,
1998		    &hashaddr, sizeof(hashaddr),
1999		    hashnl[curhash->hashtbl].n_name);
2000		if (verbose)
2001			(void)printf(
2002			    "%s %lu, %s %p, offset %ld, elemsize %llu\n",
2003			    hashnl[curhash->hashsize].n_name + 1, hashsize,
2004			    hashnl[curhash->hashtbl].n_name + 1, hashaddr,
2005			    (long)curhash->offset,
2006			    (unsigned long long)elemsize);
2007		thissize = hashsize * elemsize;
2008		if (hashbuf == NULL || thissize > hashbufsize) {
2009			if ((nhashbuf = realloc(hashbuf, thissize)) == NULL)
2010				errx(1, "malloc hashbuf %llu",
2011				    (unsigned long long)hashbufsize);
2012			hashbuf = nhashbuf;
2013			hashbufsize = thissize;
2014		}
2015		deref_kptr(hashaddr, hashbuf, thissize,
2016		    hashnl[curhash->hashtbl].n_name);
2017		used = 0;
2018		items = maxchain = 0;
2019		if (curhash->type == HASH_LIST) {
2020			hashtbl_list = hashbuf;
2021			hashtbl_slist = NULL;
2022			hashtbl_tailq = NULL;
2023		} else if (curhash->type == HASH_SLIST) {
2024			hashtbl_list = NULL;
2025			hashtbl_slist = hashbuf;
2026			hashtbl_tailq = NULL;
2027		} else {
2028			hashtbl_list = NULL;
2029			hashtbl_slist = NULL;
2030			hashtbl_tailq = hashbuf;
2031		}
2032		for (i = 0; i < hashsize; i++) {
2033			if (curhash->type == HASH_LIST)
2034				nextaddr = LIST_FIRST(&hashtbl_list[i]);
2035			else if (curhash->type == HASH_SLIST)
2036				nextaddr = SLIST_FIRST(&hashtbl_slist[i]);
2037			else
2038				nextaddr = TAILQ_FIRST(&hashtbl_tailq[i]);
2039			if (nextaddr == NULL)
2040				continue;
2041			if (verbose)
2042				(void)printf("%5lu: %p\n", i, nextaddr);
2043			used++;
2044			chain = 0;
2045			do {
2046				chain++;
2047				deref_kptr((char *)nextaddr + curhash->offset,
2048				    &nextaddr, sizeof(void *),
2049				    "hash chain corrupted");
2050				if (verbose > 1)
2051					(void)printf("got nextaddr as %p\n",
2052					    nextaddr);
2053			} while (nextaddr != NULL);
2054			items += chain;
2055			if (verbose && chain > 1)
2056				(void)printf("\tchain = %d\n", chain);
2057			if (chain > maxchain)
2058				maxchain = chain;
2059		}
2060		(void)printf("%-16s %8ld %8d %8.2f %8d %8.2f %8d\n",
2061		    hashnl[curhash->hashsize].n_name + 1,
2062		    hashsize, used, used * 100.0 / hashsize,
2063		    items, used ? (double)items / used : 0.0, maxchain);
2064	}
2065}
2066
2067void
2068dohashstat_sysctl(int verbose, int todo, const char *hashname)
2069{
2070	struct hashstat_sysctl hash, *data, *hs;
2071	int mib[3];
2072	int error;
2073	size_t i, len, miblen;
2074
2075
2076	miblen = __arraycount(mib);
2077	error = sysctlnametomib("kern.hashstat", mib, &miblen);
2078	if (error)
2079		err(EXIT_FAILURE, "nametomib kern.hashstat failed");
2080	assert(miblen < 3);
2081
2082	if (todo & HASHLIST) {
2083		mib[miblen] = CTL_DESCRIBE;
2084		miblen++;
2085	};
2086
2087	if (hashname) {
2088		mib[miblen] = CTL_QUERY;
2089		miblen++;
2090		memset(&hash, 0, sizeof(hash));
2091		strlcpy(hash.hash_name, hashname, sizeof(hash.hash_name));
2092		len = sizeof(hash);
2093		error = sysctl(mib, miblen, &hash, &len, &hash, len);
2094		if (error == ENOENT) {
2095			err(1, "hash '%s' not found", hashname);
2096			return;
2097		} else if (error) {
2098			err(1, "sysctl kern.hashstat query failed");
2099			return;
2100		}
2101
2102		data = &hash;
2103		len = 1;
2104	} else {
2105		data = asysctl(mib, miblen, &len);
2106		if (data == NULL)
2107			err(1, "failed to read kern.hashstat");
2108		len /= sizeof(*data);
2109	}
2110
2111	if (todo & HASHLIST) {
2112		printf("Supported hashes:\n");
2113		for (i = 0, hs = data; i < len; i++, hs++) {
2114			printf("\t%-16s%s\n", hs->hash_name, hs->hash_desc);
2115		}
2116	} else {
2117		printf("%-16s %8s %8s %8s %8s %8s %8s\n"
2118		    "%-16s %8s %8s %8s %8s %8s %8s\n",
2119		    "", "total", "used", "util", "num", "average", "maximum",
2120		    "hash table", "buckets", "buckets", "%", "items", "chain",
2121		    "chain");
2122		for (i = 0, hs = data; i < len; i++, hs++) {
2123			printf("%-16s %8"PRId64" %8"PRId64" %8.2f %8"PRId64
2124			    " %8.2f %8"PRId64"\n",
2125			    hs->hash_name, hs->hash_size, hs->hash_used,
2126			    hs->hash_used * 100.0 / hs->hash_size, hs->hash_items,
2127			    hs->hash_used ? (double)hs->hash_items / hs->hash_used : 0.0,
2128			    hs->hash_maxchain);
2129		}
2130	}
2131
2132	if (!hashname && (data != NULL))
2133		free(data);
2134}
2135
2136/*
2137 * kreadc like kread but returns 1 if successful, 0 otherwise
2138 */
2139int
2140kreadc(struct nlist *nl, int nlx, void *addr, size_t size)
2141{
2142	const char *sym;
2143
2144	sym = nl[nlx].n_name;
2145	if (*sym == '_')
2146		++sym;
2147	if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0)
2148		return 0;
2149	deref_kptr((void *)nl[nlx].n_value, addr, size, sym);
2150	return 1;
2151}
2152
2153/*
2154 * kread reads something from the kernel, given its nlist index in namelist[].
2155 */
2156void
2157kread(struct nlist *nl, int nlx, void *addr, size_t size)
2158{
2159	const char *sym;
2160
2161	sym = nl[nlx].n_name;
2162	if (*sym == '_')
2163		++sym;
2164	if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0)
2165		errx(1, "symbol %s not defined", sym);
2166	deref_kptr((void *)nl[nlx].n_value, addr, size, sym);
2167}
2168
2169/*
2170 * Dereference the kernel pointer `kptr' and fill in the local copy
2171 * pointed to by `ptr'.  The storage space must be pre-allocated,
2172 * and the size of the copy passed in `len'.
2173 */
2174void
2175deref_kptr(const void *kptr, void *ptr, size_t len, const char *msg)
2176{
2177
2178	if (*msg == '_')
2179		msg++;
2180	if ((size_t)kvm_read(kd, (u_long)kptr, (char *)ptr, len) != len)
2181		errx(1, "kptr %lx: %s: %s", (u_long)kptr, msg, kvm_geterr(kd));
2182}
2183
2184/*
2185 * Traverse the kernel history buffers, performing the requested action.
2186 *
2187 * Note, we assume that if we're not listing, we're dumping.
2188 */
2189void
2190hist_traverse(int todo, const char *histname)
2191{
2192	struct kern_history_head histhead;
2193	struct kern_history hist, *histkva;
2194	char *name = NULL;
2195	size_t namelen = 0;
2196
2197	if (histnl[0].n_value == 0) {
2198		warnx("kernel history is not compiled into the kernel.");
2199		return;
2200	}
2201
2202	deref_kptr((void *)histnl[X_KERN_HISTORIES].n_value, &histhead,
2203	    sizeof(histhead), histnl[X_KERN_HISTORIES].n_name);
2204
2205	if (histhead.lh_first == NULL) {
2206		warnx("No active kernel history logs.");
2207		return;
2208	}
2209
2210	if (todo & HISTLIST)
2211		(void)printf("Active kernel histories:");
2212
2213	for (histkva = LIST_FIRST(&histhead); histkva != NULL;
2214	    histkva = LIST_NEXT(&hist, list)) {
2215		deref_kptr(histkva, &hist, sizeof(hist), "histkva");
2216		if (name == NULL || hist.namelen > namelen) {
2217			if (name != NULL)
2218				free(name);
2219			namelen = hist.namelen;
2220			if ((name = malloc(namelen + 1)) == NULL)
2221				err(1, "malloc history name");
2222		}
2223
2224		deref_kptr(hist.name, name, namelen, "history name");
2225		name[namelen] = '\0';
2226		if (todo & HISTLIST)
2227			(void)printf(" %s", name);
2228		else {
2229			/*
2230			 * If we're dumping all histories, do it, else
2231			 * check to see if this is the one we want.
2232			 */
2233			if (histname == NULL || strcmp(histname, name) == 0) {
2234				if (histname == NULL)
2235					(void)printf(
2236					    "\nkernel history `%s':\n", name);
2237				hist_dodump(&hist);
2238			}
2239		}
2240	}
2241
2242	if (todo & HISTLIST)
2243		(void)putchar('\n');
2244
2245	if (name != NULL)
2246		free(name);
2247}
2248
2249/*
2250 * Actually dump the history buffer at the specified KVA.
2251 */
2252void
2253hist_dodump(struct kern_history *histp)
2254{
2255	struct kern_history_ent *histents, *e;
2256	struct timeval tv;
2257	size_t histsize;
2258	char *fmt = NULL, *fn = NULL;
2259	size_t fmtlen = 0, fnlen = 0;
2260	unsigned i;
2261
2262	histsize = sizeof(struct kern_history_ent) * histp->n;
2263
2264	if ((histents = malloc(histsize)) == NULL)
2265		err(1, "malloc history entries");
2266
2267	(void)memset(histents, 0, histsize);
2268
2269	(void)printf("%"PRIu32" entries, next is %"PRIu32"\n",
2270	    histp->n, histp->f);
2271
2272	deref_kptr(histp->e, histents, histsize, "history entries");
2273	i = histp->f;
2274	do {
2275		e = &histents[i];
2276		if (e->fmt != NULL) {
2277			if (fmt == NULL || e->fmtlen > fmtlen) {
2278				free(fmt);
2279				fmtlen = e->fmtlen;
2280				if ((fmt = malloc(fmtlen + 1)) == NULL)
2281					err(1, "malloc printf format");
2282			}
2283			if (fn == NULL || e->fnlen > fnlen) {
2284				free(fn);
2285				fnlen = e->fnlen;
2286				if ((fn = malloc(fnlen + 1)) == NULL)
2287					err(1, "malloc function name");
2288			}
2289
2290			deref_kptr(e->fmt, fmt, fmtlen, "printf format");
2291			fmt[fmtlen] = '\0';
2292			for (unsigned z = 0; z < fmtlen - 1; z++) {
2293				if (fmt[z] == '%' && fmt[z+1] == 's')
2294					fmt[z+1] = 'p';
2295			}
2296
2297			deref_kptr(e->fn, fn, fnlen, "function name");
2298			fn[fnlen] = '\0';
2299
2300			bintime2timeval(&e->bt, &tv);
2301			(void)printf("%06ld.%06ld ", (long int)tv.tv_sec,
2302			    (long int)tv.tv_usec);
2303			(void)printf("%s#%" PRId32 "@%" PRId32 ": ",
2304			    fn, e->call, e->cpunum);
2305			(void)printf(fmt, e->v[0], e->v[1], e->v[2], e->v[3]);
2306			(void)putchar('\n');
2307		}
2308		i = (i + 1) % histp->n;
2309	} while (i != histp->f);
2310
2311	free(histents);
2312	free(fmt);
2313	free(fn);
2314}
2315
2316void
2317hist_traverse_sysctl(int todo, const char *histname)
2318{
2319	int error;
2320	int mib[4];
2321	unsigned int i;
2322	size_t len, miblen;
2323	struct sysctlnode query, histnode[32];
2324
2325	/* retrieve names of available histories */
2326	miblen = __arraycount(mib);
2327	error = sysctlnametomib("kern.hist", mib, &miblen);
2328	if (error != 0) {
2329		if (errno == ENOENT) {
2330 			warnx("kernel history is not compiled into the kernel.");
2331			return;
2332		} else
2333			err(EXIT_FAILURE, "nametomib kern.hist failed");
2334	}
2335
2336	/* get the list of nodenames below kern.hist */
2337	mib[2] = CTL_QUERY;
2338	memset(&query, 0, sizeof(query));
2339	query.sysctl_flags = SYSCTL_VERSION;
2340	len = sizeof(histnode);
2341	error = sysctl(mib, 3, &histnode[0], &len, &query, sizeof(query));
2342	if (error != 0) {
2343		err(1, "query failed");
2344		return;
2345	}
2346	if (len == 0) {
2347 		warnx("No active kernel history logs.");
2348 		return;
2349 	}
2350
2351	len = len / sizeof(histnode[0]);	/* get # of entries returned */
2352
2353 	if (todo & HISTLIST)
2354 		(void)printf("Active kernel histories:");
2355
2356	for (i = 0; i < len; i++) {
2357 		if (todo & HISTLIST)
2358			(void)printf(" %s", histnode[i].sysctl_name);
2359 		else {
2360 			/*
2361 			 * If we're dumping all histories, do it, else
2362 			 * check to see if this is the one we want.
2363 			 */
2364			if (histname == NULL ||
2365			    strcmp(histname, histnode[i].sysctl_name) == 0) {
2366 				if (histname == NULL)
2367 					(void)printf(
2368					    "\nkernel history `%s':\n",
2369					    histnode[i].sysctl_name);
2370				mib[2] = histnode[i].sysctl_num;
2371				mib[3] = CTL_EOL;
2372				hist_dodump_sysctl(mib, 4);
2373 			}
2374 		}
2375 	}
2376
2377 	if (todo & HISTLIST)
2378 		(void)putchar('\n');
2379	else if (mib[2] == CTL_QUERY)
2380		warnx("history %s not found", histname);
2381 }
2382
2383 /*
2384  * Actually dump the history buffer at the specified KVA.
2385  */
2386void
2387hist_dodump_sysctl(int mib[], unsigned int miblen)
2388{
2389	struct sysctl_history *hist;
2390	struct timeval tv;
2391	struct sysctl_history_event *e;
2392 	size_t histsize;
2393	char *strp;
2394 	unsigned i;
2395	char *fmt = NULL, *fn = NULL;
2396
2397	hist = NULL;
2398	histsize = 0;
2399 	do {
2400		errno = 0;
2401		if (sysctl(mib, miblen, hist, &histsize, NULL, 0) == 0)
2402			break;
2403		if (errno != ENOMEM)
2404			break;
2405		if ((hist = realloc(hist, histsize)) == NULL)
2406			errx(1, "realloc history buffer");
2407	} while (errno == ENOMEM);
2408	if (errno != 0)
2409		err(1, "sysctl failed");
2410
2411	strp = (char *)(&hist->sh_events[hist->sh_numentries]);
2412
2413	(void)printf("%"PRIu32" entries, next is %"PRIu32"\n",
2414	    hist->sh_numentries,
2415	    hist->sh_nextfree);
2416
2417	i = hist->sh_nextfree;
2418
2419	do {
2420		e = &hist->sh_events[i];
2421		if (e->she_fmtoffset != 0) {
2422			fmt = &strp[e->she_fmtoffset];
2423			size_t fmtlen = strlen(fmt);
2424			for (unsigned z = 0; z < fmtlen - 1; z++) {
2425				if (fmt[z] == '%' && fmt[z+1] == 's')
2426					fmt[z+1] = 'p';
2427			}
2428			fn = &strp[e->she_funcoffset];
2429			bintime2timeval(&e->she_bintime, &tv);
2430			(void)printf("%06ld.%06ld %s#%"PRIu32"@%"PRIu32": ",
2431			    (long int)tv.tv_sec, (long int)tv.tv_usec,
2432			    fn, e->she_callnumber, e->she_cpunum);
2433			(void)printf(fmt, e->she_values[0], e->she_values[1],
2434			     e->she_values[2], e->she_values[3]);
2435 			(void)putchar('\n');
2436 		}
2437		i = (i + 1) % hist->sh_numentries;
2438	} while (i != hist->sh_nextfree);
2439
2440	free(hist);
2441 }
2442
2443static void
2444usage(void)
2445{
2446
2447	(void)fprintf(stderr,
2448	    "usage: %s [-CefHiLlmstUvW] [-c count] [-h hashname]\n"
2449	    "\t\t[-M core] [-N system] [-n diskcount] [-u histname]\n"
2450	    "[-w wait] [disks]\n",
2451	    getprogname());
2452	exit(1);
2453}
2454