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