1 /* 2 * Copyright (c) 1980, 1986, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1980, 1986, 1991, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #if 0 41 #ifndef lint 42 static char sccsid[] = "@(#)vmstat.c 8.1 (Berkeley) 6/6/93"; 43 #endif /* not lint */ 44 #endif 45 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 #include <sys/param.h> 50 #include <sys/time.h> 51 #include <sys/proc.h> 52 #include <sys/uio.h> 53 #include <sys/namei.h> 54 #include <sys/malloc.h> 55 #include <sys/signal.h> 56 #include <sys/fcntl.h> 57 #include <sys/ioctl.h> 58 #include <sys/resource.h> 59 #include <sys/sysctl.h> 60 #include <sys/vmmeter.h> 61 62 #include <vm/vm_param.h> 63 64 #include <ctype.h> 65 #include <devstat.h> 66 #include <err.h> 67 #include <errno.h> 68 #include <kvm.h> 69 #include <limits.h> 70 #include <memstat.h> 71 #include <nlist.h> 72 #include <paths.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #include <sysexits.h> 77 #include <time.h> 78 #include <unistd.h> 79 #include <libutil.h> 80 81 static char da[] = "da"; 82 83 static struct nlist namelist[] = { 84 #define X_SUM 0 85 { "_cnt" }, 86 #define X_BOOTTIME 1 87 { "_boottime" }, 88 #define X_HZ 2 89 { "_hz" }, 90 #define X_STATHZ 3 91 { "_stathz" }, 92 #define X_NCHSTATS 4 93 { "_nchstats" }, 94 #define X_INTRNAMES 5 95 { "_intrnames" }, 96 #define X_EINTRNAMES 6 97 { "_eintrnames" }, 98 #define X_INTRCNT 7 99 { "_intrcnt" }, 100 #define X_EINTRCNT 8 101 { "_eintrcnt" }, 102 #define X_KMEMSTATS 9 103 { "_kmemstatistics" }, 104 #define X_KMEMZONES 10 105 { "_kmemzones" }, 106 #ifdef notyet 107 #define X_DEFICIT XXX 108 { "_deficit" }, 109 #define X_REC XXX 110 { "_rectime" }, 111 #define X_PGIN XXX 112 { "_pgintime" }, 113 #define X_XSTATS XXX 114 { "_xstats" }, 115 #define X_END XXX 116 #else 117 #define X_END 11 118 #endif 119 { "" }, 120 }; 121 122 static struct statinfo cur, last; 123 static int num_devices, maxshowdevs; 124 static long generation; 125 static struct device_selection *dev_select; 126 static int num_selected; 127 static struct devstat_match *matches; 128 static int num_matches = 0; 129 static int num_devices_specified, num_selections; 130 static long select_generation; 131 static char **specified_devices; 132 static devstat_select_mode select_mode; 133 134 static struct vmmeter sum, osum; 135 136 #define VMSTAT_DEFAULT_LINES 20 /* Default number of `winlines'. */ 137 volatile sig_atomic_t wresized; /* Tty resized, when non-zero. */ 138 static int winlines = VMSTAT_DEFAULT_LINES; /* Current number of tty rows. */ 139 140 static int aflag; 141 static int nflag; 142 static int Pflag; 143 static int hflag; 144 145 static kvm_t *kd; 146 147 #define FORKSTAT 0x01 148 #define INTRSTAT 0x02 149 #define MEMSTAT 0x04 150 #define SUMSTAT 0x08 151 #define TIMESTAT 0x10 152 #define VMSTAT 0x20 153 #define ZMEMSTAT 0x40 154 155 static void cpustats(void); 156 static void pcpustats(int, u_long, int); 157 static void devstats(void); 158 static void doforkst(void); 159 static void dointr(void); 160 static void dosum(void); 161 static void dovmstat(unsigned int, int); 162 static void domemstat_malloc(void); 163 static void domemstat_zone(void); 164 static void kread(int, void *, size_t); 165 static void kreado(int, void *, size_t, size_t); 166 static char *kgetstr(const char *); 167 static void needhdr(int); 168 static void needresize(int); 169 static void doresize(void); 170 static void printhdr(int, u_long); 171 static void usage(void); 172 173 static long pct(long, long); 174 static long getuptime(void); 175 176 static char **getdrivedata(char **); 177 178 int 179 main(int argc, char *argv[]) 180 { 181 int c, todo; 182 unsigned int interval; 183 int reps; 184 char *memf, *nlistf; 185 char errbuf[_POSIX2_LINE_MAX]; 186 187 memf = nlistf = NULL; 188 interval = reps = todo = 0; 189 maxshowdevs = 2; 190 hflag = isatty(1); 191 while ((c = getopt(argc, argv, "ac:fhHiM:mN:n:Pp:stw:z")) != -1) { 192 switch (c) { 193 case 'a': 194 aflag++; 195 break; 196 case 'c': 197 reps = atoi(optarg); 198 break; 199 case 'P': 200 Pflag++; 201 break; 202 case 'f': 203 todo |= FORKSTAT; 204 break; 205 case 'h': 206 hflag = 1; 207 break; 208 case 'H': 209 hflag = 0; 210 break; 211 case 'i': 212 todo |= INTRSTAT; 213 break; 214 case 'M': 215 memf = optarg; 216 break; 217 case 'm': 218 todo |= MEMSTAT; 219 break; 220 case 'N': 221 nlistf = optarg; 222 break; 223 case 'n': 224 nflag = 1; 225 maxshowdevs = atoi(optarg); 226 if (maxshowdevs < 0) 227 errx(1, "number of devices %d is < 0", 228 maxshowdevs); 229 break; 230 case 'p': 231 if (devstat_buildmatch(optarg, &matches, &num_matches) != 0) 232 errx(1, "%s", devstat_errbuf); 233 break; 234 case 's': 235 todo |= SUMSTAT; 236 break; 237 case 't': 238 #ifdef notyet 239 todo |= TIMESTAT; 240 #else 241 errx(EX_USAGE, "sorry, -t is not (re)implemented yet"); 242 #endif 243 break; 244 case 'w': 245 interval = atoi(optarg); 246 break; 247 case 'z': 248 todo |= ZMEMSTAT; 249 break; 250 case '?': 251 default: 252 usage(); 253 } 254 } 255 argc -= optind; 256 argv += optind; 257 258 if (todo == 0) 259 todo = VMSTAT; 260 261 if (memf != NULL) { 262 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 263 if (kd == NULL) 264 errx(1, "kvm_openfiles: %s", errbuf); 265 } 266 267 if (kd != NULL && (c = kvm_nlist(kd, namelist)) != 0) { 268 if (c > 0) { 269 warnx("undefined symbols:"); 270 for (c = 0; 271 c < (int)(sizeof(namelist)/sizeof(namelist[0])); 272 c++) 273 if (namelist[c].n_type == 0) 274 (void)fprintf(stderr, " %s", 275 namelist[c].n_name); 276 (void)fputc('\n', stderr); 277 } else 278 warnx("kvm_nlist: %s", kvm_geterr(kd)); 279 exit(1); 280 } 281 if (kd && Pflag) 282 errx(1, "Cannot use -P with crash dumps"); 283 284 if (todo & VMSTAT) { 285 /* 286 * Make sure that the userland devstat version matches the 287 * kernel devstat version. If not, exit and print a 288 * message informing the user of his mistake. 289 */ 290 if (devstat_checkversion(NULL) < 0) 291 errx(1, "%s", devstat_errbuf); 292 293 294 argv = getdrivedata(argv); 295 } 296 297 #define BACKWARD_COMPATIBILITY 298 #ifdef BACKWARD_COMPATIBILITY 299 if (*argv) { 300 interval = atoi(*argv); 301 if (*++argv) 302 reps = atoi(*argv); 303 } 304 #endif 305 306 if (interval) { 307 if (!reps) 308 reps = -1; 309 } else if (reps) 310 interval = 1; 311 312 if (todo & FORKSTAT) 313 doforkst(); 314 if (todo & MEMSTAT) 315 domemstat_malloc(); 316 if (todo & ZMEMSTAT) 317 domemstat_zone(); 318 if (todo & SUMSTAT) 319 dosum(); 320 #ifdef notyet 321 if (todo & TIMESTAT) 322 dotimes(); 323 #endif 324 if (todo & INTRSTAT) 325 dointr(); 326 if (todo & VMSTAT) 327 dovmstat(interval, reps); 328 exit(0); 329 } 330 331 static int 332 mysysctl(const char *name, void *oldp, size_t *oldlenp, 333 void *newp, size_t newlen) 334 { 335 int error; 336 337 error = sysctlbyname(name, oldp, oldlenp, newp, newlen); 338 if (error != 0 && errno != ENOMEM) 339 err(1, "sysctl(%s)", name); 340 return (error); 341 } 342 343 static char ** 344 getdrivedata(char **argv) 345 { 346 if ((num_devices = devstat_getnumdevs(NULL)) < 0) 347 errx(1, "%s", devstat_errbuf); 348 349 cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 350 last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 351 352 if (devstat_getdevs(NULL, &cur) == -1) 353 errx(1, "%s", devstat_errbuf); 354 355 num_devices = cur.dinfo->numdevs; 356 generation = cur.dinfo->generation; 357 358 specified_devices = (char **)malloc(sizeof(char *)); 359 for (num_devices_specified = 0; *argv; ++argv) { 360 if (isdigit(**argv)) 361 break; 362 num_devices_specified++; 363 specified_devices = (char **)realloc(specified_devices, 364 sizeof(char *) * 365 num_devices_specified); 366 specified_devices[num_devices_specified - 1] = *argv; 367 } 368 dev_select = NULL; 369 370 if (nflag == 0 && maxshowdevs < num_devices_specified) 371 maxshowdevs = num_devices_specified; 372 373 /* 374 * People are generally only interested in disk statistics when 375 * they're running vmstat. So, that's what we're going to give 376 * them if they don't specify anything by default. We'll also give 377 * them any other random devices in the system so that we get to 378 * maxshowdevs devices, if that many devices exist. If the user 379 * specifies devices on the command line, either through a pattern 380 * match or by naming them explicitly, we will give the user only 381 * those devices. 382 */ 383 if ((num_devices_specified == 0) && (num_matches == 0)) { 384 if (devstat_buildmatch(da, &matches, &num_matches) != 0) 385 errx(1, "%s", devstat_errbuf); 386 387 select_mode = DS_SELECT_ADD; 388 } else 389 select_mode = DS_SELECT_ONLY; 390 391 /* 392 * At this point, selectdevs will almost surely indicate that the 393 * device list has changed, so we don't look for return values of 0 394 * or 1. If we get back -1, though, there is an error. 395 */ 396 if (devstat_selectdevs(&dev_select, &num_selected, &num_selections, 397 &select_generation, generation, cur.dinfo->devices, 398 num_devices, matches, num_matches, specified_devices, 399 num_devices_specified, select_mode, 400 maxshowdevs, 0) == -1) 401 errx(1, "%s", devstat_errbuf); 402 403 return(argv); 404 } 405 406 static long 407 getuptime(void) 408 { 409 struct timespec sp; 410 time_t uptime; 411 412 (void)clock_gettime(CLOCK_MONOTONIC, &sp); 413 uptime = sp.tv_sec; 414 if (uptime <= 0 || uptime > 60*60*24*365*10) 415 errx(1, "time makes no sense; namelist must be wrong"); 416 417 return(uptime); 418 } 419 420 static void 421 fill_vmmeter(struct vmmeter *vmmp) 422 { 423 if (kd != NULL) { 424 kread(X_SUM, vmmp, sizeof(*vmmp)); 425 } else { 426 size_t size = sizeof(unsigned int); 427 #define GET_VM_STATS(cat, name) \ 428 mysysctl("vm.stats." #cat "." #name, &vmmp->name, &size, NULL, 0) 429 /* sys */ 430 GET_VM_STATS(sys, v_swtch); 431 GET_VM_STATS(sys, v_trap); 432 GET_VM_STATS(sys, v_syscall); 433 GET_VM_STATS(sys, v_intr); 434 GET_VM_STATS(sys, v_soft); 435 436 /* vm */ 437 GET_VM_STATS(vm, v_vm_faults); 438 GET_VM_STATS(vm, v_cow_faults); 439 GET_VM_STATS(vm, v_cow_optim); 440 GET_VM_STATS(vm, v_zfod); 441 GET_VM_STATS(vm, v_ozfod); 442 GET_VM_STATS(vm, v_swapin); 443 GET_VM_STATS(vm, v_swapout); 444 GET_VM_STATS(vm, v_swappgsin); 445 GET_VM_STATS(vm, v_swappgsout); 446 GET_VM_STATS(vm, v_vnodein); 447 GET_VM_STATS(vm, v_vnodeout); 448 GET_VM_STATS(vm, v_vnodepgsin); 449 GET_VM_STATS(vm, v_vnodepgsout); 450 GET_VM_STATS(vm, v_intrans); 451 GET_VM_STATS(vm, v_reactivated); 452 GET_VM_STATS(vm, v_pdwakeups); 453 GET_VM_STATS(vm, v_pdpages); 454 GET_VM_STATS(vm, v_tcached); 455 GET_VM_STATS(vm, v_dfree); 456 GET_VM_STATS(vm, v_pfree); 457 GET_VM_STATS(vm, v_tfree); 458 GET_VM_STATS(vm, v_page_size); 459 GET_VM_STATS(vm, v_page_count); 460 GET_VM_STATS(vm, v_free_reserved); 461 GET_VM_STATS(vm, v_free_target); 462 GET_VM_STATS(vm, v_free_min); 463 GET_VM_STATS(vm, v_free_count); 464 GET_VM_STATS(vm, v_wire_count); 465 GET_VM_STATS(vm, v_active_count); 466 GET_VM_STATS(vm, v_inactive_target); 467 GET_VM_STATS(vm, v_inactive_count); 468 GET_VM_STATS(vm, v_cache_count); 469 GET_VM_STATS(vm, v_cache_min); 470 GET_VM_STATS(vm, v_cache_max); 471 GET_VM_STATS(vm, v_pageout_free_min); 472 GET_VM_STATS(vm, v_interrupt_free_min); 473 /*GET_VM_STATS(vm, v_free_severe);*/ 474 GET_VM_STATS(vm, v_forks); 475 GET_VM_STATS(vm, v_vforks); 476 GET_VM_STATS(vm, v_rforks); 477 GET_VM_STATS(vm, v_kthreads); 478 GET_VM_STATS(vm, v_forkpages); 479 GET_VM_STATS(vm, v_vforkpages); 480 GET_VM_STATS(vm, v_rforkpages); 481 GET_VM_STATS(vm, v_kthreadpages); 482 #undef GET_VM_STATS 483 } 484 } 485 486 static void 487 fill_vmtotal(struct vmtotal *vmtp) 488 { 489 if (kd != NULL) { 490 /* XXX fill vmtp */ 491 errx(1, "not implemented"); 492 } else { 493 size_t size = sizeof(*vmtp); 494 mysysctl("vm.vmtotal", vmtp, &size, NULL, 0); 495 if (size != sizeof(*vmtp)) 496 errx(1, "vm.total size mismatch"); 497 } 498 } 499 500 /* Determine how many cpu columns, and what index they are in kern.cp_times */ 501 static int 502 getcpuinfo(u_long *maskp, int *maxidp) 503 { 504 int maxcpu; 505 int maxid; 506 int ncpus; 507 int i, j; 508 int empty; 509 size_t size; 510 long *times; 511 u_long mask; 512 513 if (kd != NULL) 514 errx(1, "not implemented"); 515 mask = 0; 516 ncpus = 0; 517 size = sizeof(maxcpu); 518 mysysctl("kern.smp.maxcpus", &maxcpu, &size, NULL, 0); 519 if (size != sizeof(maxcpu)) 520 errx(1, "sysctl kern.smp.maxcpus"); 521 size = sizeof(long) * maxcpu * CPUSTATES; 522 times = malloc(size); 523 if (times == NULL) 524 err(1, "malloc %zd bytes", size); 525 mysysctl("kern.cp_times", times, &size, NULL, 0); 526 maxid = (size / CPUSTATES / sizeof(long)) - 1; 527 for (i = 0; i <= maxid; i++) { 528 empty = 1; 529 for (j = 0; empty && j < CPUSTATES; j++) { 530 if (times[i * CPUSTATES + j] != 0) 531 empty = 0; 532 } 533 if (!empty) { 534 mask |= (1ul << i); 535 ncpus++; 536 } 537 } 538 if (maskp) 539 *maskp = mask; 540 if (maxidp) 541 *maxidp = maxid; 542 return (ncpus); 543 } 544 545 546 static void 547 prthuman(u_int64_t val, int size) 548 { 549 char buf[10]; 550 int flags; 551 552 if (size < 5 || size > 9) 553 errx(1, "doofus"); 554 flags = HN_B | HN_NOSPACE | HN_DECIMAL; 555 humanize_number(buf, size, val, "", HN_AUTOSCALE, flags); 556 printf("%*s", size, buf); 557 } 558 559 static int hz, hdrcnt; 560 561 static long *cur_cp_times; 562 static long *last_cp_times; 563 static size_t size_cp_times; 564 565 static void 566 dovmstat(unsigned int interval, int reps) 567 { 568 struct vmtotal total; 569 time_t uptime, halfuptime; 570 struct devinfo *tmp_dinfo; 571 size_t size; 572 int ncpus, maxid; 573 u_long cpumask; 574 575 uptime = getuptime(); 576 halfuptime = uptime / 2; 577 578 /* 579 * If the user stops the program (control-Z) and then resumes it, 580 * print out the header again. 581 */ 582 (void)signal(SIGCONT, needhdr); 583 584 /* 585 * If our standard output is a tty, then install a SIGWINCH handler 586 * and set wresized so that our first iteration through the main 587 * vmstat loop will peek at the terminal's current rows to find out 588 * how many lines can fit in a screenful of output. 589 */ 590 if (isatty(fileno(stdout)) != 0) { 591 wresized = 1; 592 (void)signal(SIGWINCH, needresize); 593 } else { 594 wresized = 0; 595 winlines = VMSTAT_DEFAULT_LINES; 596 } 597 598 if (kd != NULL) { 599 if (namelist[X_STATHZ].n_type != 0 && 600 namelist[X_STATHZ].n_value != 0) 601 kread(X_STATHZ, &hz, sizeof(hz)); 602 if (!hz) 603 kread(X_HZ, &hz, sizeof(hz)); 604 } else { 605 struct clockinfo clockrate; 606 607 size = sizeof(clockrate); 608 mysysctl("kern.clockrate", &clockrate, &size, NULL, 0); 609 if (size != sizeof(clockrate)) 610 errx(1, "clockrate size mismatch"); 611 hz = clockrate.hz; 612 } 613 614 if (Pflag) { 615 ncpus = getcpuinfo(&cpumask, &maxid); 616 size_cp_times = sizeof(long) * (maxid + 1) * CPUSTATES; 617 cur_cp_times = calloc(1, size_cp_times); 618 last_cp_times = calloc(1, size_cp_times); 619 } 620 for (hdrcnt = 1;;) { 621 if (!--hdrcnt) 622 printhdr(ncpus, cpumask); 623 if (kd != NULL) { 624 if (kvm_getcptime(kd, cur.cp_time) < 0) 625 errx(1, "kvm_getcptime: %s", kvm_geterr(kd)); 626 } else { 627 size = sizeof(cur.cp_time); 628 mysysctl("kern.cp_time", &cur.cp_time, &size, NULL, 0); 629 if (size != sizeof(cur.cp_time)) 630 errx(1, "cp_time size mismatch"); 631 } 632 if (Pflag) { 633 size = size_cp_times; 634 mysysctl("kern.cp_times", cur_cp_times, &size, NULL, 0); 635 if (size != size_cp_times) 636 errx(1, "cp_times mismatch"); 637 } 638 639 tmp_dinfo = last.dinfo; 640 last.dinfo = cur.dinfo; 641 cur.dinfo = tmp_dinfo; 642 last.snap_time = cur.snap_time; 643 644 /* 645 * Here what we want to do is refresh our device stats. 646 * getdevs() returns 1 when the device list has changed. 647 * If the device list has changed, we want to go through 648 * the selection process again, in case a device that we 649 * were previously displaying has gone away. 650 */ 651 switch (devstat_getdevs(NULL, &cur)) { 652 case -1: 653 errx(1, "%s", devstat_errbuf); 654 break; 655 case 1: { 656 int retval; 657 658 num_devices = cur.dinfo->numdevs; 659 generation = cur.dinfo->generation; 660 661 retval = devstat_selectdevs(&dev_select, &num_selected, 662 &num_selections, &select_generation, 663 generation, cur.dinfo->devices, 664 num_devices, matches, num_matches, 665 specified_devices, 666 num_devices_specified, select_mode, 667 maxshowdevs, 0); 668 switch (retval) { 669 case -1: 670 errx(1, "%s", devstat_errbuf); 671 break; 672 case 1: 673 printhdr(ncpus, cpumask); 674 break; 675 default: 676 break; 677 } 678 } 679 default: 680 break; 681 } 682 683 fill_vmmeter(&sum); 684 fill_vmtotal(&total); 685 (void)printf("%2d %1d %1d", 686 total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw); 687 #define vmstat_pgtok(a) ((a) * (sum.v_page_size >> 10)) 688 #define rate(x) (((x) + halfuptime) / uptime) /* round */ 689 if (hflag) { 690 printf(" "); 691 prthuman(total.t_avm * (u_int64_t)sum.v_page_size, 7); 692 printf(" "); 693 prthuman(total.t_free * (u_int64_t)sum.v_page_size, 6); 694 printf(" "); 695 } else { 696 printf(" %7d ", vmstat_pgtok(total.t_avm)); 697 printf(" %6d ", vmstat_pgtok(total.t_free)); 698 } 699 (void)printf("%5lu ", 700 (unsigned long)rate(sum.v_vm_faults - osum.v_vm_faults)); 701 (void)printf("%3lu ", 702 (unsigned long)rate(sum.v_reactivated - osum.v_reactivated)); 703 (void)printf("%3lu ", 704 (unsigned long)rate(sum.v_swapin + sum.v_vnodein - 705 (osum.v_swapin + osum.v_vnodein))); 706 (void)printf("%3lu ", 707 (unsigned long)rate(sum.v_swapout + sum.v_vnodeout - 708 (osum.v_swapout + osum.v_vnodeout))); 709 (void)printf("%5lu ", 710 (unsigned long)rate(sum.v_tfree - osum.v_tfree)); 711 (void)printf("%3lu ", 712 (unsigned long)rate(sum.v_pdpages - osum.v_pdpages)); 713 devstats(); 714 (void)printf("%4lu %4lu %4lu", 715 (unsigned long)rate(sum.v_intr - osum.v_intr), 716 (unsigned long)rate(sum.v_syscall - osum.v_syscall), 717 (unsigned long)rate(sum.v_swtch - osum.v_swtch)); 718 if (Pflag) 719 pcpustats(ncpus, cpumask, maxid); 720 else 721 cpustats(); 722 (void)printf("\n"); 723 (void)fflush(stdout); 724 if (reps >= 0 && --reps <= 0) 725 break; 726 osum = sum; 727 uptime = interval; 728 /* 729 * We round upward to avoid losing low-frequency events 730 * (i.e., >= 1 per interval but < 1 per second). 731 */ 732 if (interval != 1) 733 halfuptime = (uptime + 1) / 2; 734 else 735 halfuptime = 0; 736 (void)sleep(interval); 737 } 738 } 739 740 static void 741 printhdr(int ncpus, u_long cpumask) 742 { 743 int i, num_shown; 744 745 num_shown = (num_selected < maxshowdevs) ? num_selected : maxshowdevs; 746 (void)printf(" procs memory page%*s", 19, ""); 747 if (num_shown > 1) 748 (void)printf(" disks %*s", num_shown * 4 - 7, ""); 749 else if (num_shown == 1) 750 (void)printf("disk"); 751 (void)printf(" faults "); 752 if (Pflag) { 753 for (i = 0; i < ncpus; i++) { 754 if (cpumask & (1ul << i)) 755 printf("cpu%-2d ", i); 756 } 757 printf("\n"); 758 } else 759 printf("cpu\n"); 760 (void)printf(" r b w avm fre flt re pi po fr sr "); 761 for (i = 0; i < num_devices; i++) 762 if ((dev_select[i].selected) 763 && (dev_select[i].selected <= maxshowdevs)) 764 (void)printf("%c%c%d ", dev_select[i].device_name[0], 765 dev_select[i].device_name[1], 766 dev_select[i].unit_number); 767 (void)printf(" in sy cs"); 768 if (Pflag) { 769 for (i = 0; i < ncpus; i++) 770 printf(" us sy id"); 771 printf("\n"); 772 } else 773 printf(" us sy id\n"); 774 if (wresized != 0) 775 doresize(); 776 hdrcnt = winlines; 777 } 778 779 /* 780 * Force a header to be prepended to the next output. 781 */ 782 static void 783 needhdr(int dummy __unused) 784 { 785 786 hdrcnt = 1; 787 } 788 789 /* 790 * When the terminal is resized, force an update of the maximum number of rows 791 * printed between each header repetition. Then force a new header to be 792 * prepended to the next output. 793 */ 794 void 795 needresize(int signo) 796 { 797 798 wresized = 1; 799 hdrcnt = 1; 800 } 801 802 /* 803 * Update the global `winlines' count of terminal rows. 804 */ 805 void 806 doresize(void) 807 { 808 int status; 809 struct winsize w; 810 811 for (;;) { 812 status = ioctl(fileno(stdout), TIOCGWINSZ, &w); 813 if (status == -1 && errno == EINTR) 814 continue; 815 else if (status == -1) 816 err(1, "ioctl"); 817 if (w.ws_row > 3) 818 winlines = w.ws_row - 3; 819 else 820 winlines = VMSTAT_DEFAULT_LINES; 821 break; 822 } 823 824 /* 825 * Inhibit doresize() calls until we are rescheduled by SIGWINCH. 826 */ 827 wresized = 0; 828 } 829 830 #ifdef notyet 831 static void 832 dotimes(void) 833 { 834 unsigned int pgintime, rectime; 835 836 kread(X_REC, &rectime, sizeof(rectime)); 837 kread(X_PGIN, &pgintime, sizeof(pgintime)); 838 kread(X_SUM, &sum, sizeof(sum)); 839 (void)printf("%u reclaims, %u total time (usec)\n", 840 sum.v_pgrec, rectime); 841 (void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec); 842 (void)printf("\n"); 843 (void)printf("%u page ins, %u total time (msec)\n", 844 sum.v_pgin, pgintime / 10); 845 (void)printf("average: %8.1f msec / page in\n", 846 pgintime / (sum.v_pgin * 10.0)); 847 } 848 #endif 849 850 static long 851 pct(long top, long bot) 852 { 853 long ans; 854 855 if (bot == 0) 856 return(0); 857 ans = (quad_t)top * 100 / bot; 858 return (ans); 859 } 860 861 #define PCT(top, bot) pct((long)(top), (long)(bot)) 862 863 static void 864 dosum(void) 865 { 866 struct nchstats lnchstats; 867 long nchtotal; 868 869 fill_vmmeter(&sum); 870 (void)printf("%9u cpu context switches\n", sum.v_swtch); 871 (void)printf("%9u device interrupts\n", sum.v_intr); 872 (void)printf("%9u software interrupts\n", sum.v_soft); 873 (void)printf("%9u traps\n", sum.v_trap); 874 (void)printf("%9u system calls\n", sum.v_syscall); 875 (void)printf("%9u kernel threads created\n", sum.v_kthreads); 876 (void)printf("%9u fork() calls\n", sum.v_forks); 877 (void)printf("%9u vfork() calls\n", sum.v_vforks); 878 (void)printf("%9u rfork() calls\n", sum.v_rforks); 879 (void)printf("%9u swap pager pageins\n", sum.v_swapin); 880 (void)printf("%9u swap pager pages paged in\n", sum.v_swappgsin); 881 (void)printf("%9u swap pager pageouts\n", sum.v_swapout); 882 (void)printf("%9u swap pager pages paged out\n", sum.v_swappgsout); 883 (void)printf("%9u vnode pager pageins\n", sum.v_vnodein); 884 (void)printf("%9u vnode pager pages paged in\n", sum.v_vnodepgsin); 885 (void)printf("%9u vnode pager pageouts\n", sum.v_vnodeout); 886 (void)printf("%9u vnode pager pages paged out\n", sum.v_vnodepgsout); 887 (void)printf("%9u page daemon wakeups\n", sum.v_pdwakeups); 888 (void)printf("%9u pages examined by the page daemon\n", sum.v_pdpages); 889 (void)printf("%9u pages reactivated\n", sum.v_reactivated); 890 (void)printf("%9u copy-on-write faults\n", sum.v_cow_faults); 891 (void)printf("%9u copy-on-write optimized faults\n", sum.v_cow_optim); 892 (void)printf("%9u zero fill pages zeroed\n", sum.v_zfod); 893 (void)printf("%9u zero fill pages prezeroed\n", sum.v_ozfod); 894 (void)printf("%9u intransit blocking page faults\n", sum.v_intrans); 895 (void)printf("%9u total VM faults taken\n", sum.v_vm_faults); 896 (void)printf("%9u pages affected by kernel thread creation\n", sum.v_kthreadpages); 897 (void)printf("%9u pages affected by fork()\n", sum.v_forkpages); 898 (void)printf("%9u pages affected by vfork()\n", sum.v_vforkpages); 899 (void)printf("%9u pages affected by rfork()\n", sum.v_rforkpages); 900 (void)printf("%9u pages cached\n", sum.v_tcached); 901 (void)printf("%9u pages freed\n", sum.v_tfree); 902 (void)printf("%9u pages freed by daemon\n", sum.v_dfree); 903 (void)printf("%9u pages freed by exiting processes\n", sum.v_pfree); 904 (void)printf("%9u pages active\n", sum.v_active_count); 905 (void)printf("%9u pages inactive\n", sum.v_inactive_count); 906 (void)printf("%9u pages in VM cache\n", sum.v_cache_count); 907 (void)printf("%9u pages wired down\n", sum.v_wire_count); 908 (void)printf("%9u pages free\n", sum.v_free_count); 909 (void)printf("%9u bytes per page\n", sum.v_page_size); 910 if (kd != NULL) { 911 kread(X_NCHSTATS, &lnchstats, sizeof(lnchstats)); 912 } else { 913 size_t size = sizeof(lnchstats); 914 mysysctl("vfs.cache.nchstats", &lnchstats, &size, NULL, 0); 915 if (size != sizeof(lnchstats)) 916 errx(1, "vfs.cache.nchstats size mismatch"); 917 } 918 nchtotal = lnchstats.ncs_goodhits + lnchstats.ncs_neghits + 919 lnchstats.ncs_badhits + lnchstats.ncs_falsehits + 920 lnchstats.ncs_miss + lnchstats.ncs_long; 921 (void)printf("%9ld total name lookups\n", nchtotal); 922 (void)printf( 923 "%9s cache hits (%ld%% pos + %ld%% neg) system %ld%% per-directory\n", 924 "", PCT(lnchstats.ncs_goodhits, nchtotal), 925 PCT(lnchstats.ncs_neghits, nchtotal), 926 PCT(lnchstats.ncs_pass2, nchtotal)); 927 (void)printf("%9s deletions %ld%%, falsehits %ld%%, toolong %ld%%\n", "", 928 PCT(lnchstats.ncs_badhits, nchtotal), 929 PCT(lnchstats.ncs_falsehits, nchtotal), 930 PCT(lnchstats.ncs_long, nchtotal)); 931 } 932 933 static void 934 doforkst(void) 935 { 936 fill_vmmeter(&sum); 937 (void)printf("%u forks, %u pages, average %.2f\n", 938 sum.v_forks, sum.v_forkpages, 939 sum.v_forks == 0 ? 0.0 : 940 (double)sum.v_forkpages / sum.v_forks); 941 (void)printf("%u vforks, %u pages, average %.2f\n", 942 sum.v_vforks, sum.v_vforkpages, 943 sum.v_vforks == 0 ? 0.0 : 944 (double)sum.v_vforkpages / sum.v_vforks); 945 (void)printf("%u rforks, %u pages, average %.2f\n", 946 sum.v_rforks, sum.v_rforkpages, 947 sum.v_rforks == 0 ? 0.0 : 948 (double)sum.v_rforkpages / sum.v_rforks); 949 } 950 951 static void 952 devstats(void) 953 { 954 int dn, state; 955 long double transfers_per_second; 956 long double busy_seconds; 957 long tmp; 958 959 for (state = 0; state < CPUSTATES; ++state) { 960 tmp = cur.cp_time[state]; 961 cur.cp_time[state] -= last.cp_time[state]; 962 last.cp_time[state] = tmp; 963 } 964 965 busy_seconds = cur.snap_time - last.snap_time; 966 967 for (dn = 0; dn < num_devices; dn++) { 968 int di; 969 970 if ((dev_select[dn].selected == 0) 971 || (dev_select[dn].selected > maxshowdevs)) 972 continue; 973 974 di = dev_select[dn].position; 975 976 if (devstat_compute_statistics(&cur.dinfo->devices[di], 977 &last.dinfo->devices[di], busy_seconds, 978 DSM_TRANSFERS_PER_SECOND, &transfers_per_second, 979 DSM_NONE) != 0) 980 errx(1, "%s", devstat_errbuf); 981 982 (void)printf("%3.0Lf ", transfers_per_second); 983 } 984 } 985 986 static void 987 percent(double pct, int *over) 988 { 989 char buf[10]; 990 int l; 991 992 l = snprintf(buf, sizeof(buf), "%.0f", pct); 993 if (l == 1 && *over) { 994 printf("%s", buf); 995 (*over)--; 996 } else 997 printf("%2s", buf); 998 if (l > 2) 999 (*over)++; 1000 } 1001 1002 static void 1003 cpustats(void) 1004 { 1005 int state, over; 1006 double lpct, total; 1007 1008 total = 0; 1009 for (state = 0; state < CPUSTATES; ++state) 1010 total += cur.cp_time[state]; 1011 if (total) 1012 lpct = 100.0 / total; 1013 else 1014 lpct = 0.0; 1015 over = 0; 1016 printf(" "); 1017 percent((cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * lpct, &over); 1018 printf(" "); 1019 percent((cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * lpct, &over); 1020 printf(" "); 1021 percent(cur.cp_time[CP_IDLE] * lpct, &over); 1022 } 1023 1024 static void 1025 pcpustats(int ncpus, u_long cpumask, int maxid) 1026 { 1027 int state, i; 1028 double lpct, total; 1029 long tmp; 1030 int over; 1031 1032 /* devstats does this for cp_time */ 1033 for (i = 0; i <= maxid; i++) { 1034 if ((cpumask & (1ul << i)) == 0) 1035 continue; 1036 for (state = 0; state < CPUSTATES; ++state) { 1037 tmp = cur_cp_times[i * CPUSTATES + state]; 1038 cur_cp_times[i * CPUSTATES + state] -= last_cp_times[i * CPUSTATES + state]; 1039 last_cp_times[i * CPUSTATES + state] = tmp; 1040 } 1041 } 1042 1043 over = 0; 1044 for (i = 0; i <= maxid; i++) { 1045 if ((cpumask & (1ul << i)) == 0) 1046 continue; 1047 total = 0; 1048 for (state = 0; state < CPUSTATES; ++state) 1049 total += cur_cp_times[i * CPUSTATES + state]; 1050 if (total) 1051 lpct = 100.0 / total; 1052 else 1053 lpct = 0.0; 1054 printf(" "); 1055 percent((cur_cp_times[i * CPUSTATES + CP_USER] + 1056 cur_cp_times[i * CPUSTATES + CP_NICE]) * lpct, &over); 1057 printf(" "); 1058 percent((cur_cp_times[i * CPUSTATES + CP_SYS] + 1059 cur_cp_times[i * CPUSTATES + CP_INTR]) * lpct, &over); 1060 printf(" "); 1061 percent(cur_cp_times[i * CPUSTATES + CP_IDLE] * lpct, &over); 1062 } 1063 } 1064 1065 static void 1066 dointr(void) 1067 { 1068 unsigned long *intrcnt, uptime; 1069 uint64_t inttotal; 1070 size_t clen, inamlen, intrcntlen, istrnamlen; 1071 unsigned int i, nintr; 1072 char *intrname, *tintrname; 1073 1074 uptime = getuptime(); 1075 if (kd != NULL) { 1076 intrcntlen = namelist[X_EINTRCNT].n_value - 1077 namelist[X_INTRCNT].n_value; 1078 inamlen = namelist[X_EINTRNAMES].n_value - 1079 namelist[X_INTRNAMES].n_value; 1080 if ((intrcnt = malloc(intrcntlen)) == NULL || 1081 (intrname = malloc(inamlen)) == NULL) 1082 err(1, "malloc()"); 1083 kread(X_INTRCNT, intrcnt, intrcntlen); 1084 kread(X_INTRNAMES, intrname, inamlen); 1085 } else { 1086 for (intrcnt = NULL, intrcntlen = 1024; ; intrcntlen *= 2) { 1087 if ((intrcnt = reallocf(intrcnt, intrcntlen)) == NULL) 1088 err(1, "reallocf()"); 1089 if (mysysctl("hw.intrcnt", 1090 intrcnt, &intrcntlen, NULL, 0) == 0) 1091 break; 1092 } 1093 for (intrname = NULL, inamlen = 1024; ; inamlen *= 2) { 1094 if ((intrname = reallocf(intrname, inamlen)) == NULL) 1095 err(1, "reallocf()"); 1096 if (mysysctl("hw.intrnames", 1097 intrname, &inamlen, NULL, 0) == 0) 1098 break; 1099 } 1100 } 1101 nintr = intrcntlen / sizeof(unsigned long); 1102 tintrname = intrname; 1103 istrnamlen = strlen("interrupt"); 1104 for (i = 0; i < nintr; i++) { 1105 clen = strlen(tintrname); 1106 if (clen > istrnamlen) 1107 istrnamlen = clen; 1108 tintrname += clen + 1; 1109 } 1110 (void)printf("%-*s %20s %10s\n", istrnamlen, "interrupt", "total", 1111 "rate"); 1112 inttotal = 0; 1113 for (i = 0; i < nintr; i++) { 1114 if (intrname[0] != '\0' && (*intrcnt != 0 || aflag)) 1115 (void)printf("%-*s %20lu %10lu\n", istrnamlen, intrname, 1116 *intrcnt, *intrcnt / uptime); 1117 intrname += strlen(intrname) + 1; 1118 inttotal += *intrcnt++; 1119 } 1120 (void)printf("%-*s %20llu %10llu\n", istrnamlen, "Total", 1121 (long long)inttotal, (long long)(inttotal / uptime)); 1122 } 1123 1124 static void 1125 domemstat_malloc(void) 1126 { 1127 struct memory_type_list *mtlp; 1128 struct memory_type *mtp; 1129 int error, first, i; 1130 1131 mtlp = memstat_mtl_alloc(); 1132 if (mtlp == NULL) { 1133 warn("memstat_mtl_alloc"); 1134 return; 1135 } 1136 if (kd == NULL) { 1137 if (memstat_sysctl_malloc(mtlp, 0) < 0) { 1138 warnx("memstat_sysctl_malloc: %s", 1139 memstat_strerror(memstat_mtl_geterror(mtlp))); 1140 return; 1141 } 1142 } else { 1143 if (memstat_kvm_malloc(mtlp, kd) < 0) { 1144 error = memstat_mtl_geterror(mtlp); 1145 if (error == MEMSTAT_ERROR_KVM) 1146 warnx("memstat_kvm_malloc: %s", 1147 kvm_geterr(kd)); 1148 else 1149 warnx("memstat_kvm_malloc: %s", 1150 memstat_strerror(error)); 1151 } 1152 } 1153 printf("%13s %5s %6s %7s %8s Size(s)\n", "Type", "InUse", "MemUse", 1154 "HighUse", "Requests"); 1155 for (mtp = memstat_mtl_first(mtlp); mtp != NULL; 1156 mtp = memstat_mtl_next(mtp)) { 1157 if (memstat_get_numallocs(mtp) == 0 && 1158 memstat_get_count(mtp) == 0) 1159 continue; 1160 printf("%13s %5lld %5lldK %7s %8lld ", 1161 memstat_get_name(mtp), memstat_get_count(mtp), 1162 ((int64_t)memstat_get_bytes(mtp) + 1023) / 1024, "-", 1163 memstat_get_numallocs(mtp)); 1164 first = 1; 1165 for (i = 0; i < 32; i++) { 1166 if (memstat_get_sizemask(mtp) & (1 << i)) { 1167 if (!first) 1168 printf(","); 1169 printf("%d", 1 << (i + 4)); 1170 first = 0; 1171 } 1172 } 1173 printf("\n"); 1174 } 1175 memstat_mtl_free(mtlp); 1176 } 1177 1178 static void 1179 domemstat_zone(void) 1180 { 1181 struct memory_type_list *mtlp; 1182 struct memory_type *mtp; 1183 char name[MEMTYPE_MAXNAME + 1]; 1184 int error; 1185 1186 mtlp = memstat_mtl_alloc(); 1187 if (mtlp == NULL) { 1188 warn("memstat_mtl_alloc"); 1189 return; 1190 } 1191 if (kd == NULL) { 1192 if (memstat_sysctl_uma(mtlp, 0) < 0) { 1193 warnx("memstat_sysctl_uma: %s", 1194 memstat_strerror(memstat_mtl_geterror(mtlp))); 1195 return; 1196 } 1197 } else { 1198 if (memstat_kvm_uma(mtlp, kd) < 0) { 1199 error = memstat_mtl_geterror(mtlp); 1200 if (error == MEMSTAT_ERROR_KVM) 1201 warnx("memstat_kvm_uma: %s", 1202 kvm_geterr(kd)); 1203 else 1204 warnx("memstat_kvm_uma: %s", 1205 memstat_strerror(error)); 1206 } 1207 } 1208 printf("%-20s %8s %8s %8s %8s %8s %8s\n\n", "ITEM", "SIZE", 1209 "LIMIT", "USED", "FREE", "REQUESTS", "FAILURES"); 1210 for (mtp = memstat_mtl_first(mtlp); mtp != NULL; 1211 mtp = memstat_mtl_next(mtp)) { 1212 strlcpy(name, memstat_get_name(mtp), MEMTYPE_MAXNAME); 1213 strcat(name, ":"); 1214 printf("%-20s %8llu, %8llu, %8llu, %8llu, %8llu, %8llu\n", name, 1215 memstat_get_size(mtp), memstat_get_countlimit(mtp), 1216 memstat_get_count(mtp), memstat_get_free(mtp), 1217 memstat_get_numallocs(mtp), memstat_get_failures(mtp)); 1218 } 1219 memstat_mtl_free(mtlp); 1220 printf("\n"); 1221 } 1222 1223 /* 1224 * kread reads something from the kernel, given its nlist index. 1225 */ 1226 static void 1227 kreado(int nlx, void *addr, size_t size, size_t offset) 1228 { 1229 const char *sym; 1230 1231 if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) { 1232 sym = namelist[nlx].n_name; 1233 if (*sym == '_') 1234 ++sym; 1235 errx(1, "symbol %s not defined", sym); 1236 } 1237 if ((size_t)kvm_read(kd, namelist[nlx].n_value + offset, addr, 1238 size) != size) { 1239 sym = namelist[nlx].n_name; 1240 if (*sym == '_') 1241 ++sym; 1242 errx(1, "%s: %s", sym, kvm_geterr(kd)); 1243 } 1244 } 1245 1246 static void 1247 kread(int nlx, void *addr, size_t size) 1248 { 1249 kreado(nlx, addr, size, 0); 1250 } 1251 1252 static char * 1253 kgetstr(const char *strp) 1254 { 1255 int n = 0, size = 1; 1256 char *ret = NULL; 1257 1258 do { 1259 if (size == n + 1) { 1260 ret = realloc(ret, size); 1261 if (ret == NULL) 1262 err(1, "%s: realloc", __func__); 1263 size *= 2; 1264 } 1265 if (kvm_read(kd, (u_long)strp + n, &ret[n], 1) != 1) 1266 errx(1, "%s: %s", __func__, kvm_geterr(kd)); 1267 } while (ret[n++] != '\0'); 1268 return (ret); 1269 } 1270 1271 static void 1272 usage(void) 1273 { 1274 (void)fprintf(stderr, "%s%s", 1275 "usage: vmstat [-afHhimPsz] [-c count] [-M core [-N system]] [-w wait]\n", 1276 " [-n devs] [-p type,if,pass] [disks]\n"); 1277 exit(1); 1278 } 1279