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