| 1 | /* |
| 2 | * Copyright (c) 1980, 1986, 1991 The Regents of the University of California. |
| 3 | * 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 | char copyright[] = |
| 36 | "@(#) Copyright (c) 1980, 1986, 1991 The Regents of the University of California.\n\ |
| 37 | All rights reserved.\n"; |
| 38 | #endif /* not lint */ |
| 39 | |
| 40 | #ifndef lint |
| 41 | static char sccsid[] = "@(#)vmstat.c 5.31 (Berkeley) 7/2/91"; |
| 42 | #endif /* not lint */ |
| 43 | |
| 44 | #include <sys/param.h> |
| 45 | #include <sys/time.h> |
| 46 | #include <sys/proc.h> |
| 47 | #include <sys/user.h> |
| 48 | #include <sys/dkstat.h> |
| 49 | #include <sys/buf.h> |
| 50 | #include <sys/namei.h> |
| 51 | #include <sys/malloc.h> |
| 52 | #include <sys/signal.h> |
| 53 | #include <sys/fcntl.h> |
| 54 | #include <sys/ioctl.h> |
| 55 | #include <sys/vmmeter.h> |
| 56 | #include <vm/vm.h> |
| 57 | #include <vm/vm_statistics.h> |
| 58 | #include <time.h> |
| 59 | #include <nlist.h> |
| 60 | #include <kvm.h> |
| 61 | #include <errno.h> |
| 62 | #include <unistd.h> |
| 63 | #include <stdio.h> |
| 64 | #include <ctype.h> |
| 65 | #include <stdlib.h> |
| 66 | #include <string.h> |
| 67 | #include <paths.h> |
| 68 | |
| 69 | #define NEWVM /* XXX till old has been updated or purged */ |
| 70 | struct nlist nl[] = { |
| 71 | #define X_CPTIME 0 |
| 72 | { "_cp_time" }, |
| 73 | #define X_TOTAL 1 |
| 74 | { "_total" }, |
| 75 | #define X_SUM 2 |
| 76 | { "_cnt" }, /* XXX for now that's where it is */ |
| 77 | #define X_BOOTTIME 3 |
| 78 | { "_boottime" }, |
| 79 | #define X_DKXFER 4 |
| 80 | { "_dk_xfer" }, |
| 81 | #define X_HZ 5 |
| 82 | { "_hz" }, |
| 83 | #define X_PHZ 6 |
| 84 | { "_phz" }, |
| 85 | #define X_NCHSTATS 7 |
| 86 | { "_nchstats" }, |
| 87 | #define X_INTRNAMES 8 |
| 88 | { "_intrnames" }, |
| 89 | #define X_EINTRNAMES 9 |
| 90 | { "_eintrnames" }, |
| 91 | #define X_INTRCNT 10 |
| 92 | { "_intrcnt" }, |
| 93 | #define X_EINTRCNT 11 |
| 94 | { "_eintrcnt" }, |
| 95 | #define X_DK_NDRIVE 12 |
| 96 | { "_dk_ndrive" }, |
| 97 | #define X_KMEMSTAT 13 |
| 98 | { "_kmemstats" }, |
| 99 | #define X_KMEMBUCKETS 14 |
| 100 | { "_bucket" }, |
| 101 | #define X_VMSTAT 15 |
| 102 | { "_vm_stat" }, |
| 103 | #ifdef notdef |
| 104 | #define X_DEFICIT 15 |
| 105 | { "_deficit" }, |
| 106 | #define X_FORKSTAT 16 |
| 107 | { "_forkstat" }, |
| 108 | #define X_REC 17 |
| 109 | { "_rectime" }, |
| 110 | #define X_PGIN 18 |
| 111 | { "_pgintime" }, |
| 112 | #define X_XSTATS 19 |
| 113 | { "_xstats" }, |
| 114 | #define X_END 19 |
| 115 | #else |
| 116 | #define X_END 15 |
| 117 | #endif |
| 118 | #ifdef hp300 |
| 119 | #define X_HPDINIT (X_END+1) |
| 120 | { "_hp_dinit" }, |
| 121 | #endif |
| 122 | #ifdef tahoe |
| 123 | #define X_VBDINIT (X_END+1) |
| 124 | { "_vbdinit" }, |
| 125 | #define X_CKEYSTATS (X_END+2) |
| 126 | { "_ckeystats" }, |
| 127 | #define X_DKEYSTATS (X_END+3) |
| 128 | { "_dkeystats" }, |
| 129 | #endif |
| 130 | #ifdef vax |
| 131 | #define X_MBDINIT (X_END+1) |
| 132 | { "_mbdinit" }, |
| 133 | #define X_UBDINIT (X_END+2) |
| 134 | { "_ubdinit" }, |
| 135 | #endif |
| 136 | #ifdef __386BSD__ |
| 137 | #define X_FREE (X_END+1) |
| 138 | { "_vm_page_free_count" }, |
| 139 | #define X_ACTIVE (X_END+2) |
| 140 | { "_vm_page_active_count" }, |
| 141 | #define X_INACTIVE (X_END+3) |
| 142 | { "_vm_page_inactive_count" }, |
| 143 | #define X_WIRED (X_END+4) |
| 144 | { "_vm_page_wire_count" }, |
| 145 | #define X_PAGESIZE (X_END+5) |
| 146 | { "_page_size" }, |
| 147 | #define X_ISA_BIO (X_END+6) |
| 148 | { "_isa_devtab_bio" }, |
| 149 | #endif /* __386BSD__ */ |
| 150 | { "" }, |
| 151 | }; |
| 152 | |
| 153 | struct _disk { |
| 154 | long time[CPUSTATES]; |
| 155 | long *xfer; |
| 156 | } cur, last; |
| 157 | |
| 158 | struct vm_statistics vm_stat, ostat; |
| 159 | struct vmmeter sum, osum; |
| 160 | char *vmunix = _PATH_UNIX; |
| 161 | char **dr_name; |
| 162 | int *dr_select, dk_ndrive, ndrives; |
| 163 | #ifdef __386BSD__ |
| 164 | /* to make up for statistics that don't get updated */ |
| 165 | int size, free_count, active_count, inactive, wired; |
| 166 | #endif |
| 167 | |
| 168 | int winlines = 20; |
| 169 | |
| 170 | #define FORKSTAT 0x01 |
| 171 | #define INTRSTAT 0x02 |
| 172 | #define MEMSTAT 0x04 |
| 173 | #define SUMSTAT 0x08 |
| 174 | #define TIMESTAT 0x10 |
| 175 | #define VMSTAT 0x20 |
| 176 | |
| 177 | #include "names.c" /* disk names -- machine dependent */ |
| 178 | |
| 179 | void cpustats(), dkstats(), dointr(), domem(), dosum(); |
| 180 | void dovmstat(), kread(), usage(); |
| 181 | #ifdef notdef |
| 182 | void dotimes(), doforkst(); |
| 183 | #endif |
| 184 | |
| 185 | main(argc, argv) |
| 186 | register int argc; |
| 187 | register char **argv; |
| 188 | { |
| 189 | extern int optind; |
| 190 | extern char *optarg; |
| 191 | register int c, todo; |
| 192 | u_int interval; |
| 193 | int reps; |
| 194 | char *kmem; |
| 195 | |
| 196 | kmem = NULL; |
| 197 | interval = reps = todo = 0; |
| 198 | while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != EOF) { |
| 199 | switch (c) { |
| 200 | case 'c': |
| 201 | reps = atoi(optarg); |
| 202 | break; |
| 203 | #ifndef notdef |
| 204 | case 'f': |
| 205 | todo |= FORKSTAT; |
| 206 | break; |
| 207 | #endif |
| 208 | case 'i': |
| 209 | todo |= INTRSTAT; |
| 210 | break; |
| 211 | case 'M': |
| 212 | kmem = optarg; |
| 213 | break; |
| 214 | case 'm': |
| 215 | todo |= MEMSTAT; |
| 216 | break; |
| 217 | case 'N': |
| 218 | vmunix = optarg; |
| 219 | break; |
| 220 | case 's': |
| 221 | todo |= SUMSTAT; |
| 222 | break; |
| 223 | #ifndef notdef |
| 224 | case 't': |
| 225 | todo |= TIMESTAT; |
| 226 | break; |
| 227 | #endif |
| 228 | case 'w': |
| 229 | interval = atoi(optarg); |
| 230 | break; |
| 231 | case '?': |
| 232 | default: |
| 233 | usage(); |
| 234 | } |
| 235 | } |
| 236 | argc -= optind; |
| 237 | argv += optind; |
| 238 | |
| 239 | if (todo == 0) |
| 240 | todo = VMSTAT; |
| 241 | |
| 242 | if (kvm_openfiles(vmunix, kmem, NULL) < 0) { |
| 243 | (void)fprintf(stderr, |
| 244 | "vmstat: kvm_openfiles: %s\n", kvm_geterr()); |
| 245 | exit(1); |
| 246 | } |
| 247 | |
| 248 | if ((c = kvm_nlist(nl)) != 0) { |
| 249 | if (c > 0) { |
| 250 | (void)fprintf(stderr, |
| 251 | "vmstat: undefined symbols in %s:", vmunix); |
| 252 | for (c = 0; c < sizeof(nl)/sizeof(nl[0]); c++) |
| 253 | if (nl[c].n_type == 0) |
| 254 | fprintf(stderr, " %s", nl[c].n_name); |
| 255 | (void)fputc('\n', stderr); |
| 256 | } else |
| 257 | (void)fprintf(stderr, "vmstat: kvm_nlist: %s\n", |
| 258 | kvm_geterr()); |
| 259 | exit(1); |
| 260 | } |
| 261 | |
| 262 | if (todo & VMSTAT) { |
| 263 | char **getdrivedata(); |
| 264 | struct winsize winsize; |
| 265 | |
| 266 | argv = getdrivedata(argv); |
| 267 | winsize.ws_row = 0; |
| 268 | (void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize); |
| 269 | if (winsize.ws_row > 0) |
| 270 | winlines = winsize.ws_row; |
| 271 | |
| 272 | } |
| 273 | |
| 274 | #define BACKWARD_COMPATIBILITY |
| 275 | #ifdef BACKWARD_COMPATIBILITY |
| 276 | if (*argv) { |
| 277 | interval = atoi(*argv); |
| 278 | if (*++argv) |
| 279 | reps = atoi(*argv); |
| 280 | } |
| 281 | #endif |
| 282 | |
| 283 | if (interval) { |
| 284 | if (!reps) |
| 285 | reps = -1; |
| 286 | } else if (reps) |
| 287 | interval = 1; |
| 288 | |
| 289 | #ifdef notdef |
| 290 | if (todo & FORKSTAT) |
| 291 | doforkst(); |
| 292 | #endif |
| 293 | if (todo & MEMSTAT) |
| 294 | domem(); |
| 295 | if (todo & SUMSTAT) |
| 296 | dosum(); |
| 297 | #ifdef notdef |
| 298 | if (todo & TIMESTAT) |
| 299 | dotimes(); |
| 300 | #endif |
| 301 | if (todo & INTRSTAT) |
| 302 | dointr(); |
| 303 | if (todo & VMSTAT) |
| 304 | dovmstat(interval, reps); |
| 305 | exit(0); |
| 306 | } |
| 307 | |
| 308 | char ** |
| 309 | getdrivedata(argv) |
| 310 | char **argv; |
| 311 | { |
| 312 | register int i; |
| 313 | register char **cp; |
| 314 | char buf[30]; |
| 315 | |
| 316 | kread(X_DK_NDRIVE, &dk_ndrive, sizeof(dk_ndrive)); |
| 317 | if (dk_ndrive <= 0) { |
| 318 | (void)fprintf(stderr, "vmstat: dk_ndrive %d\n", dk_ndrive); |
| 319 | exit(1); |
| 320 | } |
| 321 | dr_select = calloc((size_t)dk_ndrive, sizeof(int)); |
| 322 | dr_name = calloc((size_t)dk_ndrive, sizeof(char *)); |
| 323 | for (i = 0; i < dk_ndrive; i++) |
| 324 | dr_name[i] = NULL; |
| 325 | cur.xfer = calloc((size_t)dk_ndrive, sizeof(long)); |
| 326 | last.xfer = calloc((size_t)dk_ndrive, sizeof(long)); |
| 327 | read_names(); |
| 328 | for (i = 0; i < dk_ndrive; i++) |
| 329 | if (dr_name[i] == NULL) { |
| 330 | (void)sprintf(buf, "??%d", i); |
| 331 | dr_name[i] = strdup(buf); |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | * Choose drives to be displayed. Priority goes to (in order) drives |
| 336 | * supplied as arguments, default drives. If everything isn't filled |
| 337 | * in and there are drives not taken care of, display the first few |
| 338 | * that fit. |
| 339 | */ |
| 340 | #define BACKWARD_COMPATIBILITY |
| 341 | for (ndrives = 0; *argv; ++argv) { |
| 342 | #ifdef BACKWARD_COMPATIBILITY |
| 343 | if (isdigit(**argv)) |
| 344 | break; |
| 345 | #endif |
| 346 | for (i = 0; i < dk_ndrive; i++) { |
| 347 | if (strcmp(dr_name[i], *argv)) |
| 348 | continue; |
| 349 | dr_select[i] = 1; |
| 350 | ++ndrives; |
| 351 | break; |
| 352 | } |
| 353 | } |
| 354 | for (i = 0; i < dk_ndrive && ndrives < 4; i++) { |
| 355 | if (dr_select[i]) |
| 356 | continue; |
| 357 | for (cp = defdrives; *cp; cp++) |
| 358 | if (strcmp(dr_name[i], *cp) == 0) { |
| 359 | dr_select[i] = 1; |
| 360 | ++ndrives; |
| 361 | break; |
| 362 | } |
| 363 | } |
| 364 | for (i = 0; i < dk_ndrive && ndrives < 4; i++) { |
| 365 | if (dr_select[i]) |
| 366 | continue; |
| 367 | dr_select[i] = 1; |
| 368 | ++ndrives; |
| 369 | } |
| 370 | return(argv); |
| 371 | } |
| 372 | |
| 373 | #ifdef __386BSD__ |
| 374 | /* |
| 375 | * Make up for the fact that under 0.1, VM doesn't update all of the |
| 376 | * fields in the statistics structures. |
| 377 | */ |
| 378 | |
| 379 | void |
| 380 | fill_in_vm_stat(vm_stat) |
| 381 | struct vm_statistics *vm_stat; |
| 382 | { |
| 383 | kread(X_FREE, &vm_stat->free_count, sizeof(vm_stat->free_count)); |
| 384 | kread(X_ACTIVE, &vm_stat->active_count, sizeof(vm_stat->active_count)); |
| 385 | kread(X_INACTIVE, &vm_stat->inactive_count, |
| 386 | sizeof(vm_stat->inactive_count)); |
| 387 | kread(X_WIRED, &vm_stat->wire_count, sizeof(vm_stat->wire_count)); |
| 388 | kread(X_PAGESIZE, &vm_stat->pagesize, sizeof(vm_stat->pagesize)); |
| 389 | } |
| 390 | #endif |
| 391 | |
| 392 | long |
| 393 | getuptime() |
| 394 | { |
| 395 | static time_t now, boottime; |
| 396 | time_t uptime; |
| 397 | |
| 398 | if (boottime == 0) |
| 399 | kread(X_BOOTTIME, &boottime, sizeof(boottime)); |
| 400 | (void)time(&now); |
| 401 | uptime = now - boottime; |
| 402 | if (uptime <= 0 || uptime > 60*60*24*365*10) { |
| 403 | (void)fprintf(stderr, |
| 404 | "vmstat: time makes no sense; namelist must be wrong.\n"); |
| 405 | exit(1); |
| 406 | } |
| 407 | return(uptime); |
| 408 | } |
| 409 | |
| 410 | int hz, hdrcnt; |
| 411 | |
| 412 | void |
| 413 | dovmstat(interval, reps) |
| 414 | u_int interval; |
| 415 | int reps; |
| 416 | { |
| 417 | struct vmtotal total; |
| 418 | time_t uptime, halfuptime; |
| 419 | void needhdr(); |
| 420 | #ifndef notdef |
| 421 | int deficit; |
| 422 | #endif |
| 423 | |
| 424 | uptime = getuptime(); |
| 425 | halfuptime = uptime / 2; |
| 426 | (void)signal(SIGCONT, needhdr); |
| 427 | |
| 428 | if (nl[X_PHZ].n_type != 0 && nl[X_PHZ].n_value != 0) |
| 429 | kread(X_PHZ, &hz, sizeof(hz)); |
| 430 | if (!hz) |
| 431 | kread(X_HZ, &hz, sizeof(hz)); |
| 432 | |
| 433 | for (hdrcnt = 1;;) { |
| 434 | if (!--hdrcnt) |
| 435 | printhdr(); |
| 436 | kread(X_CPTIME, cur.time, sizeof(cur.time)); |
| 437 | kread(X_DKXFER, cur.xfer, sizeof(*cur.xfer) * dk_ndrive); |
| 438 | kread(X_SUM, &sum, sizeof(sum)); |
| 439 | kread(X_TOTAL, &total, sizeof(total)); |
| 440 | kread(X_VMSTAT, &vm_stat, sizeof(vm_stat)); |
| 441 | #ifdef __386BSD__ |
| 442 | fill_in_vm_stat (&vm_stat); |
| 443 | #endif |
| 444 | #ifdef notdef |
| 445 | kread(X_DEFICIT, &deficit, sizeof(deficit)); |
| 446 | #endif |
| 447 | (void)printf("%2d %1d %1d ", |
| 448 | total.t_rq, total.t_dw + total.t_pw, total.t_sw); |
| 449 | #define pgtok(a) ((a)*NBPG >> 10) |
| 450 | #define rate(x) (((x) + halfuptime) / uptime) /* round */ |
| 451 | (void)printf("%5ld %5ld ", |
| 452 | #ifdef __386BSD__ |
| 453 | pgtok(vm_stat.active_count), pgtok(vm_stat.free_count)); |
| 454 | #else |
| 455 | pgtok(total.t_avm), pgtok(total.t_free)); |
| 456 | #endif |
| 457 | #ifdef NEWVM |
| 458 | (void)printf("%4lu ", rate(vm_stat.faults - ostat.faults)); |
| 459 | (void)printf("%3lu ", |
| 460 | rate(vm_stat.reactivations - ostat.reactivations)); |
| 461 | (void)printf("%3lu ", rate(vm_stat.pageins - ostat.pageins)); |
| 462 | (void)printf("%3lu %3lu ", |
| 463 | rate(vm_stat.pageouts - ostat.pageouts), 0); |
| 464 | #else |
| 465 | (void)printf("%3lu %2lu ", |
| 466 | rate(sum.v_pgrec - (sum.v_xsfrec+sum.v_xifrec) - |
| 467 | (osum.v_pgrec - (osum.v_xsfrec+osum.v_xifrec))), |
| 468 | rate(sum.v_xsfrec + sum.v_xifrec - |
| 469 | osum.v_xsfrec - osum.v_xifrec)); |
| 470 | (void)printf("%3lu ", |
| 471 | rate(pgtok(sum.v_pgpgin - osum.v_pgpgin))); |
| 472 | (void)printf("%3lu %3lu ", |
| 473 | rate(pgtok(sum.v_pgpgout - osum.v_pgpgout)), |
| 474 | rate(pgtok(sum.v_dfree - osum.v_dfree))); |
| 475 | (void)printf("%3d ", pgtok(deficit)); |
| 476 | #endif |
| 477 | (void)printf("%3lu ", rate(sum.v_scan - osum.v_scan)); |
| 478 | dkstats(); |
| 479 | (void)printf("%4lu %4lu %3lu ", |
| 480 | rate(sum.v_intr - osum.v_intr), |
| 481 | rate(sum.v_syscall - osum.v_syscall), |
| 482 | rate(sum.v_swtch - osum.v_swtch)); |
| 483 | cpustats(); |
| 484 | (void)printf("\n"); |
| 485 | (void)fflush(stdout); |
| 486 | if (reps >= 0 && --reps <= 0) |
| 487 | break; |
| 488 | osum = sum; |
| 489 | ostat = vm_stat; |
| 490 | uptime = interval; |
| 491 | /* |
| 492 | * We round upward to avoid losing low-frequency events |
| 493 | * (i.e., >= 1 per interval but < 1 per second). |
| 494 | */ |
| 495 | halfuptime = (uptime + 1) / 2; |
| 496 | (void)sleep(interval); |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | printhdr() |
| 501 | { |
| 502 | register int i; |
| 503 | |
| 504 | (void)printf(" procs memory page%*s", 20, ""); |
| 505 | if (ndrives > 1) |
| 506 | (void)printf("disks %*s faults cpu\n", |
| 507 | ndrives * 3 - 6, ""); |
| 508 | else |
| 509 | (void)printf("%*s faults cpu\n", ndrives * 3, ""); |
| 510 | #ifndef NEWVM |
| 511 | (void)printf(" r b w avm fre re at pi po fr de sr "); |
| 512 | #else |
| 513 | (void)printf(" r b w avm fre flt re pi po fr sr "); |
| 514 | #endif |
| 515 | for (i = 0; i < dk_ndrive; i++) |
| 516 | if (dr_select[i]) |
| 517 | (void)printf("%c%c ", dr_name[i][0], |
| 518 | dr_name[i][strlen(dr_name[i]) - 1]); |
| 519 | (void)printf(" in sy cs us sy id\n"); |
| 520 | hdrcnt = winlines - 2; |
| 521 | } |
| 522 | |
| 523 | /* |
| 524 | * Force a header to be prepended to the next output. |
| 525 | */ |
| 526 | void |
| 527 | needhdr() |
| 528 | { |
| 529 | |
| 530 | hdrcnt = 1; |
| 531 | } |
| 532 | |
| 533 | #ifdef notdef |
| 534 | void |
| 535 | dotimes() |
| 536 | { |
| 537 | u_int pgintime, rectime; |
| 538 | |
| 539 | kread(X_REC, &rectime, sizeof(rectime)); |
| 540 | kread(X_PGIN, &pgintime, sizeof(pgintime)); |
| 541 | kread(X_SUM, &sum, sizeof(sum)); |
| 542 | (void)printf("%u reclaims, %u total time (usec)\n", |
| 543 | sum.v_pgrec, rectime); |
| 544 | (void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec); |
| 545 | (void)printf("\n"); |
| 546 | (void)printf("%u page ins, %u total time (msec)\n", |
| 547 | sum.v_pgin, pgintime / 10); |
| 548 | (void)printf("average: %8.1f msec / page in\n", |
| 549 | pgintime / (sum.v_pgin * 10.0)); |
| 550 | } |
| 551 | #endif |
| 552 | |
| 553 | pct(top, bot) |
| 554 | long top, bot; |
| 555 | { |
| 556 | if (bot == 0) |
| 557 | return(0); |
| 558 | return((top * 100) / bot); |
| 559 | } |
| 560 | |
| 561 | #define PCT(top, bot) pct((long)(top), (long)(bot)) |
| 562 | |
| 563 | #if defined(tahoe) |
| 564 | #include <machine/cpu.h> |
| 565 | #endif |
| 566 | |
| 567 | void |
| 568 | dosum() |
| 569 | { |
| 570 | struct nchstats nchstats; |
| 571 | #ifndef NEWVM |
| 572 | struct xstats xstats; |
| 573 | #endif |
| 574 | long nchtotal; |
| 575 | #if defined(tahoe) |
| 576 | struct keystats keystats; |
| 577 | #endif |
| 578 | |
| 579 | kread(X_SUM, &sum, sizeof(sum)); |
| 580 | #ifdef NEWVM |
| 581 | kread(X_VMSTAT, &vm_stat, sizeof(vm_stat)); |
| 582 | #ifdef __386BSD__ |
| 583 | fill_in_vm_stat(&vm_stat); |
| 584 | #endif |
| 585 | #else |
| 586 | (void)printf("%9u swap ins\n", sum.v_swpin); |
| 587 | (void)printf("%9u swap outs\n", sum.v_swpout); |
| 588 | (void)printf("%9u pages swapped in\n", sum.v_pswpin / CLSIZE); |
| 589 | (void)printf("%9u pages swapped out\n", sum.v_pswpout / CLSIZE); |
| 590 | (void)printf("%9u total address trans. faults taken\n", sum.v_faults); |
| 591 | (void)printf("%9u page ins\n", sum.v_pgin); |
| 592 | (void)printf("%9u page outs\n", sum.v_pgout); |
| 593 | (void)printf("%9u pages paged in\n", sum.v_pgpgin); |
| 594 | (void)printf("%9u pages paged out\n", sum.v_pgpgout); |
| 595 | (void)printf("%9u sequential process pages freed\n", sum.v_seqfree); |
| 596 | (void)printf("%9u total reclaims (%d%% fast)\n", sum.v_pgrec, |
| 597 | PCT(sum.v_fastpgrec, sum.v_pgrec)); |
| 598 | (void)printf("%9u reclaims from free list\n", sum.v_pgfrec); |
| 599 | (void)printf("%9u intransit blocking page faults\n", sum.v_intrans); |
| 600 | (void)printf("%9u zero fill pages created\n", sum.v_nzfod / CLSIZE); |
| 601 | (void)printf("%9u zero fill page faults\n", sum.v_zfod / CLSIZE); |
| 602 | (void)printf("%9u executable fill pages created\n", |
| 603 | sum.v_nexfod / CLSIZE); |
| 604 | (void)printf("%9u executable fill page faults\n", |
| 605 | sum.v_exfod / CLSIZE); |
| 606 | (void)printf("%9u swap text pages found in free list\n", |
| 607 | sum.v_xsfrec); |
| 608 | (void)printf("%9u inode text pages found in free list\n", |
| 609 | sum.v_xifrec); |
| 610 | (void)printf("%9u file fill pages created\n", sum.v_nvrfod / CLSIZE); |
| 611 | (void)printf("%9u file fill page faults\n", sum.v_vrfod / CLSIZE); |
| 612 | (void)printf("%9u pages examined by the clock daemon\n", sum.v_scan); |
| 613 | (void)printf("%9u revolutions of the clock hand\n", sum.v_rev); |
| 614 | (void)printf("%9u pages freed by the clock daemon\n", |
| 615 | sum.v_dfree / CLSIZE); |
| 616 | #endif |
| 617 | (void)printf("%9u cpu context switches\n", sum.v_swtch); |
| 618 | (void)printf("%9u device interrupts\n", sum.v_intr); |
| 619 | (void)printf("%9u software interrupts\n", sum.v_soft); |
| 620 | #ifdef vax |
| 621 | (void)printf("%9u pseudo-dma dz interrupts\n", sum.v_pdma); |
| 622 | #endif |
| 623 | (void)printf("%9u traps\n", sum.v_trap); |
| 624 | (void)printf("%9u system calls\n", sum.v_syscall); |
| 625 | #ifdef NEWVM |
| 626 | (void)printf("%9u bytes per page\n", vm_stat.pagesize); |
| 627 | (void)printf("%9u pages free\n", vm_stat.free_count); |
| 628 | (void)printf("%9u pages active\n", vm_stat.active_count); |
| 629 | (void)printf("%9u pages inactive\n", vm_stat.inactive_count); |
| 630 | (void)printf("%9u pages wired down\n", vm_stat.wire_count); |
| 631 | (void)printf("%9u zero-fill pages\n", vm_stat.zero_fill_count); |
| 632 | (void)printf("%9u pages reactivated\n", vm_stat.reactivations); |
| 633 | (void)printf("%9u pageins\n", vm_stat.pageins); |
| 634 | (void)printf("%9u pageouts\n", vm_stat.pageouts); |
| 635 | (void)printf("%9u VM faults\n", vm_stat.faults); |
| 636 | (void)printf("%9u copy-on-write faults\n", vm_stat.cow_faults); |
| 637 | (void)printf("%9u VM object cache lookups\n", vm_stat.lookups); |
| 638 | (void)printf("%9u VM object hits\n", vm_stat.hits); |
| 639 | #endif |
| 640 | |
| 641 | kread(X_NCHSTATS, &nchstats, sizeof(nchstats)); |
| 642 | nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits + |
| 643 | nchstats.ncs_badhits + nchstats.ncs_falsehits + |
| 644 | nchstats.ncs_miss + nchstats.ncs_long; |
| 645 | (void)printf("%9ld total name lookups\n", nchtotal); |
| 646 | (void)printf( |
| 647 | "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n", |
| 648 | "", PCT(nchstats.ncs_goodhits, nchtotal), |
| 649 | PCT(nchstats.ncs_neghits, nchtotal), |
| 650 | PCT(nchstats.ncs_pass2, nchtotal)); |
| 651 | (void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "", |
| 652 | PCT(nchstats.ncs_badhits, nchtotal), |
| 653 | PCT(nchstats.ncs_falsehits, nchtotal), |
| 654 | PCT(nchstats.ncs_long, nchtotal)); |
| 655 | #ifndef NEWVM |
| 656 | kread(X_XSTATS, &xstats, sizeof(xstats)); |
| 657 | (void)printf("%9lu total calls to xalloc (cache hits %d%%)\n", |
| 658 | xstats.alloc, PCT(xstats.alloc_cachehit, xstats.alloc)); |
| 659 | (void)printf("%9s sticky %lu flushed %lu unused %lu\n", "", |
| 660 | xstats.alloc_inuse, xstats.alloc_cacheflush, xstats.alloc_unused); |
| 661 | (void)printf("%9lu total calls to xfree", xstats.free); |
| 662 | (void)printf(" (sticky %lu cached %lu swapped %lu)\n", |
| 663 | xstats.free_inuse, xstats.free_cache, xstats.free_cacheswap); |
| 664 | #endif |
| 665 | #if defined(tahoe) |
| 666 | kread(X_CKEYSTATS, &keystats, sizeof(keystats)); |
| 667 | (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n", |
| 668 | keystats.ks_allocs, "code cache keys allocated", |
| 669 | PCT(keystats.ks_allocfree, keystats.ks_allocs), |
| 670 | PCT(keystats.ks_norefs, keystats.ks_allocs), |
| 671 | PCT(keystats.ks_taken, keystats.ks_allocs), |
| 672 | PCT(keystats.ks_shared, keystats.ks_allocs)); |
| 673 | kread(X_DKEYSTATS, &keystats, sizeof(keystats)); |
| 674 | (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n", |
| 675 | keystats.ks_allocs, "data cache keys allocated", |
| 676 | PCT(keystats.ks_allocfree, keystats.ks_allocs), |
| 677 | PCT(keystats.ks_norefs, keystats.ks_allocs), |
| 678 | PCT(keystats.ks_taken, keystats.ks_allocs), |
| 679 | PCT(keystats.ks_shared, keystats.ks_allocs)); |
| 680 | #endif |
| 681 | } |
| 682 | |
| 683 | #ifdef notdef |
| 684 | void |
| 685 | doforkst() |
| 686 | { |
| 687 | struct forkstat fks; |
| 688 | |
| 689 | kread(X_FORKSTAT, &fks, sizeof(struct forkstat)); |
| 690 | (void)printf("%d forks, %d pages, average %.2f\n", |
| 691 | fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork); |
| 692 | (void)printf("%d vforks, %d pages, average %.2f\n", |
| 693 | fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork); |
| 694 | } |
| 695 | #endif |
| 696 | |
| 697 | void |
| 698 | dkstats() |
| 699 | { |
| 700 | register int dn, state; |
| 701 | double etime; |
| 702 | long tmp; |
| 703 | |
| 704 | for (dn = 0; dn < dk_ndrive; ++dn) { |
| 705 | tmp = cur.xfer[dn]; |
| 706 | cur.xfer[dn] -= last.xfer[dn]; |
| 707 | last.xfer[dn] = tmp; |
| 708 | } |
| 709 | etime = 0; |
| 710 | for (state = 0; state < CPUSTATES; ++state) { |
| 711 | tmp = cur.time[state]; |
| 712 | cur.time[state] -= last.time[state]; |
| 713 | last.time[state] = tmp; |
| 714 | etime += cur.time[state]; |
| 715 | } |
| 716 | if (etime == 0) |
| 717 | etime = 1; |
| 718 | etime /= hz; |
| 719 | for (dn = 0; dn < dk_ndrive; ++dn) { |
| 720 | if (!dr_select[dn]) |
| 721 | continue; |
| 722 | (void)printf("%2.0f ", cur.xfer[dn] / etime); |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | void |
| 727 | cpustats() |
| 728 | { |
| 729 | register int state; |
| 730 | double pct, total; |
| 731 | |
| 732 | total = 0; |
| 733 | for (state = 0; state < CPUSTATES; ++state) |
| 734 | total += cur.time[state]; |
| 735 | if (total) |
| 736 | pct = 100 / total; |
| 737 | else |
| 738 | pct = 0; |
| 739 | (void)printf("%2.0f ", /* user + nice */ |
| 740 | (cur.time[0] + cur.time[1]) * pct); |
| 741 | (void)printf("%2.0f ", cur.time[2] * pct); /* system */ |
| 742 | (void)printf("%2.0f", cur.time[3] * pct); /* idle */ |
| 743 | } |
| 744 | |
| 745 | void |
| 746 | dointr() |
| 747 | { |
| 748 | register unsigned long *intrcnt, inttotal, uptime; |
| 749 | register int nintr, inamlen; |
| 750 | register char *intrname; |
| 751 | |
| 752 | uptime = getuptime(); |
| 753 | nintr = nl[X_EINTRCNT].n_value - nl[X_INTRCNT].n_value; |
| 754 | inamlen = nl[X_EINTRNAMES].n_value - nl[X_INTRNAMES].n_value; |
| 755 | intrcnt = malloc((size_t)nintr); |
| 756 | intrname = malloc((size_t)inamlen); |
| 757 | if (intrcnt == NULL || intrname == NULL) { |
| 758 | (void)fprintf(stderr, "vmstat: %s.\n", strerror(errno)); |
| 759 | exit(1); |
| 760 | } |
| 761 | kread(X_INTRCNT, intrcnt, (size_t)nintr); |
| 762 | kread(X_INTRNAMES, intrname, (size_t)inamlen); |
| 763 | (void)printf("%-12s %10s %8s\n", "interrupt", "count", "rate"); |
| 764 | inttotal = 0; |
| 765 | nintr /= sizeof(long); |
| 766 | while (--nintr >= 0) { |
| 767 | if (*intrcnt) |
| 768 | (void)printf("%-12s %10lu %8lu\n", intrname, |
| 769 | *intrcnt, *intrcnt / uptime); |
| 770 | intrname += strlen(intrname) + 1; |
| 771 | inttotal += *intrcnt++; |
| 772 | } |
| 773 | (void)printf("%-12s %10lu %8lu\n", "Total", inttotal, inttotal / uptime); |
| 774 | } |
| 775 | |
| 776 | /* |
| 777 | * These names are defined in <sys/malloc.h>. |
| 778 | */ |
| 779 | char *kmemnames[] = INITKMEMNAMES; |
| 780 | |
| 781 | void |
| 782 | domem() |
| 783 | { |
| 784 | register struct kmembuckets *kp; |
| 785 | register struct kmemstats *ks; |
| 786 | register int i; |
| 787 | int size; |
| 788 | long totuse = 0, totfree = 0, totreq = 0; |
| 789 | struct kmemstats kmemstats[M_LAST]; |
| 790 | struct kmembuckets buckets[MINBUCKET + 16]; |
| 791 | |
| 792 | kread(X_KMEMBUCKETS, buckets, sizeof(buckets)); |
| 793 | (void)printf("Memory statistics by bucket size\n"); |
| 794 | (void)printf( |
| 795 | " Size In Use Free Requests HighWater Couldfree\n"); |
| 796 | for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) { |
| 797 | if (kp->kb_calls == 0) |
| 798 | continue; |
| 799 | size = 1 << i; |
| 800 | (void)printf("%8d %8ld %6ld %10ld %7ld %10ld\n", size, |
| 801 | kp->kb_total - kp->kb_totalfree, |
| 802 | kp->kb_totalfree, kp->kb_calls, |
| 803 | kp->kb_highwat, kp->kb_couldfree); |
| 804 | totfree += size * kp->kb_totalfree; |
| 805 | } |
| 806 | |
| 807 | kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); |
| 808 | (void)printf("\nMemory statistics by type\n"); |
| 809 | (void)printf( |
| 810 | " Type In Use MemUse HighUse Limit Requests TypeLimit KernLimit\n"); |
| 811 | for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { |
| 812 | if (ks->ks_calls == 0) |
| 813 | continue; |
| 814 | (void)printf("%11s %7ld %7ldK %8ldK %5ldK %8ld %6u %9u\n", |
| 815 | kmemnames[i] ? kmemnames[i] : "undefined", |
| 816 | ks->ks_inuse, (ks->ks_memuse + 1023) / 1024, |
| 817 | (ks->ks_maxused + 1023) / 1024, |
| 818 | (ks->ks_limit + 1023) / 1024, ks->ks_calls, |
| 819 | ks->ks_limblocks, ks->ks_mapblocks); |
| 820 | totuse += ks->ks_memuse; |
| 821 | totreq += ks->ks_calls; |
| 822 | } |
| 823 | (void)printf("\nMemory Totals: In Use Free Requests\n"); |
| 824 | (void)printf(" %7ldK %6ldK %8ld\n", |
| 825 | (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq); |
| 826 | } |
| 827 | |
| 828 | /* |
| 829 | * kread reads something from the kernel, given its nlist index. |
| 830 | */ |
| 831 | void |
| 832 | kread(nlx, addr, size) |
| 833 | int nlx; |
| 834 | void *addr; |
| 835 | size_t size; |
| 836 | { |
| 837 | char *sym; |
| 838 | |
| 839 | if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0) { |
| 840 | sym = nl[nlx].n_name; |
| 841 | if (*sym == '_') |
| 842 | ++sym; |
| 843 | (void)fprintf(stderr, |
| 844 | "vmstat: %s: symbol %s not defined\n", vmunix, sym); |
| 845 | exit(1); |
| 846 | } |
| 847 | if (kvm_read((void *)nl[nlx].n_value, addr, size) != size) { |
| 848 | sym = nl[nlx].n_name; |
| 849 | if (*sym == '_') |
| 850 | ++sym; |
| 851 | (void)fprintf(stderr, "vmstat: %s: %s\n", sym, kvm_geterr()); |
| 852 | exit(1); |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | void |
| 857 | usage() |
| 858 | { |
| 859 | (void)fprintf(stderr, |
| 860 | #ifndef NEWVM |
| 861 | "usage: vmstat [-fimst] [-c count] [-M core] \ |
| 862 | [-N system] [-w wait] [disks]\n"); |
| 863 | #else |
| 864 | "usage: vmstat [-ims] [-c count] [-M core] \ |
| 865 | [-N system] [-w wait] [disks]\n"); |
| 866 | #endif |
| 867 | exit(1); |
| 868 | } |