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