1 /* $NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $ */ 2 /* $OpenBSD: vmstat.c,v 1.155 2022/12/04 23:50:50 cheloha 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 62 struct 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 */ 83 extern struct _disk cur, last; 84 extern char **dr_name; 85 extern int *dk_select, dk_ndrive; 86 87 struct uvmexp uvmexp, ouvmexp; 88 int ndrives; 89 90 int winlines = 20; 91 92 kvm_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 101 void cpustats(void); 102 time_t getuptime(void); 103 void dkstats(void); 104 void dointr(void); 105 void domem(void); 106 void dopool(void); 107 void dosum(void); 108 void dovmstat(u_int, int); 109 void kread(int, void *, size_t); 110 void usage(void); 111 void dotimes(void); 112 void doforkst(void); 113 void needhdr(int); 114 int pct(int64_t, int64_t); 115 void printhdr(void); 116 117 char **choosedrives(char **); 118 119 /* Namelist and memory file names. */ 120 char *nlistf, *memf; 121 122 extern char *__progname; 123 124 int verbose = 0; 125 int zflag = 0; 126 127 int 128 main(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 261 char ** 262 choosedrives(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 297 time_t 298 getuptime(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 314 int hz; 315 volatile sig_atomic_t hdrcnt; 316 317 void 318 dovmstat(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 395 void 396 printhdr(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 */ 427 void 428 needhdr(__unused int signo) 429 { 430 431 hdrcnt = 1; 432 } 433 434 void 435 dotimes(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 470 int 471 pct(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 481 void 482 dosum(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 517 /* swap */ 518 (void)printf("%11u swap pages\n", uvmexp.swpages); 519 (void)printf("%11u swap pages in use\n", uvmexp.swpginuse); 520 521 /* stat counters */ 522 (void)printf("%11u page faults\n", uvmexp.faults); 523 (void)printf("%11u traps\n", uvmexp.traps); 524 (void)printf("%11u interrupts\n", uvmexp.intrs); 525 (void)printf("%11u cpu context switches\n", uvmexp.swtch); 526 (void)printf("%11u fpu context switches\n", uvmexp.fpswtch); 527 (void)printf("%11u software interrupts\n", uvmexp.softs); 528 (void)printf("%11u syscalls\n", uvmexp.syscalls); 529 (void)printf("%11u pagein operations\n", uvmexp.pageins); 530 (void)printf("%11u forks\n", uvmexp.forks); 531 (void)printf("%11u forks where vmspace is shared\n", 532 uvmexp.forks_sharevm); 533 (void)printf("%11u kernel map entries\n", uvmexp.kmapent); 534 (void)printf("%11u zeroed page hits\n", uvmexp.pga_zerohit); 535 (void)printf("%11u zeroed page misses\n", uvmexp.pga_zeromiss); 536 537 /* daemon counters */ 538 (void)printf("%11u number of times the pagedaemon woke up\n", 539 uvmexp.pdwoke); 540 (void)printf("%11u revolutions of the clock hand\n", uvmexp.pdrevs); 541 (void)printf("%11u pages freed by pagedaemon\n", uvmexp.pdfreed); 542 (void)printf("%11u pages scanned by pagedaemon\n", uvmexp.pdscans); 543 (void)printf("%11u pages reactivated by pagedaemon\n", uvmexp.pdreact); 544 (void)printf("%11u busy pages found by pagedaemon\n", uvmexp.pdbusy); 545 546 if (nlistf == NULL && memf == NULL) { 547 size = sizeof(nchstats); 548 mib[0] = CTL_KERN; 549 mib[1] = KERN_NCHSTATS; 550 if (sysctl(mib, 2, &nchstats, &size, NULL, 0) == -1) { 551 warn("could not read kern.nchstats"); 552 memset(&nchstats, 0, sizeof(nchstats)); 553 } 554 } else { 555 kread(X_NCHSTATS, &nchstats, sizeof(nchstats)); 556 } 557 558 nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits + 559 nchstats.ncs_badhits + nchstats.ncs_falsehits + 560 nchstats.ncs_miss + nchstats.ncs_long; 561 (void)printf("%11lld total name lookups\n", nchtotal); 562 (void)printf("%11s cache hits (%d%% pos + %d%% neg) system %d%% " 563 "per-directory\n", 564 "", pct(nchstats.ncs_goodhits, nchtotal), 565 pct(nchstats.ncs_neghits, nchtotal), 566 pct(nchstats.ncs_pass2, nchtotal)); 567 (void)printf("%11s deletions %d%%, falsehits %d%%, toolong %d%%\n", "", 568 pct(nchstats.ncs_badhits, nchtotal), 569 pct(nchstats.ncs_falsehits, nchtotal), 570 pct(nchstats.ncs_long, nchtotal)); 571 } 572 573 void 574 doforkst(void) 575 { 576 struct forkstat fks; 577 size_t size; 578 int mib[2]; 579 580 if (nlistf == NULL && memf == NULL) { 581 size = sizeof(struct forkstat); 582 mib[0] = CTL_KERN; 583 mib[1] = KERN_FORKSTAT; 584 if (sysctl(mib, 2, &fks, &size, NULL, 0) == -1) { 585 warn("could not read kern.forkstat"); 586 memset(&fks, 0, sizeof(struct forkstat)); 587 } 588 } else { 589 kread(X_FORKSTAT, &fks, sizeof(struct forkstat)); 590 } 591 592 (void)printf("%u forks, %llu pages, average %.2f\n", 593 fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork); 594 (void)printf("%u vforks, %llu pages, average %.2f\n", 595 fks.cntvfork, fks.sizvfork, 596 (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1)); 597 (void)printf("%u __tforks, %llu pages, average %.2f\n", 598 fks.cnttfork, fks.siztfork, 599 (double)fks.siztfork / (fks.cnttfork ? fks.cnttfork : 1)); 600 (void)printf("%u kthread creations, %llu pages, average %.2f\n", 601 fks.cntkthread, fks.sizkthread, 602 (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1)); 603 } 604 605 void 606 dkstats(void) 607 { 608 int dn, state; 609 double etime; 610 611 /* Calculate disk stat deltas. */ 612 dkswap(); 613 etime = 0; 614 for (state = 0; state < CPUSTATES; ++state) { 615 etime += cur.cp_time[state]; 616 } 617 if (etime == 0) 618 etime = 1; 619 etime /= hz; 620 for (dn = 0; dn < dk_ndrive; ++dn) { 621 if (!dk_select[dn]) 622 continue; 623 (void)printf("%3.0f ", 624 (cur.dk_rxfer[dn] + cur.dk_rxfer[dn]) / etime); 625 } 626 } 627 628 void 629 cpustats(void) 630 { 631 double percent, total; 632 int state; 633 634 total = 0; 635 for (state = 0; state < CPUSTATES; ++state) 636 total += cur.cp_time[state]; 637 if (total) 638 percent = 100 / total; 639 else 640 percent = 0; 641 (void)printf("%2.0f ", (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * percent); 642 (void)printf("%2.0f ", (cur.cp_time[CP_SYS] + cur.cp_time[CP_SPIN] + cur.cp_time[CP_INTR]) * percent); 643 (void)printf("%2.0f", cur.cp_time[CP_IDLE] * percent); 644 } 645 646 void 647 dointr(void) 648 { 649 int nintr, mib[4], i; 650 char intrname[128]; 651 u_int64_t inttotal; 652 time_t uptime; 653 size_t siz; 654 655 if (nlistf != NULL || memf != NULL) { 656 errx(1, 657 "interrupt statistics are only available on live kernels"); 658 } 659 660 uptime = getuptime(); 661 662 mib[0] = CTL_KERN; 663 mib[1] = KERN_INTRCNT; 664 mib[2] = KERN_INTRCNT_NUM; 665 siz = sizeof(nintr); 666 if (sysctl(mib, 3, &nintr, &siz, NULL, 0) == -1) { 667 warnx("could not read kern.intrcnt.nintrcnt"); 668 return; 669 } 670 671 (void)printf("%-16s %20s %8s\n", "interrupt", "total", "rate"); 672 673 inttotal = 0; 674 for (i = 0; i < nintr; i++) { 675 char name[128]; 676 uint64_t cnt; 677 int vector; 678 679 mib[0] = CTL_KERN; 680 mib[1] = KERN_INTRCNT; 681 mib[2] = KERN_INTRCNT_NAME; 682 mib[3] = i; 683 siz = sizeof(name); 684 if (sysctl(mib, 4, name, &siz, NULL, 0) == -1) { 685 warnx("could not read kern.intrcnt.name.%d", i); 686 return; 687 } 688 689 mib[0] = CTL_KERN; 690 mib[1] = KERN_INTRCNT; 691 mib[2] = KERN_INTRCNT_VECTOR; 692 mib[3] = i; 693 siz = sizeof(vector); 694 if (sysctl(mib, 4, &vector, &siz, NULL, 0) == -1) { 695 strlcpy(intrname, name, sizeof(intrname)); 696 } else { 697 snprintf(intrname, sizeof(intrname), "irq%d/%s", 698 vector, name); 699 } 700 701 mib[0] = CTL_KERN; 702 mib[1] = KERN_INTRCNT; 703 mib[2] = KERN_INTRCNT_CNT; 704 mib[3] = i; 705 siz = sizeof(cnt); 706 if (sysctl(mib, 4, &cnt, &siz, NULL, 0) == -1) { 707 warnx("could not read kern.intrcnt.cnt.%d", i); 708 return; 709 } 710 711 if (cnt || zflag) 712 (void)printf("%-16.16s %20llu %8llu\n", intrname, 713 cnt, cnt / uptime); 714 inttotal += cnt; 715 } 716 717 (void)printf("%-16s %20llu %8llu\n", "Total", inttotal, 718 inttotal / uptime); 719 } 720 721 /* 722 * These names are defined in <sys/malloc.h>. 723 */ 724 const char *kmemnames[] = INITKMEMNAMES; 725 726 void 727 domem(void) 728 { 729 struct kmembuckets buckets[MINBUCKET + 16], *kp; 730 struct kmemstats kmemstats[M_LAST], *ks; 731 int i, j, len, size, first, mib[4]; 732 u_long totuse = 0, totfree = 0; 733 char buf[BUFSIZ], *bufp, *ap; 734 unsigned long long totreq = 0; 735 const char *name; 736 size_t siz; 737 738 if (memf == NULL && nlistf == NULL) { 739 mib[0] = CTL_KERN; 740 mib[1] = KERN_MALLOCSTATS; 741 mib[2] = KERN_MALLOC_BUCKETS; 742 siz = sizeof(buf); 743 if (sysctl(mib, 3, buf, &siz, NULL, 0) == -1) { 744 warnx("could not read kern.malloc.buckets"); 745 return; 746 } 747 748 bufp = buf; 749 mib[2] = KERN_MALLOC_BUCKET; 750 siz = sizeof(struct kmembuckets); 751 i = 0; 752 while ((ap = strsep(&bufp, ",")) != NULL) { 753 const char *errstr; 754 755 mib[3] = strtonum(ap, 0, INT_MAX, &errstr); 756 if (errstr) { 757 warnx("kernel lied about %d being a number", mib[3]); 758 return; 759 } 760 761 if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz, 762 NULL, 0) == -1) { 763 warn("could not read kern.malloc.bucket.%d", mib[3]); 764 return; 765 } 766 i++; 767 } 768 } else { 769 kread(X_KMEMBUCKETS, buckets, sizeof(buckets)); 770 } 771 772 for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; 773 i++, kp++) { 774 if (kp->kb_calls == 0 && !verbose) 775 continue; 776 if (first) { 777 (void)printf("Memory statistics by bucket size\n"); 778 (void)printf( 779 " Size In Use Free Requests HighWater Couldfree\n"); 780 first = 0; 781 } 782 size = 1 << i; 783 (void)printf("%8d %8llu %6llu %18llu %7llu %10llu\n", size, 784 (unsigned long long)(kp->kb_total - kp->kb_totalfree), 785 (unsigned long long)kp->kb_totalfree, 786 (unsigned long long)kp->kb_calls, 787 (unsigned long long)kp->kb_highwat, 788 (unsigned long long)kp->kb_couldfree); 789 totfree += size * kp->kb_totalfree; 790 } 791 792 /* 793 * If kmem statistics are not being gathered by the kernel, 794 * first will still be 1. 795 */ 796 if (first) { 797 printf( 798 "Kmem statistics are not being gathered by the kernel.\n"); 799 return; 800 } 801 802 if (memf == NULL && nlistf == NULL) { 803 memset(kmemstats, 0, sizeof(kmemstats)); 804 for (i = 0; i < M_LAST; i++) { 805 mib[0] = CTL_KERN; 806 mib[1] = KERN_MALLOCSTATS; 807 mib[2] = KERN_MALLOC_KMEMSTATS; 808 mib[3] = i; 809 siz = sizeof(struct kmemstats); 810 811 /* 812 * Skip errors -- these are presumed to be unallocated 813 * entries. 814 */ 815 if (sysctl(mib, 4, &kmemstats[i], &siz, NULL, 0) == -1) 816 continue; 817 } 818 } else { 819 kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); 820 } 821 822 (void)printf("\nMemory usage type by bucket size\n"); 823 (void)printf(" Size Type(s)\n"); 824 kp = &buckets[MINBUCKET]; 825 for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) { 826 if (kp->kb_calls == 0) 827 continue; 828 first = 1; 829 len = 8; 830 for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { 831 if (ks->ks_calls == 0) 832 continue; 833 if ((ks->ks_size & j) == 0) 834 continue; 835 name = kmemnames[i] ? kmemnames[i] : "undefined"; 836 len += 2 + strlen(name); 837 if (first) 838 printf("%8d %s", j, name); 839 else 840 printf(","); 841 if (len >= 80) { 842 printf("\n\t "); 843 len = 10 + strlen(name); 844 } 845 if (!first) 846 printf(" %s", name); 847 first = 0; 848 } 849 printf("\n"); 850 } 851 852 (void)printf( 853 "\nMemory statistics by type Type Kern\n"); 854 (void)printf( 855 " Type InUse MemUse HighUse Limit Requests Limit Size(s)\n"); 856 for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { 857 if (ks->ks_calls == 0) 858 continue; 859 (void)printf("%14s%6ld%6ldK%7ldK%6ldK%9ld%5u", 860 kmemnames[i] ? kmemnames[i] : "undefined", 861 ks->ks_inuse, (ks->ks_memuse + 1023) / 1024, 862 (ks->ks_maxused + 1023) / 1024, 863 (ks->ks_limit + 1023) / 1024, ks->ks_calls, 864 ks->ks_limblocks); 865 first = 1; 866 for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) { 867 if ((ks->ks_size & j) == 0) 868 continue; 869 if (first) 870 printf(" %d", j); 871 else 872 printf(",%d", j); 873 first = 0; 874 } 875 printf("\n"); 876 totuse += ks->ks_memuse; 877 totreq += ks->ks_calls; 878 } 879 (void)printf("\nMemory Totals: In Use Free Requests\n"); 880 (void)printf(" %7luK %6luK %8llu\n", 881 (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq); 882 } 883 884 static void 885 print_pool(struct kinfo_pool *pp, char *name) 886 { 887 static int first = 1; 888 char maxp[32]; 889 int ovflw; 890 891 if (first) { 892 (void)printf("Memory resource pool statistics\n"); 893 (void)printf( 894 "%-11s%5s%9s%5s%9s%6s%6s%6s%6s%6s%6s%5s\n", 895 "Name", 896 "Size", 897 "Requests", 898 "Fail", 899 "InUse", 900 "Pgreq", 901 "Pgrel", 902 "Npage", 903 "Hiwat", 904 "Minpg", 905 "Maxpg", 906 "Idle"); 907 first = 0; 908 } 909 910 /* Skip unused pools unless verbose output. */ 911 if (pp->pr_nget == 0 && !verbose) 912 return; 913 914 if (pp->pr_maxpages == UINT_MAX) 915 snprintf(maxp, sizeof maxp, "inf"); 916 else 917 snprintf(maxp, sizeof maxp, "%u", pp->pr_maxpages); 918 /* 919 * Print single word. `ovflow' is number of characters didn't fit 920 * on the last word. `fmt' is a format string to print this word. 921 * It must contain asterisk for field width. `width' is a width 922 * occupied by this word. `fixed' is a number of constant chars in 923 * `fmt'. `val' is a value to be printed using format string `fmt'. 924 */ 925 #define PRWORD(ovflw, fmt, width, fixed, val) do { \ 926 (ovflw) += printf((fmt), \ 927 (width) - (fixed) - (ovflw) > 0 ? \ 928 (width) - (fixed) - (ovflw) : 0, \ 929 (val)) - (width); \ 930 if ((ovflw) < 0) \ 931 (ovflw) = 0; \ 932 } while (/* CONSTCOND */0) 933 934 ovflw = 0; 935 PRWORD(ovflw, "%-*s", 11, 0, name); 936 PRWORD(ovflw, " %*u", 5, 1, pp->pr_size); 937 PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget); 938 PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nfail); 939 PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget - pp->pr_nput); 940 PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagealloc); 941 PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagefree); 942 PRWORD(ovflw, " %*d", 6, 1, pp->pr_npages); 943 PRWORD(ovflw, " %*d", 6, 1, pp->pr_hiwat); 944 PRWORD(ovflw, " %*d", 6, 1, pp->pr_minpages); 945 PRWORD(ovflw, " %*s", 6, 1, maxp); 946 PRWORD(ovflw, " %*lu\n", 5, 1, pp->pr_nidle); 947 } 948 949 static void dopool_kvm(void); 950 static void dopool_sysctl(void); 951 952 void 953 dopool(void) 954 { 955 if (nlistf == NULL && memf == NULL) 956 dopool_sysctl(); 957 else 958 dopool_kvm(); 959 } 960 961 void 962 dopool_sysctl(void) 963 { 964 int mib[4], npools, i; 965 long total = 0, inuse = 0; 966 struct kinfo_pool pool; 967 size_t size; 968 969 mib[0] = CTL_KERN; 970 mib[1] = KERN_POOL; 971 mib[2] = KERN_POOL_NPOOLS; 972 size = sizeof(npools); 973 if (sysctl(mib, 3, &npools, &size, NULL, 0) == -1) { 974 warn("can't figure out number of pools in kernel"); 975 return; 976 } 977 978 for (i = 1; npools; i++) { 979 char name[32]; 980 981 mib[0] = CTL_KERN; 982 mib[1] = KERN_POOL; 983 mib[2] = KERN_POOL_POOL; 984 mib[3] = i; 985 size = sizeof(pool); 986 if (sysctl(mib, 4, &pool, &size, NULL, 0) == -1) { 987 if (errno == ENOENT) 988 continue; 989 warn("error getting pool"); 990 return; 991 } 992 npools--; 993 mib[2] = KERN_POOL_NAME; 994 size = sizeof(name); 995 if (sysctl(mib, 4, &name, &size, NULL, 0) == -1) { 996 warn("error getting pool name"); 997 return; 998 } 999 print_pool(&pool, name); 1000 1001 inuse += (pool.pr_nget - pool.pr_nput) * pool.pr_size; 1002 total += pool.pr_npages * pool.pr_pgsize; 1003 } 1004 1005 inuse /= 1024; 1006 total /= 1024; 1007 printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n", 1008 inuse, total, (double)(100 * inuse) / total); 1009 } 1010 1011 void 1012 dopool_kvm(void) 1013 { 1014 SIMPLEQ_HEAD(,pool) pool_head; 1015 struct pool pool, *pp = &pool; 1016 struct kinfo_pool pi; 1017 long total = 0, inuse = 0; 1018 u_long addr; 1019 1020 kread(X_POOLHEAD, &pool_head, sizeof(pool_head)); 1021 addr = (u_long)SIMPLEQ_FIRST(&pool_head); 1022 1023 while (addr != 0) { 1024 char name[32]; 1025 1026 if (kvm_read(kd, addr, (void *)pp, sizeof *pp) != sizeof *pp) { 1027 (void)fprintf(stderr, 1028 "vmstat: pool chain trashed: %s\n", 1029 kvm_geterr(kd)); 1030 exit(1); 1031 } 1032 if (kvm_read(kd, (u_long)pp->pr_wchan, name, sizeof name) < 0) { 1033 (void)fprintf(stderr, 1034 "vmstat: pool name trashed: %s\n", 1035 kvm_geterr(kd)); 1036 exit(1); 1037 } 1038 name[31] = '\0'; 1039 1040 memset(&pi, 0, sizeof(pi)); 1041 pi.pr_size = pp->pr_size; 1042 pi.pr_pgsize = pp->pr_pgsize; 1043 pi.pr_itemsperpage = pp->pr_itemsperpage; 1044 pi.pr_npages = pp->pr_npages; 1045 pi.pr_minpages = pp->pr_minpages; 1046 pi.pr_maxpages = pp->pr_maxpages; 1047 pi.pr_hardlimit = pp->pr_hardlimit; 1048 pi.pr_nout = pp->pr_nout; 1049 pi.pr_nitems = pp->pr_nitems; 1050 pi.pr_nget = pp->pr_nget; 1051 pi.pr_nput = pp->pr_nput; 1052 pi.pr_nfail = pp->pr_nfail; 1053 pi.pr_npagealloc = pp->pr_npagealloc; 1054 pi.pr_npagefree = pp->pr_npagefree; 1055 pi.pr_hiwat = pp->pr_hiwat; 1056 pi.pr_nidle = pp->pr_nidle; 1057 1058 print_pool(&pi, name); 1059 1060 inuse += (pi.pr_nget - pi.pr_nput) * pi.pr_size; 1061 total += pi.pr_npages * pi.pr_pgsize; 1062 1063 addr = (u_long)SIMPLEQ_NEXT(pp, pr_poollist); 1064 } 1065 1066 inuse /= 1024; 1067 total /= 1024; 1068 printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n", 1069 inuse, total, (double)(100 * inuse) / total); 1070 } 1071 1072 /* 1073 * kread reads something from the kernel, given its nlist index. 1074 */ 1075 void 1076 kread(int nlx, void *addr, size_t size) 1077 { 1078 char *sym; 1079 1080 if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) { 1081 sym = namelist[nlx].n_name; 1082 if (*sym == '_') 1083 ++sym; 1084 errx(1, "symbol %s not defined", sym); 1085 } 1086 if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) { 1087 sym = namelist[nlx].n_name; 1088 if (*sym == '_') 1089 ++sym; 1090 errx(1, "%s: %s", sym, kvm_geterr(kd)); 1091 } 1092 } 1093 1094 void 1095 usage(void) 1096 { 1097 (void)fprintf(stderr, "usage: %s [-fimstvz] [-c count] [-M core] " 1098 "[-N system] [-w wait] [disk ...]\n", __progname); 1099 exit(1); 1100 } 1101