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