jcs's openbsd hax
openbsd
1/* $NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $ */
2/* $OpenBSD: vmstat.c,v 1.160 2025/05/21 03:04:14 tedu Exp $ */
3
4/*
5 * Copyright (c) 1980, 1986, 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/time.h>
34#include <sys/signal.h>
35#include <sys/proc.h>
36#include <sys/namei.h>
37#include <sys/malloc.h>
38#include <sys/ioctl.h>
39#include <sys/sysctl.h>
40#include <sys/device.h>
41#include <sys/pool.h>
42#include <sys/sched.h>
43#include <sys/vmmeter.h>
44
45#include <ctype.h>
46#include <err.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <kvm.h>
50#include <limits.h>
51#include <nlist.h>
52#include <paths.h>
53#include <signal.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <time.h>
58#include <unistd.h>
59
60#include "dkstats.h"
61
62struct nlist namelist[] = {
63#define X_UVMEXP 0 /* sysctl */
64 { "_uvmexp" },
65#define X_TIME_UPTIME 1
66 { "_time_uptime" },
67#define X_NCHSTATS 2 /* sysctl */
68 { "_nchstats" },
69#define X_KMEMSTAT 3 /* sysctl */
70 { "_kmemstats" },
71#define X_KMEMBUCKETS 4 /* sysctl */
72 { "_bucket" },
73#define X_FORKSTAT 5 /* sysctl */
74 { "_forkstat" },
75#define X_POOLHEAD 6 /* sysctl */
76 { "_pool_head" },
77#define X_NAPTIME 7
78 { "_naptime" },
79 { NULL },
80};
81
82/* Objects defined in dkstats.c */
83extern struct _disk cur, last;
84extern char **dr_name;
85extern int *dk_select, dk_ndrive;
86
87struct uvmexp uvmexp, ouvmexp;
88int ndrives;
89
90int winlines = 20;
91
92kvm_t *kd;
93
94#define FORKSTAT 0x01
95#define INTRSTAT 0x02
96#define MEMSTAT 0x04
97#define SUMSTAT 0x08
98#define TIMESTAT 0x10
99#define VMSTAT 0x20
100
101void cpustats(void);
102time_t getuptime(void);
103void dkstats(void);
104void dointr(void);
105void domem(void);
106void dopool(void);
107void dosum(void);
108void dovmstat(u_int, int);
109void kread(int, void *, size_t);
110void usage(void);
111void dotimes(void);
112void doforkst(void);
113void needhdr(int);
114int pct(int64_t, int64_t);
115void printhdr(void);
116
117char **choosedrives(char **);
118
119/* Namelist and memory file names. */
120char *nlistf, *memf;
121
122extern char *__progname;
123
124int verbose = 0;
125int zflag = 0;
126
127int
128main(int argc, char *argv[])
129{
130 char errbuf[_POSIX2_LINE_MAX];
131 int c, todo = 0, reps = 0;
132 struct winsize winsize;
133 const char *errstr;
134 u_int interval = 0;
135
136 while ((c = getopt(argc, argv, "c:fiM:mN:stw:vz")) != -1) {
137 switch (c) {
138 case 'c':
139 reps = strtonum(optarg, 0, INT_MAX, &errstr);
140 if (errstr)
141 errx(1, "-c %s: %s", optarg, errstr);
142 break;
143 case 'f':
144 todo |= FORKSTAT;
145 break;
146 case 'i':
147 todo |= INTRSTAT;
148 break;
149 case 'M':
150 memf = optarg;
151 break;
152 case 'm':
153 todo |= MEMSTAT;
154 break;
155 case 'N':
156 nlistf = optarg;
157 break;
158 case 's':
159 todo |= SUMSTAT;
160 break;
161 case 't':
162 todo |= TIMESTAT;
163 break;
164 case 'w':
165 interval = (u_int)strtonum(optarg, 0, 1000, &errstr);
166 if (errstr)
167 errx(1, "-w %s: %s", optarg, errstr);
168 break;
169 case 'v':
170 verbose = 1;
171 break;
172 case 'z':
173 zflag = 1;
174 break;
175 default:
176 usage();
177 }
178 }
179 argc -= optind;
180 argv += optind;
181
182 if (todo == 0)
183 todo = VMSTAT;
184
185 if (nlistf != NULL || memf != NULL) {
186 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
187 if (kd == 0)
188 errx(1, "kvm_openfiles: %s", errbuf);
189
190 if ((c = kvm_nlist(kd, namelist)) != 0) {
191 if (c > 0) {
192 (void)fprintf(stderr,
193 "%s: undefined symbols:", __progname);
194 for (c = 0;
195 c < sizeof(namelist)/sizeof(namelist[0]);
196 c++)
197 if (namelist[c].n_type == 0)
198 fprintf(stderr, " %s",
199 namelist[c].n_name);
200 (void)fputc('\n', stderr);
201 exit(1);
202 } else
203 errx(1, "kvm_nlist: %s", kvm_geterr(kd));
204 }
205 }
206
207 if (todo & VMSTAT) {
208 dkinit(0); /* Initialize disk stats, no disks selected. */
209 argv = choosedrives(argv); /* Select disks. */
210 }
211
212 if (unveil("/", "") == -1)
213 err(1, "unveil /");
214 if (unveil(NULL, NULL) == -1)
215 err(1, "unveil");
216
217 winsize.ws_row = 0;
218 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) == 0) {
219 if (winsize.ws_row > 0)
220 winlines = winsize.ws_row;
221 }
222
223#define BACKWARD_COMPATIBILITY
224#ifdef BACKWARD_COMPATIBILITY
225 if (*argv) {
226 interval = (u_int)strtonum(*argv, 0, 1000, &errstr);
227 if (errstr)
228 errx(1, "interval %s: %s", *argv, errstr);
229
230 if (*++argv) {
231 reps = strtonum(*argv, 0, INT_MAX, &errstr);
232 if (errstr)
233 errx(1, "reps %s: %s", *argv, errstr);
234 }
235 }
236#endif
237
238 if (interval) {
239 if (!reps)
240 reps = -1;
241 } else if (reps)
242 interval = 1;
243
244 if (todo & FORKSTAT)
245 doforkst();
246 if (todo & MEMSTAT) {
247 domem();
248 dopool();
249 }
250 if (todo & SUMSTAT)
251 dosum();
252 if (todo & TIMESTAT)
253 dotimes();
254 if (todo & INTRSTAT)
255 dointr();
256 if (todo & VMSTAT)
257 dovmstat(interval, reps);
258 exit(0);
259}
260
261char **
262choosedrives(char **argv)
263{
264 int i;
265
266 /*
267 * Choose drives to be displayed. Priority goes to (in order) drives
268 * supplied as arguments, default drives. If everything isn't filled
269 * in and there are drives not taken care of, display the first few
270 * that fit.
271 */
272#define BACKWARD_COMPATIBILITY
273 for (ndrives = 0; *argv; ++argv) {
274#ifdef BACKWARD_COMPATIBILITY
275 if (isdigit((unsigned char)**argv))
276 break;
277#endif
278 for (i = 0; i < dk_ndrive; i++) {
279 if (strcmp(dr_name[i], *argv))
280 continue;
281 dk_select[i] = 1;
282 ++ndrives;
283 break;
284 }
285 if (i == dk_ndrive)
286 errx(1, "invalid interval or drive name: %s", *argv);
287 }
288 for (i = 0; i < dk_ndrive && ndrives < 2; i++) {
289 if (dk_select[i])
290 continue;
291 dk_select[i] = 1;
292 ++ndrives;
293 }
294 return(argv);
295}
296
297time_t
298getuptime(void)
299{
300 struct timespec uptime;
301 time_t time_uptime, naptime;
302
303 if (nlistf == NULL && memf == NULL) {
304 if (clock_gettime(CLOCK_UPTIME, &uptime) == -1)
305 err(1, "clock_gettime");
306 return (uptime.tv_sec);
307 }
308
309 kread(X_NAPTIME, &naptime, sizeof(naptime));
310 kread(X_TIME_UPTIME, &time_uptime, sizeof(time_uptime));
311 return (time_uptime - naptime);
312}
313
314int hz;
315volatile sig_atomic_t hdrcnt;
316
317void
318dovmstat(u_int interval, int reps)
319{
320 time_t uptime, halfuptime;
321 struct clockinfo clkinfo;
322 struct vmtotal total;
323 size_t size;
324 int mib[2];
325
326 uptime = getuptime();
327 halfuptime = uptime / 2;
328 (void)signal(SIGCONT, needhdr);
329
330 mib[0] = CTL_KERN;
331 mib[1] = KERN_CLOCKRATE;
332 size = sizeof(clkinfo);
333 if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) == -1) {
334 warn("could not read kern.clockrate");
335 return;
336 }
337 hz = clkinfo.stathz;
338
339 for (hdrcnt = 1;;) {
340 /* Read new disk statistics */
341 dkreadstats();
342 if (!--hdrcnt || last.dk_ndrive != cur.dk_ndrive)
343 printhdr();
344 if (nlistf == NULL && memf == NULL) {
345 size = sizeof(struct uvmexp);
346 mib[0] = CTL_VM;
347 mib[1] = VM_UVMEXP;
348 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
349 warn("could not get vm.uvmexp");
350 memset(&uvmexp, 0, sizeof(struct uvmexp));
351 }
352 } else {
353 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
354 }
355 size = sizeof(total);
356 mib[0] = CTL_VM;
357 mib[1] = VM_METER;
358 if (sysctl(mib, 2, &total, &size, NULL, 0) == -1) {
359 warn("could not read vm.vmmeter");
360 memset(&total, 0, sizeof(total));
361 }
362 (void)printf("%2u %3u", total.t_rq - 1, total.t_sl);
363#define rate(x) ((unsigned)((((unsigned)x) + halfuptime) / uptime)) /* round */
364#define pgtok(a) ((a) * ((unsigned int)uvmexp.pagesize >> 10))
365 (void)printf("%5uM %6uM ",
366 pgtok(uvmexp.active + uvmexp.swpginuse) / 1024,
367 pgtok(uvmexp.free) / 1024);
368 (void)printf("%4u ", rate(uvmexp.faults - ouvmexp.faults));
369 (void)printf("%3u ", rate(uvmexp.pdreact - ouvmexp.pdreact));
370 (void)printf("%3u ", rate(uvmexp.pageins - ouvmexp.pageins));
371 (void)printf("%3u %3u ",
372 rate(uvmexp.pdpageouts - ouvmexp.pdpageouts), 0);
373 (void)printf("%3u ", rate(uvmexp.pdscans - ouvmexp.pdscans));
374 dkstats();
375 (void)printf("%4u %5u %4u ",
376 rate(uvmexp.intrs - ouvmexp.intrs),
377 rate(uvmexp.syscalls - ouvmexp.syscalls),
378 rate(uvmexp.swtch - ouvmexp.swtch));
379 cpustats();
380 (void)printf("\n");
381 (void)fflush(stdout);
382 if (reps >= 0 && --reps <= 0)
383 break;
384 ouvmexp = uvmexp;
385 uptime = interval;
386 /*
387 * We round upward to avoid losing low-frequency events
388 * (i.e., >= 1 per interval but < 1 per second).
389 */
390 halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2;
391 (void)sleep(interval);
392 }
393}
394
395void
396printhdr(void)
397{
398 int i;
399 static int printedhdr;
400
401 if (printedhdr && !isatty(STDOUT_FILENO))
402 return;
403
404 (void)printf(" procs memory page%*s", 20, "");
405 if (ndrives > 0)
406 (void)printf("%s %*straps cpu\n",
407 ((ndrives > 1) ? "disks" : "disk"),
408 ((ndrives > 1) ? ndrives * 4 - 5 : 0), "");
409 else
410 (void)printf("%*s traps cpu\n",
411 ndrives * 3, "");
412
413 (void)printf(" r s avm fre flt re pi po fr sr ");
414 for (i = 0; i < dk_ndrive; i++)
415 if (dk_select[i])
416 (void)printf("%c%c%c ", dr_name[i][0],
417 dr_name[i][1],
418 dr_name[i][strlen(dr_name[i]) - 1]);
419 (void)printf(" int sys cs us sy id\n");
420 hdrcnt = winlines - 2;
421 printedhdr = 1;
422}
423
424/*
425 * Force a header to be prepended to the next output.
426 */
427void
428needhdr(__unused int signo)
429{
430
431 hdrcnt = 1;
432}
433
434void
435dotimes(void)
436{
437 u_int pgintime, rectime;
438 size_t size;
439 int mib[2];
440
441 /* XXX Why are these set to 0 ? This doesn't look right. */
442 pgintime = 0;
443 rectime = 0;
444
445 if (nlistf == NULL && memf == NULL) {
446 size = sizeof(struct uvmexp);
447 mib[0] = CTL_VM;
448 mib[1] = VM_UVMEXP;
449 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
450 warn("could not read vm.uvmexp");
451 memset(&uvmexp, 0, sizeof(struct uvmexp));
452 }
453 } else {
454 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
455 }
456
457 (void)printf("%u reactivates, %u total time (usec)\n",
458 uvmexp.pdreact, rectime);
459 if (uvmexp.pdreact != 0)
460 (void)printf("average: %u usec / reclaim\n",
461 rectime / uvmexp.pdreact);
462 (void)printf("\n");
463 (void)printf("%u page ins, %u total time (msec)\n",
464 uvmexp.pageins, pgintime / 10);
465 if (uvmexp.pageins != 0)
466 (void)printf("average: %8.1f msec / page in\n",
467 pgintime / (uvmexp.pageins * 10.0));
468}
469
470int
471pct(int64_t top, int64_t bot)
472{
473 int ans;
474
475 if (bot == 0)
476 return(0);
477 ans = top * 100 / bot;
478 return (ans);
479}
480
481void
482dosum(void)
483{
484 struct nchstats nchstats;
485 int mib[2];
486 long long nchtotal;
487 size_t size;
488
489 if (nlistf == NULL && memf == NULL) {
490 size = sizeof(struct uvmexp);
491 mib[0] = CTL_VM;
492 mib[1] = VM_UVMEXP;
493 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
494 warn("could not read vm.uvmexp");
495 memset(&uvmexp, 0, sizeof(struct uvmexp));
496 }
497 } else {
498 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
499 }
500
501 /* vm_page constants */
502 (void)printf("%11u bytes per page\n", uvmexp.pagesize);
503
504 /* vm_page counters */
505 (void)printf("%11u pages managed\n", uvmexp.npages);
506 (void)printf("%11u pages free\n", uvmexp.free);
507 (void)printf("%11u pages active\n", uvmexp.active);
508 (void)printf("%11u pages inactive\n", uvmexp.inactive);
509 (void)printf("%11u pages being paged out\n", uvmexp.paging);
510 (void)printf("%11u pages wired\n", uvmexp.wired);
511 (void)printf("%11u pages zeroed\n", uvmexp.zeropages);
512 (void)printf("%11u pages reserved for pagedaemon\n",
513 uvmexp.reserve_pagedaemon);
514 (void)printf("%11u pages reserved for kernel\n",
515 uvmexp.reserve_kernel);
516 (void)printf("%11u pages in per-cpu caches\n",
517 uvmexp.percpucaches);
518
519 /* per-cpu cache */
520 (void)printf("%11u per-cpu cache hits\n", uvmexp.pcphit);
521 (void)printf("%11u per-cpu cache misses\n", uvmexp.pcpmiss);
522 /* swap */
523 (void)printf("%11u swap pages\n", uvmexp.swpages);
524 (void)printf("%11u swap pages in use\n", uvmexp.swpginuse);
525
526 /* stat counters */
527 (void)printf("%11u page faults\n", uvmexp.faults);
528 (void)printf("%11u traps\n", uvmexp.traps);
529 (void)printf("%11u interrupts\n", uvmexp.intrs);
530 (void)printf("%11u cpu context switches\n", uvmexp.swtch);
531 (void)printf("%11u fpu context switches\n", uvmexp.fpswtch);
532 (void)printf("%11u software interrupts\n", uvmexp.softs);
533 (void)printf("%11u syscalls\n", uvmexp.syscalls);
534 (void)printf("%11u pagein operations\n", uvmexp.pageins);
535 (void)printf("%11u forks\n", uvmexp.forks);
536 (void)printf("%11u forks where vmspace is shared\n",
537 uvmexp.forks_sharevm);
538 (void)printf("%11u kernel map entries\n", uvmexp.kmapent);
539 (void)printf("%11u zeroed page hits\n", uvmexp.pga_zerohit);
540 (void)printf("%11u zeroed page misses\n", uvmexp.pga_zeromiss);
541
542 /* daemon counters */
543 (void)printf("%11u number of times the pagedaemon woke up\n",
544 uvmexp.pdwoke);
545 (void)printf("%11u number of times the pagedaemon scanned for free pages\n", uvmexp.pdrevs);
546 (void)printf("%11u pages freed by pagedaemon\n", uvmexp.pdfreed);
547 (void)printf("%11u pages scanned by pagedaemon\n", uvmexp.pdscans);
548 (void)printf("%11u pages reactivated by pagedaemon\n", uvmexp.pdreact);
549 (void)printf("%11u busy pages found by pagedaemon\n", uvmexp.pdbusy);
550
551 if (nlistf == NULL && memf == NULL) {
552 size = sizeof(nchstats);
553 mib[0] = CTL_KERN;
554 mib[1] = KERN_NCHSTATS;
555 if (sysctl(mib, 2, &nchstats, &size, NULL, 0) == -1) {
556 warn("could not read kern.nchstats");
557 memset(&nchstats, 0, sizeof(nchstats));
558 }
559 } else {
560 kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
561 }
562
563 nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
564 nchstats.ncs_badhits + nchstats.ncs_falsehits +
565 nchstats.ncs_miss + nchstats.ncs_long;
566 (void)printf("%11lld total name lookups\n", nchtotal);
567 (void)printf("%11s cache hits (%d%% pos + %d%% neg) system %d%% "
568 "per-directory\n",
569 "", pct(nchstats.ncs_goodhits, nchtotal),
570 pct(nchstats.ncs_neghits, nchtotal),
571 pct(nchstats.ncs_pass2, nchtotal));
572 (void)printf("%11s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
573 pct(nchstats.ncs_badhits, nchtotal),
574 pct(nchstats.ncs_falsehits, nchtotal),
575 pct(nchstats.ncs_long, nchtotal));
576}
577
578void
579doforkst(void)
580{
581 struct forkstat fks;
582 size_t size;
583 int mib[2];
584
585 if (nlistf == NULL && memf == NULL) {
586 size = sizeof(struct forkstat);
587 mib[0] = CTL_KERN;
588 mib[1] = KERN_FORKSTAT;
589 if (sysctl(mib, 2, &fks, &size, NULL, 0) == -1) {
590 warn("could not read kern.forkstat");
591 memset(&fks, 0, sizeof(struct forkstat));
592 }
593 } else {
594 kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
595 }
596
597 (void)printf("%u forks, %llu pages, average %.2f\n",
598 fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
599 (void)printf("%u vforks, %llu pages, average %.2f\n",
600 fks.cntvfork, fks.sizvfork,
601 (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1));
602 (void)printf("%u __tforks, %llu pages, average %.2f\n",
603 fks.cnttfork, fks.siztfork,
604 (double)fks.siztfork / (fks.cnttfork ? fks.cnttfork : 1));
605 (void)printf("%u kthread creations, %llu pages, average %.2f\n",
606 fks.cntkthread, fks.sizkthread,
607 (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1));
608}
609
610void
611dkstats(void)
612{
613 int dn, state;
614 double etime;
615
616 /* Calculate disk stat deltas. */
617 dkswap();
618 etime = 0;
619 for (state = 0; state < CPUSTATES; ++state) {
620 etime += cur.cp_time[state];
621 }
622 if (etime == 0)
623 etime = 1;
624 etime /= hz;
625 for (dn = 0; dn < dk_ndrive; ++dn) {
626 if (!dk_select[dn])
627 continue;
628 (void)printf("%3.0f ",
629 (cur.dk_rxfer[dn] + cur.dk_rxfer[dn]) / etime);
630 }
631}
632
633void
634cpustats(void)
635{
636 double percent, total;
637 int state;
638
639 total = 0;
640 for (state = 0; state < CPUSTATES; ++state)
641 total += cur.cp_time[state];
642 if (total)
643 percent = 100 / total;
644 else
645 percent = 0;
646 (void)printf("%2.0f ", (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * percent);
647 (void)printf("%2.0f ", (cur.cp_time[CP_SYS] + cur.cp_time[CP_SPIN] + cur.cp_time[CP_INTR]) * percent);
648 (void)printf("%2.0f", cur.cp_time[CP_IDLE] * percent);
649}
650
651void
652dointr(void)
653{
654 int nintr, mib[4], i;
655 char intrname[128];
656 u_int64_t inttotal;
657 time_t uptime;
658 size_t siz;
659
660 if (nlistf != NULL || memf != NULL) {
661 errx(1,
662 "interrupt statistics are only available on live kernels");
663 }
664
665 uptime = getuptime();
666
667 mib[0] = CTL_KERN;
668 mib[1] = KERN_INTRCNT;
669 mib[2] = KERN_INTRCNT_NUM;
670 siz = sizeof(nintr);
671 if (sysctl(mib, 3, &nintr, &siz, NULL, 0) == -1) {
672 warnx("could not read kern.intrcnt.nintrcnt");
673 return;
674 }
675
676 (void)printf("%-16s %20s %8s\n", "interrupt", "total", "rate");
677
678 inttotal = 0;
679 for (i = 0; i < nintr; i++) {
680 char name[128];
681 uint64_t cnt;
682 int vector;
683
684 mib[0] = CTL_KERN;
685 mib[1] = KERN_INTRCNT;
686 mib[2] = KERN_INTRCNT_NAME;
687 mib[3] = i;
688 siz = sizeof(name);
689 if (sysctl(mib, 4, name, &siz, NULL, 0) == -1) {
690 warnx("could not read kern.intrcnt.name.%d", i);
691 return;
692 }
693
694 mib[0] = CTL_KERN;
695 mib[1] = KERN_INTRCNT;
696 mib[2] = KERN_INTRCNT_VECTOR;
697 mib[3] = i;
698 siz = sizeof(vector);
699 if (sysctl(mib, 4, &vector, &siz, NULL, 0) == -1) {
700 strlcpy(intrname, name, sizeof(intrname));
701 } else {
702 snprintf(intrname, sizeof(intrname), "irq%d/%s",
703 vector, name);
704 }
705
706 mib[0] = CTL_KERN;
707 mib[1] = KERN_INTRCNT;
708 mib[2] = KERN_INTRCNT_CNT;
709 mib[3] = i;
710 siz = sizeof(cnt);
711 if (sysctl(mib, 4, &cnt, &siz, NULL, 0) == -1) {
712 warnx("could not read kern.intrcnt.cnt.%d", i);
713 return;
714 }
715
716 if (cnt || zflag)
717 (void)printf("%-16.16s %20llu %8llu\n", intrname,
718 cnt, cnt / uptime);
719 inttotal += cnt;
720 }
721
722 (void)printf("%-16s %20llu %8llu\n", "Total", inttotal,
723 inttotal / uptime);
724}
725
726/*
727 * These names are defined in <sys/malloc.h>.
728 */
729const char *kmemnames[] = INITKMEMNAMES;
730
731void
732domem(void)
733{
734 struct kmembuckets buckets[MINBUCKET + 16], *kp;
735 struct kmemstats kmemstats[M_LAST], *ks;
736 int i, j, len, size, first, mib[4];
737 u_long totuse = 0, totfree = 0;
738 char buf[BUFSIZ], *bufp, *ap;
739 unsigned long long totreq = 0;
740 const char *name;
741 size_t siz;
742
743 if (memf == NULL && nlistf == NULL) {
744 mib[0] = CTL_KERN;
745 mib[1] = KERN_MALLOCSTATS;
746 mib[2] = KERN_MALLOC_BUCKETS;
747 siz = sizeof(buf);
748 if (sysctl(mib, 3, buf, &siz, NULL, 0) == -1) {
749 warnx("could not read kern.malloc.buckets");
750 return;
751 }
752
753 bufp = buf;
754 mib[2] = KERN_MALLOC_BUCKET;
755 siz = sizeof(struct kmembuckets);
756 i = 0;
757 while ((ap = strsep(&bufp, ",")) != NULL) {
758 const char *errstr;
759
760 mib[3] = strtonum(ap, 0, INT_MAX, &errstr);
761 if (errstr) {
762 warnx("kernel lied about %d being a number", mib[3]);
763 return;
764 }
765
766 if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz,
767 NULL, 0) == -1) {
768 warn("could not read kern.malloc.bucket.%d", mib[3]);
769 return;
770 }
771 i++;
772 }
773 } else {
774 kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
775 }
776
777 for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16;
778 i++, kp++) {
779 if (kp->kb_calls == 0 && !verbose)
780 continue;
781 if (first) {
782 (void)printf("Memory statistics by bucket size\n");
783 (void)printf(
784 " Size In Use Free Requests HighWater Couldfree\n");
785 first = 0;
786 }
787 size = 1 << i;
788 (void)printf("%8d %8llu %6llu %18llu %7llu %10llu\n", size,
789 (unsigned long long)(kp->kb_total - kp->kb_totalfree),
790 (unsigned long long)kp->kb_totalfree,
791 (unsigned long long)kp->kb_calls,
792 (unsigned long long)kp->kb_highwat,
793 (unsigned long long)kp->kb_couldfree);
794 totfree += size * kp->kb_totalfree;
795 }
796
797 /*
798 * If kmem statistics are not being gathered by the kernel,
799 * first will still be 1.
800 */
801 if (first) {
802 printf(
803 "Kmem statistics are not being gathered by the kernel.\n");
804 return;
805 }
806
807 if (memf == NULL && nlistf == NULL) {
808 memset(kmemstats, 0, sizeof(kmemstats));
809 for (i = 0; i < M_LAST; i++) {
810 mib[0] = CTL_KERN;
811 mib[1] = KERN_MALLOCSTATS;
812 mib[2] = KERN_MALLOC_KMEMSTATS;
813 mib[3] = i;
814 siz = sizeof(struct kmemstats);
815
816 /*
817 * Skip errors -- these are presumed to be unallocated
818 * entries.
819 */
820 if (sysctl(mib, 4, &kmemstats[i], &siz, NULL, 0) == -1)
821 continue;
822 }
823 } else {
824 kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats));
825 }
826
827 (void)printf("\nMemory usage type by bucket size\n");
828 (void)printf(" Size Type(s)\n");
829 kp = &buckets[MINBUCKET];
830 for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
831 if (kp->kb_calls == 0)
832 continue;
833 first = 1;
834 len = 8;
835 for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
836 if (ks->ks_calls == 0)
837 continue;
838 if ((ks->ks_size & j) == 0)
839 continue;
840 name = kmemnames[i] ? kmemnames[i] : "undefined";
841 len += 2 + strlen(name);
842 if (first)
843 printf("%8d %s", j, name);
844 else
845 printf(",");
846 if (len >= 80) {
847 printf("\n\t ");
848 len = 10 + strlen(name);
849 }
850 if (!first)
851 printf(" %s", name);
852 first = 0;
853 }
854 printf("\n");
855 }
856
857 (void)printf("\n"
858"Memory statistics by type Type Kern\n"
859" Type InUse MemUse HighUse Limit Requests Limit Size(s)\n");
860 for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
861 if (ks->ks_calls == 0)
862 continue;
863 (void)printf(" %-13s%6ld%6ldK%7ldK%5ldM%9ld%6u",
864 kmemnames[i] ? kmemnames[i] : "undefined",
865 ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
866 (ks->ks_maxused + 1023) / 1024,
867 (ks->ks_limit + 1023) / 1024 / 1024, ks->ks_calls,
868 ks->ks_limblocks);
869 first = 1;
870 for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
871 if ((ks->ks_size & j) == 0)
872 continue;
873 if (first)
874 printf(" %d", j);
875 else
876 printf(",%d", j);
877 first = 0;
878 }
879 printf("\n");
880 totuse += ks->ks_memuse;
881 totreq += ks->ks_calls;
882 }
883 (void)printf("\nMemory Totals: In Use Free Requests\n");
884 (void)printf(" %7luK %6luK %8llu\n",
885 (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
886}
887
888static void
889print_pool(struct kinfo_pool *pp, char *name)
890{
891 static int first = 1;
892 char maxp[32];
893 int ovflw;
894
895 if (first) {
896 (void)printf("Memory resource pool statistics\n");
897 (void)printf(
898 "%-11s%5s%9s%5s%9s%6s%6s%6s%6s%6s%6s%5s\n",
899 "Name",
900 "Size",
901 "Requests",
902 "Fail",
903 "InUse",
904 "Pgreq",
905 "Pgrel",
906 "Npage",
907 "Hiwat",
908 "Minpg",
909 "Maxpg",
910 "Idle");
911 first = 0;
912 }
913
914 /* Skip unused pools unless verbose output. */
915 if (pp->pr_nget == 0 && !verbose)
916 return;
917
918 if (pp->pr_maxpages == UINT_MAX)
919 snprintf(maxp, sizeof maxp, "inf");
920 else
921 snprintf(maxp, sizeof maxp, "%u", pp->pr_maxpages);
922/*
923 * Print single word. `ovflow' is number of characters didn't fit
924 * on the last word. `fmt' is a format string to print this word.
925 * It must contain asterisk for field width. `width' is a width
926 * occupied by this word. `fixed' is a number of constant chars in
927 * `fmt'. `val' is a value to be printed using format string `fmt'.
928 */
929#define PRWORD(ovflw, fmt, width, fixed, val) do { \
930 (ovflw) += printf((fmt), \
931 (width) - (fixed) - (ovflw) > 0 ? \
932 (width) - (fixed) - (ovflw) : 0, \
933 (val)) - (width); \
934 if ((ovflw) < 0) \
935 (ovflw) = 0; \
936} while (/* CONSTCOND */0)
937
938 ovflw = 0;
939 PRWORD(ovflw, "%-*s", 11, 0, name);
940 PRWORD(ovflw, " %*u", 5, 1, pp->pr_size);
941 PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget);
942 PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nfail);
943 PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget - pp->pr_nput);
944 PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagealloc);
945 PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagefree);
946 PRWORD(ovflw, " %*d", 6, 1, pp->pr_npages);
947 PRWORD(ovflw, " %*d", 6, 1, pp->pr_hiwat);
948 PRWORD(ovflw, " %*d", 6, 1, pp->pr_minpages);
949 PRWORD(ovflw, " %*s", 6, 1, maxp);
950 PRWORD(ovflw, " %*lu\n", 5, 1, pp->pr_nidle);
951}
952
953static void dopool_kvm(void);
954static void dopool_sysctl(void);
955
956void
957dopool(void)
958{
959 if (nlistf == NULL && memf == NULL)
960 dopool_sysctl();
961 else
962 dopool_kvm();
963}
964
965void
966dopool_sysctl(void)
967{
968 int mib[4], npools, i;
969 long total = 0, inuse = 0;
970 struct kinfo_pool pool;
971 size_t size;
972
973 mib[0] = CTL_KERN;
974 mib[1] = KERN_POOL;
975 mib[2] = KERN_POOL_NPOOLS;
976 size = sizeof(npools);
977 if (sysctl(mib, 3, &npools, &size, NULL, 0) == -1) {
978 warn("can't figure out number of pools in kernel");
979 return;
980 }
981
982 for (i = 1; npools; i++) {
983 char name[32];
984
985 mib[0] = CTL_KERN;
986 mib[1] = KERN_POOL;
987 mib[2] = KERN_POOL_POOL;
988 mib[3] = i;
989 size = sizeof(pool);
990 if (sysctl(mib, 4, &pool, &size, NULL, 0) == -1) {
991 if (errno == ENOENT)
992 continue;
993 warn("error getting pool");
994 return;
995 }
996 npools--;
997 mib[2] = KERN_POOL_NAME;
998 size = sizeof(name);
999 if (sysctl(mib, 4, &name, &size, NULL, 0) == -1) {
1000 warn("error getting pool name");
1001 return;
1002 }
1003 print_pool(&pool, name);
1004
1005 inuse += (pool.pr_nget - pool.pr_nput) * pool.pr_size;
1006 total += pool.pr_npages * pool.pr_pgsize;
1007 }
1008
1009 inuse /= 1024;
1010 total /= 1024;
1011 printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n",
1012 inuse, total, (double)(100 * inuse) / total);
1013}
1014
1015void
1016dopool_kvm(void)
1017{
1018 SIMPLEQ_HEAD(,pool) pool_head;
1019 struct pool pool, *pp = &pool;
1020 struct kinfo_pool pi;
1021 long total = 0, inuse = 0;
1022 u_long addr;
1023
1024 kread(X_POOLHEAD, &pool_head, sizeof(pool_head));
1025 addr = (u_long)SIMPLEQ_FIRST(&pool_head);
1026
1027 while (addr != 0) {
1028 char name[32];
1029
1030 if (kvm_read(kd, addr, (void *)pp, sizeof *pp) != sizeof *pp) {
1031 (void)fprintf(stderr,
1032 "vmstat: pool chain trashed: %s\n",
1033 kvm_geterr(kd));
1034 exit(1);
1035 }
1036 if (kvm_read(kd, (u_long)pp->pr_wchan, name, sizeof name) < 0) {
1037 (void)fprintf(stderr,
1038 "vmstat: pool name trashed: %s\n",
1039 kvm_geterr(kd));
1040 exit(1);
1041 }
1042 name[31] = '\0';
1043
1044 memset(&pi, 0, sizeof(pi));
1045 pi.pr_size = pp->pr_size;
1046 pi.pr_pgsize = pp->pr_pgsize;
1047 pi.pr_itemsperpage = pp->pr_itemsperpage;
1048 pi.pr_npages = pp->pr_npages;
1049 pi.pr_minpages = pp->pr_minpages;
1050 pi.pr_maxpages = pp->pr_maxpages;
1051 pi.pr_hardlimit = pp->pr_hardlimit;
1052 pi.pr_nout = pp->pr_nout;
1053 pi.pr_nitems = pp->pr_nitems;
1054 pi.pr_nget = pp->pr_nget;
1055 pi.pr_nput = pp->pr_nput;
1056 pi.pr_nfail = pp->pr_nfail;
1057 pi.pr_npagealloc = pp->pr_npagealloc;
1058 pi.pr_npagefree = pp->pr_npagefree;
1059 pi.pr_hiwat = pp->pr_hiwat;
1060 pi.pr_nidle = pp->pr_nidle;
1061
1062 print_pool(&pi, name);
1063
1064 inuse += (pi.pr_nget - pi.pr_nput) * pi.pr_size;
1065 total += pi.pr_npages * pi.pr_pgsize;
1066
1067 addr = (u_long)SIMPLEQ_NEXT(pp, pr_poollist);
1068 }
1069
1070 inuse /= 1024;
1071 total /= 1024;
1072 printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n",
1073 inuse, total, (double)(100 * inuse) / total);
1074}
1075
1076/*
1077 * kread reads something from the kernel, given its nlist index.
1078 */
1079void
1080kread(int nlx, void *addr, size_t size)
1081{
1082 char *sym;
1083
1084 if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
1085 sym = namelist[nlx].n_name;
1086 if (*sym == '_')
1087 ++sym;
1088 errx(1, "symbol %s not defined", sym);
1089 }
1090 if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
1091 sym = namelist[nlx].n_name;
1092 if (*sym == '_')
1093 ++sym;
1094 errx(1, "%s: %s", sym, kvm_geterr(kd));
1095 }
1096}
1097
1098void
1099usage(void)
1100{
1101 (void)fprintf(stderr, "usage: %s [-fimstvz] [-c count] [-M core] "
1102 "[-N system] [-w wait] [disk ...]\n", __progname);
1103 exit(1);
1104}