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