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