1 /* $NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $ */ 2 /* $OpenBSD: vmstat.c,v 1.132 2014/07/13 21:13:51 kettenis 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> 34 #include <sys/time.h> 35 #include <sys/proc.h> 36 #include <sys/dkstat.h> 37 #include <sys/buf.h> 38 #include <sys/namei.h> 39 #include <sys/malloc.h> 40 #include <sys/fcntl.h> 41 #include <sys/ioctl.h> 42 #include <sys/sysctl.h> 43 #include <sys/device.h> 44 #include <sys/pool.h> 45 #include <sys/vmmeter.h> 46 47 #include <time.h> 48 #include <nlist.h> 49 #include <kvm.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <unistd.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <ctype.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <paths.h> 59 #include <limits.h> 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 const char *errstr; 135 u_int interval = 0; 136 137 while ((c = getopt(argc, argv, "c:fiM:mN:stw:vz")) != -1) { 138 switch (c) { 139 case 'c': 140 reps = atoi(optarg); 141 break; 142 case 'f': 143 todo |= FORKSTAT; 144 break; 145 case 'i': 146 todo |= INTRSTAT; 147 break; 148 case 'M': 149 memf = optarg; 150 break; 151 case 'm': 152 todo |= MEMSTAT; 153 break; 154 case 'N': 155 nlistf = optarg; 156 break; 157 case 's': 158 todo |= SUMSTAT; 159 break; 160 case 't': 161 todo |= TIMESTAT; 162 break; 163 case 'w': 164 interval = (u_int)strtonum(optarg, 0, 1000, &errstr); 165 if (errstr) 166 errx(1, "-w %s: %s", optarg, errstr); 167 break; 168 case 'v': 169 verbose = 1; 170 break; 171 case 'z': 172 zflag = 1; 173 break; 174 case '?': 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 187 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 188 if (kd == 0) 189 errx(1, "kvm_openfiles: %s", errbuf); 190 191 if ((c = kvm_nlist(kd, namelist)) != 0) { 192 193 if (c > 0) { 194 (void)fprintf(stderr, 195 "%s: undefined symbols:", __progname); 196 for (c = 0; 197 c < sizeof(namelist)/sizeof(namelist[0]); 198 c++) 199 if (namelist[c].n_type == 0) 200 fprintf(stderr, " %s", 201 namelist[c].n_name); 202 (void)fputc('\n', stderr); 203 exit(1); 204 } else 205 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 206 } 207 } 208 209 if (todo & VMSTAT) { 210 struct winsize winsize; 211 212 dkinit(0); /* Initialize disk stats, no disks selected. */ 213 argv = choosedrives(argv); /* Select disks. */ 214 winsize.ws_row = 0; 215 (void) ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 216 if (winsize.ws_row > 0) 217 winlines = winsize.ws_row; 218 219 } 220 221 #define BACKWARD_COMPATIBILITY 222 #ifdef BACKWARD_COMPATIBILITY 223 if (*argv) { 224 interval = (u_int)strtonum(*argv, 0, 1000, &errstr); 225 if (errstr) 226 errx(1, "%s: %s", *argv, errstr); 227 228 if (*++argv) 229 reps = atoi(*argv); 230 } 231 #endif 232 233 if (interval) { 234 if (!reps) 235 reps = -1; 236 } else if (reps) 237 interval = 1; 238 239 if (todo & FORKSTAT) 240 doforkst(); 241 if (todo & MEMSTAT) { 242 domem(); 243 dopool(); 244 } 245 if (todo & SUMSTAT) 246 dosum(); 247 if (todo & TIMESTAT) 248 dotimes(); 249 if (todo & INTRSTAT) 250 dointr(); 251 if (todo & VMSTAT) 252 dovmstat(interval, reps); 253 exit(0); 254 } 255 256 char ** 257 choosedrives(char **argv) 258 { 259 int i; 260 261 /* 262 * Choose drives to be displayed. Priority goes to (in order) drives 263 * supplied as arguments, default drives. If everything isn't filled 264 * in and there are drives not taken care of, display the first few 265 * that fit. 266 */ 267 #define BACKWARD_COMPATIBILITY 268 for (ndrives = 0; *argv; ++argv) { 269 #ifdef BACKWARD_COMPATIBILITY 270 if (isdigit((unsigned char)**argv)) 271 break; 272 #endif 273 for (i = 0; i < dk_ndrive; i++) { 274 if (strcmp(dr_name[i], *argv)) 275 continue; 276 dk_select[i] = 1; 277 ++ndrives; 278 break; 279 } 280 } 281 for (i = 0; i < dk_ndrive && ndrives < 2; i++) { 282 if (dk_select[i]) 283 continue; 284 dk_select[i] = 1; 285 ++ndrives; 286 } 287 return(argv); 288 } 289 290 time_t 291 getuptime(void) 292 { 293 struct timespec uptime; 294 time_t time_uptime, naptime; 295 296 if (nlistf == NULL && memf == NULL) { 297 if (clock_gettime(CLOCK_UPTIME, &uptime) == -1) 298 err(1, "clock_gettime"); 299 return (uptime.tv_sec); 300 } 301 302 kread(X_NAPTIME, &naptime, sizeof(naptime)); 303 kread(X_TIME_UPTIME, &time_uptime, sizeof(time_uptime)); 304 return (time_uptime - naptime); 305 } 306 307 int hz; 308 volatile sig_atomic_t hdrcnt; 309 310 void 311 dovmstat(u_int interval, int reps) 312 { 313 time_t uptime, halfuptime; 314 struct clockinfo clkinfo; 315 struct vmtotal total; 316 size_t size; 317 int mib[2]; 318 319 uptime = getuptime(); 320 halfuptime = uptime / 2; 321 (void)signal(SIGCONT, needhdr); 322 323 mib[0] = CTL_KERN; 324 mib[1] = KERN_CLOCKRATE; 325 size = sizeof(clkinfo); 326 if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) { 327 warn("could not read kern.clockrate"); 328 return; 329 } 330 hz = clkinfo.stathz; 331 332 for (hdrcnt = 1;;) { 333 /* Read new disk statistics */ 334 dkreadstats(); 335 if (!--hdrcnt || last.dk_ndrive != cur.dk_ndrive) 336 printhdr(); 337 if (nlistf == NULL && memf == NULL) { 338 size = sizeof(struct uvmexp); 339 mib[0] = CTL_VM; 340 mib[1] = VM_UVMEXP; 341 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) { 342 warn("could not get vm.uvmexp"); 343 bzero(&uvmexp, sizeof(struct uvmexp)); 344 } 345 } else { 346 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp)); 347 } 348 size = sizeof(total); 349 mib[0] = CTL_VM; 350 mib[1] = VM_METER; 351 if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) { 352 warn("could not read vm.vmmeter"); 353 bzero(&total, sizeof(total)); 354 } 355 (void)printf(" %u %u %u ", 356 total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw); 357 #define rate(x) ((unsigned)((((unsigned)x) + halfuptime) / uptime)) /* round */ 358 #define pgtok(a) ((a) * ((unsigned int)uvmexp.pagesize >> 10)) 359 (void)printf("%6u %7u ", 360 pgtok(uvmexp.active + uvmexp.swpginuse), 361 pgtok(uvmexp.free)); 362 (void)printf("%4u ", rate(uvmexp.faults - ouvmexp.faults)); 363 (void)printf("%3u ", rate(uvmexp.pdreact - ouvmexp.pdreact)); 364 (void)printf("%3u ", rate(uvmexp.pageins - ouvmexp.pageins)); 365 (void)printf("%3u %3u ", 366 rate(uvmexp.pdpageouts - ouvmexp.pdpageouts), 0); 367 (void)printf("%3u ", rate(uvmexp.pdscans - ouvmexp.pdscans)); 368 dkstats(); 369 (void)printf("%4u %5u %4u ", 370 rate(uvmexp.intrs - ouvmexp.intrs), 371 rate(uvmexp.syscalls - ouvmexp.syscalls), 372 rate(uvmexp.swtch - ouvmexp.swtch)); 373 cpustats(); 374 (void)printf("\n"); 375 (void)fflush(stdout); 376 if (reps >= 0 && --reps <= 0) 377 break; 378 ouvmexp = uvmexp; 379 uptime = interval; 380 /* 381 * We round upward to avoid losing low-frequency events 382 * (i.e., >= 1 per interval but < 1 per second). 383 */ 384 halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2; 385 (void)sleep(interval); 386 } 387 } 388 389 void 390 printhdr(void) 391 { 392 int i; 393 static int printedhdr; 394 395 if (printedhdr && !isatty(STDOUT_FILENO)) 396 return; 397 398 (void)printf(" procs memory page%*s", 20, ""); 399 if (ndrives > 0) 400 (void)printf("%s %*straps cpu\n", 401 ((ndrives > 1) ? "disks" : "disk"), 402 ((ndrives > 1) ? ndrives * 4 - 5 : 0), ""); 403 else 404 (void)printf("%*s traps cpu\n", 405 ndrives * 3, ""); 406 407 (void)printf(" r b w avm fre flt re pi po fr sr "); 408 for (i = 0; i < dk_ndrive; i++) 409 if (dk_select[i]) 410 (void)printf("%c%c%c ", dr_name[i][0], 411 dr_name[i][1], 412 dr_name[i][strlen(dr_name[i]) - 1]); 413 (void)printf(" int sys cs us sy id\n"); 414 hdrcnt = winlines - 2; 415 printedhdr = 1; 416 } 417 418 /* 419 * Force a header to be prepended to the next output. 420 */ 421 /* ARGSUSED */ 422 void 423 needhdr(int signo) 424 { 425 426 hdrcnt = 1; 427 } 428 429 void 430 dotimes(void) 431 { 432 u_int pgintime, rectime; 433 size_t size; 434 int mib[2]; 435 436 /* XXX Why are these set to 0 ? This doesn't look right. */ 437 pgintime = 0; 438 rectime = 0; 439 440 if (nlistf == NULL && memf == NULL) { 441 size = sizeof(struct uvmexp); 442 mib[0] = CTL_VM; 443 mib[1] = VM_UVMEXP; 444 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) { 445 warn("could not read vm.uvmexp"); 446 bzero(&uvmexp, sizeof(struct uvmexp)); 447 } 448 } else { 449 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp)); 450 } 451 452 (void)printf("%u reactivates, %u total time (usec)\n", 453 uvmexp.pdreact, rectime); 454 if (uvmexp.pdreact != 0) 455 (void)printf("average: %u usec / reclaim\n", 456 rectime / uvmexp.pdreact); 457 (void)printf("\n"); 458 (void)printf("%u page ins, %u total time (msec)\n", 459 uvmexp.pageins, pgintime / 10); 460 if (uvmexp.pageins != 0) 461 (void)printf("average: %8.1f msec / page in\n", 462 pgintime / (uvmexp.pageins * 10.0)); 463 } 464 465 int 466 pct(int64_t top, int64_t bot) 467 { 468 int ans; 469 470 if (bot == 0) 471 return(0); 472 ans = top * 100 / bot; 473 return (ans); 474 } 475 476 void 477 dosum(void) 478 { 479 struct nchstats nchstats; 480 int mib[2], nselcoll; 481 long long nchtotal; 482 size_t size; 483 484 if (nlistf == NULL && memf == NULL) { 485 size = sizeof(struct uvmexp); 486 mib[0] = CTL_VM; 487 mib[1] = VM_UVMEXP; 488 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) { 489 warn("could not read vm.uvmexp"); 490 bzero(&uvmexp, sizeof(struct uvmexp)); 491 } 492 } else { 493 kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp)); 494 } 495 496 /* vm_page constants */ 497 (void)printf("%11u bytes per page\n", uvmexp.pagesize); 498 499 /* vm_page counters */ 500 (void)printf("%11u pages managed\n", uvmexp.npages); 501 (void)printf("%11u pages free\n", uvmexp.free); 502 (void)printf("%11u pages active\n", uvmexp.active); 503 (void)printf("%11u pages inactive\n", uvmexp.inactive); 504 (void)printf("%11u pages being paged out\n", uvmexp.paging); 505 (void)printf("%11u pages wired\n", uvmexp.wired); 506 (void)printf("%11u pages zeroed\n", uvmexp.zeropages); 507 (void)printf("%11u pages reserved for pagedaemon\n", 508 uvmexp.reserve_pagedaemon); 509 (void)printf("%11u pages reserved for kernel\n", 510 uvmexp.reserve_kernel); 511 512 /* swap */ 513 (void)printf("%11u swap pages\n", uvmexp.swpages); 514 (void)printf("%11u swap pages in use\n", uvmexp.swpginuse); 515 (void)printf("%11u total anon's in system\n", uvmexp.nanon); 516 (void)printf("%11u free anon's\n", uvmexp.nfreeanon); 517 518 /* stat counters */ 519 (void)printf("%11u page faults\n", uvmexp.faults); 520 (void)printf("%11u traps\n", uvmexp.traps); 521 (void)printf("%11u interrupts\n", uvmexp.intrs); 522 (void)printf("%11u cpu context switches\n", uvmexp.swtch); 523 (void)printf("%11u fpu context switches\n", uvmexp.fpswtch); 524 (void)printf("%11u software interrupts\n", uvmexp.softs); 525 (void)printf("%11u syscalls\n", uvmexp.syscalls); 526 (void)printf("%11u pagein operations\n", uvmexp.pageins); 527 (void)printf("%11u forks\n", uvmexp.forks); 528 (void)printf("%11u forks where vmspace is shared\n", 529 uvmexp.forks_sharevm); 530 (void)printf("%11u kernel map entries\n", uvmexp.kmapent); 531 (void)printf("%11u zeroed page hits\n", uvmexp.pga_zerohit); 532 (void)printf("%11u zeroed page misses\n", uvmexp.pga_zeromiss); 533 534 /* daemon counters */ 535 (void)printf("%11u number of times the pagedaemon woke up\n", 536 uvmexp.pdwoke); 537 (void)printf("%11u revolutions of the clock hand\n", uvmexp.pdrevs); 538 (void)printf("%11u pages freed by pagedaemon\n", uvmexp.pdfreed); 539 (void)printf("%11u pages scanned by pagedaemon\n", uvmexp.pdscans); 540 (void)printf("%11u pages reactivated by pagedaemon\n", uvmexp.pdreact); 541 (void)printf("%11u busy pages found by pagedaemon\n", uvmexp.pdbusy); 542 543 if (nlistf == NULL && memf == NULL) { 544 size = sizeof(nchstats); 545 mib[0] = CTL_KERN; 546 mib[1] = KERN_NCHSTATS; 547 if (sysctl(mib, 2, &nchstats, &size, NULL, 0) < 0) { 548 warn("could not read kern.nchstats"); 549 bzero(&nchstats, sizeof(nchstats)); 550 } 551 } else { 552 kread(X_NCHSTATS, &nchstats, sizeof(nchstats)); 553 } 554 555 nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits + 556 nchstats.ncs_badhits + nchstats.ncs_falsehits + 557 nchstats.ncs_miss + nchstats.ncs_long; 558 (void)printf("%11lld total name lookups\n", nchtotal); 559 (void)printf("%11s cache hits (%d%% pos + %d%% neg) system %d%% " 560 "per-directory\n", 561 "", pct(nchstats.ncs_goodhits, nchtotal), 562 pct(nchstats.ncs_neghits, nchtotal), 563 pct(nchstats.ncs_pass2, nchtotal)); 564 (void)printf("%11s deletions %d%%, falsehits %d%%, toolong %d%%\n", "", 565 pct(nchstats.ncs_badhits, nchtotal), 566 pct(nchstats.ncs_falsehits, nchtotal), 567 pct(nchstats.ncs_long, nchtotal)); 568 569 if (nlistf == NULL && memf == NULL) { 570 size = sizeof(nselcoll); 571 mib[0] = CTL_KERN; 572 mib[1] = KERN_NSELCOLL; 573 if (sysctl(mib, 2, &nselcoll, &size, NULL, 0) < 0) { 574 warn("could not read kern.nselcoll"); 575 nselcoll = 0; 576 } 577 } else { 578 kread(X_NSELCOLL, &nselcoll, sizeof(nselcoll)); 579 } 580 (void)printf("%11d select collisions\n", nselcoll); 581 } 582 583 void 584 doforkst(void) 585 { 586 struct forkstat fks; 587 size_t size; 588 int mib[2]; 589 590 if (nlistf == NULL && memf == NULL) { 591 size = sizeof(struct forkstat); 592 mib[0] = CTL_KERN; 593 mib[1] = KERN_FORKSTAT; 594 if (sysctl(mib, 2, &fks, &size, NULL, 0) < 0) { 595 warn("could not read kern.forkstat"); 596 bzero(&fks, sizeof(struct forkstat)); 597 } 598 } else { 599 kread(X_FORKSTAT, &fks, sizeof(struct forkstat)); 600 } 601 602 (void)printf("%d forks, %d pages, average %.2f\n", 603 fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork); 604 (void)printf("%d vforks, %d pages, average %.2f\n", 605 fks.cntvfork, fks.sizvfork, 606 (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1)); 607 (void)printf("%d __tforks, %d pages, average %.2f\n", 608 fks.cnttfork, fks.siztfork, 609 (double)fks.siztfork / (fks.cnttfork ? fks.cnttfork : 1)); 610 (void)printf("%d kthread creations, %d pages, average %.2f\n", 611 fks.cntkthread, fks.sizkthread, 612 (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1)); 613 } 614 615 void 616 dkstats(void) 617 { 618 int dn, state; 619 double etime; 620 621 /* Calculate disk stat deltas. */ 622 dkswap(); 623 etime = 0; 624 for (state = 0; state < CPUSTATES; ++state) { 625 etime += cur.cp_time[state]; 626 } 627 if (etime == 0) 628 etime = 1; 629 etime /= hz; 630 for (dn = 0; dn < dk_ndrive; ++dn) { 631 if (!dk_select[dn]) 632 continue; 633 (void)printf("%3.0f ", 634 (cur.dk_rxfer[dn] + cur.dk_rxfer[dn]) / etime); 635 } 636 } 637 638 void 639 cpustats(void) 640 { 641 double percent, total; 642 int state; 643 644 total = 0; 645 for (state = 0; state < CPUSTATES; ++state) 646 total += cur.cp_time[state]; 647 if (total) 648 percent = 100 / total; 649 else 650 percent = 0; 651 (void)printf("%2.0f ", (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * percent); 652 (void)printf("%2.0f ", (cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * percent); 653 (void)printf("%2.0f", cur.cp_time[CP_IDLE] * percent); 654 } 655 656 void 657 dointr(void) 658 { 659 int nintr, mib[4], i; 660 char intrname[128]; 661 u_int64_t inttotal; 662 time_t uptime; 663 size_t siz; 664 665 if (nlistf != NULL || memf != NULL) { 666 errx(1, 667 "interrupt statistics are only available on live kernels"); 668 } 669 670 uptime = getuptime(); 671 672 mib[0] = CTL_KERN; 673 mib[1] = KERN_INTRCNT; 674 mib[2] = KERN_INTRCNT_NUM; 675 siz = sizeof(nintr); 676 if (sysctl(mib, 3, &nintr, &siz, NULL, 0) < 0) { 677 warnx("could not read kern.intrcnt.nintrcnt"); 678 return; 679 } 680 681 (void)printf("%-16s %20s %8s\n", "interrupt", "total", "rate"); 682 683 inttotal = 0; 684 for (i = 0; i < nintr; i++) { 685 char name[128]; 686 u_quad_t cnt; 687 int vector; 688 689 mib[0] = CTL_KERN; 690 mib[1] = KERN_INTRCNT; 691 mib[2] = KERN_INTRCNT_NAME; 692 mib[3] = i; 693 siz = sizeof(name); 694 if (sysctl(mib, 4, name, &siz, NULL, 0) < 0) { 695 warnx("could not read kern.intrcnt.name.%d", i); 696 return; 697 } 698 699 mib[0] = CTL_KERN; 700 mib[1] = KERN_INTRCNT; 701 mib[2] = KERN_INTRCNT_VECTOR; 702 mib[3] = i; 703 siz = sizeof(vector); 704 if (sysctl(mib, 4, &vector, &siz, NULL, 0) < 0) { 705 strlcpy(intrname, name, sizeof(intrname)); 706 } else { 707 snprintf(intrname, sizeof(intrname), "irq%d/%s", 708 vector, name); 709 } 710 711 mib[0] = CTL_KERN; 712 mib[1] = KERN_INTRCNT; 713 mib[2] = KERN_INTRCNT_CNT; 714 mib[3] = i; 715 siz = sizeof(cnt); 716 if (sysctl(mib, 4, &cnt, &siz, NULL, 0) < 0) { 717 warnx("could not read kern.intrcnt.cnt.%d", i); 718 return; 719 } 720 721 if (cnt || zflag) 722 (void)printf("%-16.16s %20llu %8llu\n", intrname, 723 cnt, cnt / uptime); 724 inttotal += cnt; 725 } 726 727 (void)printf("%-16s %20llu %8llu\n", "Total", inttotal, 728 inttotal / uptime); 729 } 730 731 /* 732 * These names are defined in <sys/malloc.h>. 733 */ 734 const char *kmemnames[] = INITKMEMNAMES; 735 736 void 737 domem(void) 738 { 739 struct kmembuckets buckets[MINBUCKET + 16], *kp; 740 struct kmemstats kmemstats[M_LAST], *ks; 741 int i, j, len, size, first, mib[4]; 742 u_long totuse = 0, totfree = 0; 743 char buf[BUFSIZ], *bufp, *ap; 744 quad_t totreq = 0; 745 const char *name; 746 size_t siz; 747 748 if (memf == NULL && nlistf == NULL) { 749 mib[0] = CTL_KERN; 750 mib[1] = KERN_MALLOCSTATS; 751 mib[2] = KERN_MALLOC_BUCKETS; 752 siz = sizeof(buf); 753 if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) { 754 warnx("could not read kern.malloc.buckets"); 755 return; 756 } 757 758 bufp = buf; 759 mib[2] = KERN_MALLOC_BUCKET; 760 siz = sizeof(struct kmembuckets); 761 i = 0; 762 while ((ap = strsep(&bufp, ",")) != NULL) { 763 mib[3] = atoi(ap); 764 765 if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz, 766 NULL, 0) < 0) { 767 warn("could not read kern.malloc.bucket.%d", mib[3]); 768 return; 769 } 770 i++; 771 } 772 } else { 773 kread(X_KMEMBUCKETS, buckets, sizeof(buckets)); 774 } 775 776 for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; 777 i++, kp++) { 778 if (kp->kb_calls == 0 && !verbose) 779 continue; 780 if (first) { 781 (void)printf("Memory statistics by bucket size\n"); 782 (void)printf( 783 " Size In Use Free Requests HighWater Couldfree\n"); 784 first = 0; 785 } 786 size = 1 << i; 787 (void)printf("%8d %8llu %6llu %18llu %7llu %10llu\n", size, 788 (unsigned long long)(kp->kb_total - kp->kb_totalfree), 789 (unsigned long long)kp->kb_totalfree, 790 (unsigned long long)kp->kb_calls, 791 (unsigned long long)kp->kb_highwat, 792 (unsigned long long)kp->kb_couldfree); 793 totfree += size * kp->kb_totalfree; 794 } 795 796 /* 797 * If kmem statistics are not being gathered by the kernel, 798 * first will still be 1. 799 */ 800 if (first) { 801 printf( 802 "Kmem statistics are not being gathered by the kernel.\n"); 803 return; 804 } 805 806 if (memf == NULL && nlistf == NULL) { 807 bzero(kmemstats, sizeof(kmemstats)); 808 for (i = 0; i < M_LAST; i++) { 809 mib[0] = CTL_KERN; 810 mib[1] = KERN_MALLOCSTATS; 811 mib[2] = KERN_MALLOC_KMEMSTATS; 812 mib[3] = i; 813 siz = sizeof(struct kmemstats); 814 815 /* 816 * Skip errors -- these are presumed to be unallocated 817 * entries. 818 */ 819 if (sysctl(mib, 4, &kmemstats[i], &siz, NULL, 0) < 0) 820 continue; 821 } 822 } else { 823 kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); 824 } 825 826 (void)printf("\nMemory usage type by bucket size\n"); 827 (void)printf(" Size Type(s)\n"); 828 kp = &buckets[MINBUCKET]; 829 for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) { 830 if (kp->kb_calls == 0) 831 continue; 832 first = 1; 833 len = 8; 834 for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { 835 if (ks->ks_calls == 0) 836 continue; 837 if ((ks->ks_size & j) == 0) 838 continue; 839 name = kmemnames[i] ? kmemnames[i] : "undefined"; 840 len += 2 + strlen(name); 841 if (first) 842 printf("%8d %s", j, name); 843 else 844 printf(","); 845 if (len >= 80) { 846 printf("\n\t "); 847 len = 10 + strlen(name); 848 } 849 if (!first) 850 printf(" %s", name); 851 first = 0; 852 } 853 printf("\n"); 854 } 855 856 (void)printf( 857 "\nMemory statistics by type Type Kern\n"); 858 (void)printf( 859 " Type InUse MemUse HighUse Limit Requests Limit 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("%14s%6ld%6ldK%7ldK%6ldK%9ld%5u%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, ks->ks_calls, 868 ks->ks_limblocks, ks->ks_mapblocks); 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 %8qu\n", 885 (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq); 886 } 887 888 static void 889 print_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 953 static void dopool_kvm(void); 954 static void dopool_sysctl(void); 955 956 void 957 dopool(void) 958 { 959 if (nlistf == NULL && memf == NULL) 960 dopool_sysctl(); 961 else 962 dopool_kvm(); 963 } 964 965 void 966 dopool_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) < 0) { 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) < 0) { 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) < 0) { 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 1015 void 1016 dopool_kvm(void) 1017 { 1018 SIMPLEQ_HEAD(,pool) pool_head; 1019 struct pool pool, *pp = &pool; 1020 struct pool_allocator palloc; 1021 struct kinfo_pool pi; 1022 long total = 0, inuse = 0; 1023 u_long addr; 1024 1025 kread(X_POOLHEAD, &pool_head, sizeof(pool_head)); 1026 addr = (u_long)SIMPLEQ_FIRST(&pool_head); 1027 1028 while (addr != 0) { 1029 char name[32]; 1030 1031 if (kvm_read(kd, addr, (void *)pp, sizeof *pp) != sizeof *pp) { 1032 (void)fprintf(stderr, 1033 "vmstat: pool chain trashed: %s\n", 1034 kvm_geterr(kd)); 1035 exit(1); 1036 } 1037 if (kvm_read(kd, (u_long)pp->pr_wchan, name, sizeof name) < 0) { 1038 (void)fprintf(stderr, 1039 "vmstat: pool name trashed: %s\n", 1040 kvm_geterr(kd)); 1041 exit(1); 1042 } 1043 if (kvm_read(kd, (u_long)pp->pr_alloc, 1044 &palloc, sizeof(palloc)) < 0) { 1045 (void)fprintf(stderr, 1046 "vmstat: pool allocator trashed: %s\n", 1047 kvm_geterr(kd)); 1048 exit(1); 1049 } 1050 1051 name[31] = '\0'; 1052 1053 memset(&pi, 0, sizeof(pi)); 1054 pi.pr_size = pp->pr_size; 1055 pi.pr_pgsize = palloc.pa_pagesz; 1056 pi.pr_itemsperpage = pp->pr_itemsperpage; 1057 pi.pr_npages = pp->pr_npages; 1058 pi.pr_minpages = pp->pr_minpages; 1059 pi.pr_maxpages = pp->pr_maxpages; 1060 pi.pr_hardlimit = pp->pr_hardlimit; 1061 pi.pr_nout = pp->pr_nout; 1062 pi.pr_nitems = pp->pr_nitems; 1063 pi.pr_nget = pp->pr_nget; 1064 pi.pr_nput = pp->pr_nput; 1065 pi.pr_nfail = pp->pr_nfail; 1066 pi.pr_npagealloc = pp->pr_npagealloc; 1067 pi.pr_npagefree = pp->pr_npagefree; 1068 pi.pr_hiwat = pp->pr_hiwat; 1069 pi.pr_nidle = pp->pr_nidle; 1070 1071 print_pool(&pi, name); 1072 1073 inuse += (pi.pr_nget - pi.pr_nput) * pi.pr_size; 1074 total += pi.pr_npages * pi.pr_pgsize; 1075 1076 addr = (u_long)SIMPLEQ_NEXT(pp, pr_poollist); 1077 } 1078 1079 inuse /= 1024; 1080 total /= 1024; 1081 printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n", 1082 inuse, total, (double)(100 * inuse) / total); 1083 } 1084 1085 /* 1086 * kread reads something from the kernel, given its nlist index. 1087 */ 1088 void 1089 kread(int nlx, void *addr, size_t size) 1090 { 1091 char *sym; 1092 1093 if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) { 1094 sym = namelist[nlx].n_name; 1095 if (*sym == '_') 1096 ++sym; 1097 errx(1, "symbol %s not defined", sym); 1098 } 1099 if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) { 1100 sym = namelist[nlx].n_name; 1101 if (*sym == '_') 1102 ++sym; 1103 errx(1, "%s: %s", sym, kvm_geterr(kd)); 1104 } 1105 } 1106 1107 void 1108 usage(void) 1109 { 1110 (void)fprintf(stderr, "usage: %s [-fimstvz] [-c count] [-M core] " 1111 "[-N system] [-w wait] [disk ...]\n", __progname); 1112 exit(1); 1113 } 1114