vmstat.c revision 1.233
1/* $NetBSD: vmstat.c,v 1.233 2020/01/06 11:24:30 ad Exp $ */ 2 3/*- 4 * Copyright (c) 1998, 2000, 2001, 2007, 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation by: 8 * - Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * - Simon Burge and Luke Mewburn of Wasabi Systems, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34/* 35 * Copyright (c) 1980, 1986, 1991, 1993 36 * The Regents of the University of California. All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63#include <sys/cdefs.h> 64#ifndef lint 65__COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1991, 1993\ 66 The Regents of the University of California. All rights reserved."); 67#endif /* not lint */ 68 69#ifndef lint 70#if 0 71static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/95"; 72#else 73__RCSID("$NetBSD: vmstat.c,v 1.233 2020/01/06 11:24:30 ad Exp $"); 74#endif 75#endif /* not lint */ 76 77#define __POOL_EXPOSE 78#define __NAMECACHE_PRIVATE 79 80#include <sys/param.h> 81#include <sys/types.h> 82#include <sys/mount.h> 83#include <sys/uio.h> 84 85#include <sys/buf.h> 86#include <sys/evcnt.h> 87#include <sys/ioctl.h> 88#include <sys/malloc.h> 89#include <sys/mallocvar.h> 90#include <sys/namei.h> 91#include <sys/pool.h> 92#include <sys/proc.h> 93#include <sys/sched.h> 94#include <sys/socket.h> 95#include <sys/sysctl.h> 96#include <sys/time.h> 97#include <sys/queue.h> 98#include <sys/kernhist.h> 99#include <sys/vnode.h> 100#include <sys/vnode_impl.h> 101 102#include <uvm/uvm_extern.h> 103#include <uvm/uvm_stat.h> 104 105#include <net/if.h> 106#include <netinet/in.h> 107#include <netinet/in_var.h> 108 109#include <ufs/ufs/inode.h> 110 111#include <nfs/rpcv2.h> 112#include <nfs/nfsproto.h> 113#include <nfs/nfsnode.h> 114 115#include <ctype.h> 116#include <err.h> 117#include <errno.h> 118#include <fcntl.h> 119#include <kvm.h> 120#include <limits.h> 121#include <nlist.h> 122#undef n_hash 123#include <paths.h> 124#include <signal.h> 125#include <stdio.h> 126#include <stddef.h> 127#include <stdlib.h> 128#include <string.h> 129#include <time.h> 130#include <unistd.h> 131#include <util.h> 132 133#include "drvstats.h" 134 135/* 136 * All this mess will go away once everything is converted. 137 */ 138#ifdef __HAVE_CPU_DATA_FIRST 139 140# include <sys/cpu_data.h> 141struct cpu_info { 142 struct cpu_data ci_data; 143}; 144#else 145# include <sys/cpu.h> 146#endif 147 148/* 149 * General namelist 150 */ 151struct nlist namelist[] = 152{ 153#define X_HZ 0 154 { .n_name = "_hz" }, 155#define X_STATHZ 1 156 { .n_name = "_stathz" }, 157#define X_NCHSTATS 2 158 { .n_name = "_nchstats" }, 159#define X_ALLEVENTS 3 160 { .n_name = "_allevents" }, 161#define X_POOLHEAD 4 162 { .n_name = "_pool_head" }, 163#define X_UVMEXP 5 164 { .n_name = "_uvmexp" }, 165#define X_CPU_INFOS 6 166 { .n_name = "_cpu_infos" }, 167#define X_NL_SIZE 7 168 { .n_name = NULL }, 169}; 170 171/* 172 * Namelist for time data. 173 */ 174struct nlist timenl[] = 175{ 176#define X_TIMEBASEBIN 0 177 { .n_name = "_timebasebin" }, 178#define X_TIME_SECOND 1 179 { .n_name = "_time_second" }, 180#define X_TIME 2 181 { .n_name = "_time" }, 182#define X_TIMENL_SIZE 3 183 { .n_name = NULL }, 184}; 185 186/* 187 * Namelist for pre-evcnt interrupt counters. 188 */ 189struct nlist intrnl[] = 190{ 191#define X_INTRNAMES 0 192 { .n_name = "_intrnames" }, 193#define X_EINTRNAMES 1 194 { .n_name = "_eintrnames" }, 195#define X_INTRCNT 2 196 { .n_name = "_intrcnt" }, 197#define X_EINTRCNT 3 198 { .n_name = "_eintrcnt" }, 199#define X_INTRNL_SIZE 4 200 { .n_name = NULL }, 201}; 202 203 204/* 205 * Namelist for hash statistics 206 */ 207struct nlist hashnl[] = 208{ 209#define X_NFSNODE 0 210 { .n_name = "_nfsnodehash" }, 211#define X_NFSNODETBL 1 212 { .n_name = "_nfsnodehashtbl" }, 213#define X_IHASH 2 214 { .n_name = "_ihash" }, 215#define X_IHASHTBL 3 216 { .n_name = "_ihashtbl" }, 217#define X_BUFHASH 4 218 { .n_name = "_bufhash" }, 219#define X_BUFHASHTBL 5 220 { .n_name = "_bufhashtbl" }, 221#define X_UIHASH 6 222 { .n_name = "_uihash" }, 223#define X_UIHASHTBL 7 224 { .n_name = "_uihashtbl" }, 225#define X_IFADDRHASH 8 226 { .n_name = "_in_ifaddrhash" }, 227#define X_IFADDRHASHTBL 9 228 { .n_name = "_in_ifaddrhashtbl" }, 229#define X_NCHASH 10 230 { .n_name = "_nchash" }, 231#define X_NCHASHTBL 11 232 { .n_name = "_nchashtbl" }, 233#define X_NCVCACHEHASH 12 234 { .n_name = "_vcache_hashmask" }, 235#define X_NCVCACHETBL 13 236 { .n_name = "_vcache_hashtab" }, 237#define X_HASHNL_SIZE 14 /* must be last */ 238 { .n_name = NULL }, 239}; 240 241/* 242 * Namelist for kernel histories 243 */ 244struct nlist histnl[] = 245{ 246 { .n_name = "_kern_histories" }, 247#define X_KERN_HISTORIES 0 248 { .n_name = NULL }, 249}; 250 251 252#define KILO 1024 253 254struct cpu_counter { 255 uint64_t nintr; 256 uint64_t nsyscall; 257 uint64_t nswtch; 258 uint64_t nfault; 259 uint64_t ntrap; 260 uint64_t nsoft; 261} cpucounter, ocpucounter; 262 263struct uvmexp_sysctl uvmexp, ouvmexp; 264int ndrives; 265 266int winlines = 20; 267 268kvm_t *kd; 269 270 271#define FORKSTAT 0x001 272#define INTRSTAT 0x002 273#define MEMSTAT 0x004 274#define SUMSTAT 0x008 275#define EVCNTSTAT 0x010 276#define VMSTAT 0x020 277#define HISTLIST 0x040 278#define HISTDUMP 0x080 279#define HASHSTAT 0x100 280#define HASHLIST 0x200 281#define VMTOTAL 0x400 282#define POOLCACHESTAT 0x800 283 284/* 285 * Print single word. `ovflow' is number of characters didn't fit 286 * on the last word. `fmt' is a format string to print this word. 287 * It must contain asterisk for field width. `width' is a width 288 * occupied by this word. `fixed' is a number of constant chars in 289 * `fmt'. `val' is a value to be printed using format string `fmt'. 290 */ 291#define PRWORD(ovflw, fmt, width, fixed, val) do { \ 292 (ovflw) += printf((fmt), \ 293 (width) - (fixed) - (ovflw) > 0 ? \ 294 (width) - (fixed) - (ovflw) : 0, \ 295 (val)) - (width); \ 296 if ((ovflw) < 0) \ 297 (ovflw) = 0; \ 298} while (/* CONSTCOND */0) 299 300void cpustats(int *); 301void cpucounters(struct cpu_counter *); 302void deref_kptr(const void *, void *, size_t, const char *); 303void drvstats(int *); 304void doevcnt(int verbose, int type); 305void dohashstat(int, int, const char *); 306void dointr(int verbose); 307void dopool(int, int); 308void dopoolcache(int); 309void dosum(void); 310void dovmstat(struct timespec *, int); 311void print_total_hdr(void); 312void dovmtotal(struct timespec *, int); 313void kread(struct nlist *, int, void *, size_t); 314int kreadc(struct nlist *, int, void *, size_t); 315void needhdr(int); 316void getnlist(int); 317long getuptime(void); 318void printhdr(void); 319long pct(u_long, u_long); 320__dead static void usage(void); 321void doforkst(void); 322 323void hist_traverse(int, const char *); 324void hist_dodump(struct kern_history *); 325void hist_traverse_sysctl(int, const char *); 326void hist_dodump_sysctl(int[], unsigned int); 327 328char **choosedrives(char **); 329 330/* Namelist and memory file names. */ 331char *nlistf, *memf; 332 333/* allow old usage [vmstat 1] */ 334#define BACKWARD_COMPATIBILITY 335 336static const int clockrate_mib[] = { CTL_KERN, KERN_CLOCKRATE }; 337static const int vmmeter_mib[] = { CTL_VM, VM_METER }; 338static const int uvmexp2_mib[] = { CTL_VM, VM_UVMEXP2 }; 339static const int boottime_mib[] = { CTL_KERN, KERN_BOOTTIME }; 340static char kvm_errbuf[_POSIX2_LINE_MAX]; 341 342int 343main(int argc, char *argv[]) 344{ 345 int c, todo, verbose, wide; 346 struct timespec interval; 347 int reps; 348 gid_t egid = getegid(); 349 const char *histname, *hashname; 350 351 histname = hashname = NULL; 352 (void)setegid(getgid()); 353 memf = nlistf = NULL; 354 reps = todo = verbose = wide = 0; 355 interval.tv_sec = 0; 356 interval.tv_nsec = 0; 357 while ((c = getopt(argc, argv, "Cc:efh:HilLM:mN:stu:UvWw:")) != -1) { 358 switch (c) { 359 case 'c': 360 reps = atoi(optarg); 361 break; 362 case 'C': 363 todo |= POOLCACHESTAT; 364 break; 365 case 'e': 366 todo |= EVCNTSTAT; 367 break; 368 case 'f': 369 todo |= FORKSTAT; 370 break; 371 case 'h': 372 hashname = optarg; 373 /* FALLTHROUGH */ 374 case 'H': 375 todo |= HASHSTAT; 376 break; 377 case 'i': 378 todo |= INTRSTAT; 379 break; 380 case 'l': 381 todo |= HISTLIST; 382 break; 383 case 'L': 384 todo |= HASHLIST; 385 break; 386 case 'M': 387 memf = optarg; 388 break; 389 case 'm': 390 todo |= MEMSTAT; 391 break; 392 case 'N': 393 nlistf = optarg; 394 break; 395 case 's': 396 todo |= SUMSTAT; 397 break; 398 case 't': 399 todo |= VMTOTAL; 400 break; 401 case 'u': 402 histname = optarg; 403 /* FALLTHROUGH */ 404 case 'U': 405 todo |= HISTDUMP; 406 break; 407 case 'v': 408 verbose++; 409 break; 410 case 'W': 411 wide++; 412 break; 413 case 'w': 414 interval.tv_sec = atol(optarg); 415 break; 416 case '?': 417 default: 418 usage(); 419 } 420 } 421 argc -= optind; 422 argv += optind; 423 424 if (todo == 0) 425 todo = VMSTAT; 426 427 /* 428 * Discard setgid privileges. If not the running kernel, we toss 429 * them away totally so that bad guys can't print interesting stuff 430 * from kernel memory, otherwise switch back to kmem for the 431 * duration of the kvm_openfiles() call. 432 */ 433 if (nlistf != NULL || memf != NULL) 434 (void)setgid(getgid()); 435 else 436 (void)setegid(egid); 437 438 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, kvm_errbuf); 439 if (kd == NULL) { 440 if (nlistf != NULL || memf != NULL) { 441 errx(1, "kvm_openfiles: %s", kvm_errbuf); 442 } 443 } 444 445 if (nlistf == NULL && memf == NULL) 446 (void)setgid(getgid()); 447 448 449 if (todo & VMSTAT) { 450 struct winsize winsize; 451 452 (void)drvinit(0);/* Initialize disk stats, no disks selected. */ 453 454 (void)setgid(getgid()); /* don't need privs anymore */ 455 456 argv = choosedrives(argv); /* Select disks. */ 457 winsize.ws_row = 0; 458 (void)ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 459 if (winsize.ws_row > 0) 460 winlines = winsize.ws_row; 461 462 } 463 464#ifdef BACKWARD_COMPATIBILITY 465 if (*argv) { 466 interval.tv_sec = atol(*argv); 467 if (*++argv) 468 reps = atoi(*argv); 469 } 470#endif 471 472 if (interval.tv_sec) { 473 if (!reps) 474 reps = -1; 475 } else if (reps) 476 interval.tv_sec = 1; 477 478 479 getnlist(todo); 480 /* 481 * Statistics dumping is incompatible with the default 482 * VMSTAT/dovmstat() output. So perform the interval/reps handling 483 * for it here. 484 */ 485 if ((todo & (VMSTAT|VMTOTAL)) == 0) { 486 for (;;) { 487 if (todo & (HISTLIST|HISTDUMP)) { 488 if ((todo & (HISTLIST|HISTDUMP)) == 489 (HISTLIST|HISTDUMP)) 490 errx(1, "you may list or dump," 491 " but not both!"); 492 if (memf != NULL) 493 hist_traverse(todo, histname); 494 else 495 hist_traverse_sysctl(todo, histname); 496 (void)putchar('\n'); 497 } 498 if (todo & FORKSTAT) { 499 doforkst(); 500 (void)putchar('\n'); 501 } 502 if (todo & MEMSTAT) { 503 dopool(verbose, wide); 504 (void)putchar('\n'); 505 } 506 if (todo & POOLCACHESTAT) { 507 dopoolcache(verbose); 508 (void)putchar('\n'); 509 } 510 if (todo & SUMSTAT) { 511 dosum(); 512 (void)putchar('\n'); 513 } 514 if (todo & INTRSTAT) { 515 dointr(verbose); 516 (void)putchar('\n'); 517 } 518 if (todo & EVCNTSTAT) { 519 doevcnt(verbose, EVCNT_TYPE_ANY); 520 (void)putchar('\n'); 521 } 522 if (todo & (HASHLIST|HASHSTAT)) { 523 if ((todo & (HASHLIST|HASHSTAT)) == 524 (HASHLIST|HASHSTAT)) 525 errx(1, "you may list or display," 526 " but not both!"); 527 dohashstat(verbose, todo, hashname); 528 (void)putchar('\n'); 529 } 530 531 fflush(stdout); 532 if (reps >= 0 && --reps <=0) 533 break; 534 (void)nanosleep(&interval, NULL); 535 } 536 } else { 537 if ((todo & (VMSTAT|VMTOTAL)) == (VMSTAT|VMTOTAL)) { 538 errx(1, "you may not both do vmstat and vmtotal"); 539 } 540 if (todo & VMSTAT) 541 dovmstat(&interval, reps); 542 if (todo & VMTOTAL) 543 dovmtotal(&interval, reps); 544 } 545 return 0; 546} 547 548void 549getnlist(int todo) 550{ 551 static int namelist_done = 0; 552 static int done = 0; 553 int c; 554 size_t i; 555 556 if (kd == NULL) 557 errx(1, "kvm_openfiles: %s", kvm_errbuf); 558 559 if (!namelist_done) { 560 namelist_done = 1; 561 if ((c = kvm_nlist(kd, namelist)) != 0) { 562 int doexit = 0; 563 if (c == -1) 564 errx(1, "kvm_nlist: %s %s", 565 "namelist", kvm_geterr(kd)); 566 for (i = 0; i < __arraycount(namelist)-1; i++) 567 if (namelist[i].n_type == 0) { 568 if (doexit++ == 0) 569 (void)fprintf(stderr, 570 "%s: undefined symbols:", 571 getprogname()); 572 (void)fprintf(stderr, " %s", 573 namelist[i].n_name); 574 } 575 if (doexit) { 576 (void)fputc('\n', stderr); 577 exit(1); 578 } 579 } 580 } 581 if ((todo & (VMSTAT|INTRSTAT)) && !(done & (VMSTAT))) { 582 done |= VMSTAT; 583 if ((c = kvm_nlist(kd, timenl)) == -1 || c == X_TIMENL_SIZE) 584 errx(1, "kvm_nlist: %s %s", "timenl", kvm_geterr(kd)); 585 } 586 if ((todo & (SUMSTAT|INTRSTAT)) && !(done & (SUMSTAT|INTRSTAT))) { 587 done |= SUMSTAT|INTRSTAT; 588 (void) kvm_nlist(kd, intrnl); 589 } 590 if ((todo & (HASHLIST|HASHSTAT)) && !(done & (HASHLIST|HASHSTAT))) { 591 done |= HASHLIST|HASHSTAT; 592 if ((c = kvm_nlist(kd, hashnl)) == -1 || c == X_HASHNL_SIZE) 593 errx(1, "kvm_nlist: %s %s", "hashnl", kvm_geterr(kd)); 594 } 595 if ((todo & (HISTLIST|HISTDUMP)) && !(done & (HISTLIST|HISTDUMP))) { 596 done |= HISTLIST|HISTDUMP; 597 if (kvm_nlist(kd, histnl) == -1) 598 errx(1, "kvm_nlist: %s %s", "histnl", kvm_geterr(kd)); 599 } 600} 601 602char ** 603choosedrives(char **argv) 604{ 605 size_t i; 606 607 /* 608 * Choose drives to be displayed. Priority goes to (in order) drives 609 * supplied as arguments, default drives. If everything isn't filled 610 * in and there are drives not taken care of, display the first few 611 * that fit. 612 */ 613#define BACKWARD_COMPATIBILITY 614 for (ndrives = 0; *argv; ++argv) { 615#ifdef BACKWARD_COMPATIBILITY 616 if (isdigit((unsigned char)**argv)) 617 break; 618#endif 619 for (i = 0; i < ndrive; i++) { 620 if (strcmp(dr_name[i], *argv)) 621 continue; 622 drv_select[i] = 1; 623 ++ndrives; 624 break; 625 } 626 } 627 for (i = 0; i < ndrive && ndrives < 2; i++) { 628 if (drv_select[i]) 629 continue; 630 drv_select[i] = 1; 631 ++ndrives; 632 } 633 634 return (argv); 635} 636 637long 638getuptime(void) 639{ 640 static struct timespec boottime; 641 struct timespec now; 642 time_t uptime, nowsec; 643 644 if (memf == NULL) { 645 if (boottime.tv_sec == 0) { 646 size_t buflen = sizeof(boottime); 647 if (sysctl(boottime_mib, __arraycount(boottime_mib), 648 &boottime, &buflen, NULL, 0) == -1) 649 warn("Can't get boottime"); 650 } 651 clock_gettime(CLOCK_REALTIME, &now); 652 } else { 653 if (boottime.tv_sec == 0) { 654 struct bintime bt; 655 656 kread(timenl, X_TIMEBASEBIN, &bt, sizeof(bt)); 657 bintime2timespec(&bt, &boottime); 658 } 659 if (kreadc(timenl, X_TIME_SECOND, &nowsec, sizeof(nowsec))) { 660 /* 661 * XXX this assignment dance can be removed once 662 * timeval tv_sec is SUS mandated time_t 663 */ 664 now.tv_sec = nowsec; 665 now.tv_nsec = 0; 666 } else { 667 kread(timenl, X_TIME, &now, sizeof(now)); 668 } 669 } 670 uptime = now.tv_sec - boottime.tv_sec; 671 if (uptime <= 0 || uptime > 60*60*24*365*10) 672 errx(1, "time makes no sense; namelist must be wrong."); 673 return (uptime); 674} 675 676int hz, hdrcnt; 677 678void 679print_total_hdr(void) 680{ 681 682 (void)printf("procs memory\n"); 683 (void)printf("ru dw pw sl"); 684 (void)printf(" total-v active-v active-r"); 685 (void)printf(" vm-sh avm-sh rm-sh arm-sh free\n"); 686 hdrcnt = winlines - 2; 687} 688 689void 690dovmtotal(struct timespec *interval, int reps) 691{ 692 struct vmtotal total; 693 size_t size; 694 695 (void)signal(SIGCONT, needhdr); 696 697 for (hdrcnt = 1;;) { 698 if (!--hdrcnt) 699 print_total_hdr(); 700 if (memf != NULL) { 701 warnx("Unable to get vmtotals from crash dump."); 702 (void)memset(&total, 0, sizeof(total)); 703 } else { 704 size = sizeof(total); 705 if (sysctl(vmmeter_mib, __arraycount(vmmeter_mib), 706 &total, &size, NULL, 0) == -1) { 707 warn("Can't get vmtotals"); 708 (void)memset(&total, 0, sizeof(total)); 709 } 710 } 711 (void)printf("%2d ", total.t_rq); 712 (void)printf("%2d ", total.t_dw); 713 (void)printf("%2d ", total.t_pw); 714 (void)printf("%2d ", total.t_sl); 715 716 (void)printf("%9d ", total.t_vm); 717 (void)printf("%9d ", total.t_avm); 718 (void)printf("%9d ", total.t_arm); 719 (void)printf("%5d ", total.t_vmshr); 720 (void)printf("%6d ", total.t_avmshr); 721 (void)printf("%5d ", total.t_rmshr); 722 (void)printf("%6d ", total.t_armshr); 723 (void)printf("%5d", total.t_free); 724 725 (void)putchar('\n'); 726 727 (void)fflush(stdout); 728 if (reps >= 0 && --reps <= 0) 729 break; 730 731 (void)nanosleep(interval, NULL); 732 } 733} 734 735void 736dovmstat(struct timespec *interval, int reps) 737{ 738 struct vmtotal total; 739 time_t uptime, halfuptime; 740 size_t size; 741 int pagesize = getpagesize(); 742 int ovflw; 743 744 uptime = getuptime(); 745 halfuptime = uptime / 2; 746 (void)signal(SIGCONT, needhdr); 747 748 if (memf != NULL) { 749 if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0) 750 kread(namelist, X_STATHZ, &hz, sizeof(hz)); 751 if (!hz) 752 kread(namelist, X_HZ, &hz, sizeof(hz)); 753 } else { 754 struct clockinfo clockinfo; 755 size = sizeof(clockinfo); 756 if (sysctl(clockrate_mib, 2, &clockinfo, &size, NULL, 0) == -1) 757 err(1, "sysctl kern.clockrate failed"); 758 hz = clockinfo.stathz; 759 if (!hz) 760 hz = clockinfo.hz; 761 } 762 763 for (hdrcnt = 1;;) { 764 if (!--hdrcnt) 765 printhdr(); 766 /* Read new disk statistics */ 767 cpureadstats(); 768 drvreadstats(); 769 tkreadstats(); 770 if (memf != NULL) { 771 struct uvmexp uvmexp_kernel; 772 /* 773 * XXX Can't do this if we're reading a crash 774 * XXX dump because they're lazily-calculated. 775 */ 776 warnx("Unable to get vmtotals from crash dump."); 777 (void)memset(&total, 0, sizeof(total)); 778 kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel)); 779#define COPY(field) uvmexp.field = uvmexp_kernel.field 780 COPY(pdreact); 781 COPY(pageins); 782 COPY(pgswapout); 783 COPY(pdfreed); 784 COPY(pdscans); 785#undef COPY 786 } else { 787 size = sizeof(total); 788 if (sysctl(vmmeter_mib, __arraycount(vmmeter_mib), 789 &total, &size, NULL, 0) == -1) { 790 warn("Can't get vmtotals"); 791 (void)memset(&total, 0, sizeof(total)); 792 } 793 size = sizeof(uvmexp); 794 if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp, 795 &size, NULL, 0) == -1) 796 warn("sysctl vm.uvmexp2 failed"); 797 } 798 cpucounters(&cpucounter); 799 ovflw = 0; 800 PRWORD(ovflw, " %*d", 2, 1, total.t_rq - 1); 801 PRWORD(ovflw, " %*d", 2, 1, total.t_dw + total.t_pw); 802#define pgtok(a) (long)((a) * ((uint32_t)pagesize >> 10)) 803#define rate(x) (u_long)(((x) + halfuptime) / uptime) /* round */ 804 PRWORD(ovflw, " %*ld", 9, 1, pgtok(total.t_avm)); 805 PRWORD(ovflw, " %*ld", 7, 1, pgtok(total.t_free)); 806 PRWORD(ovflw, " %*ld", 5, 1, 807 rate(cpucounter.nfault - ocpucounter.nfault)); 808 PRWORD(ovflw, " %*ld", 4, 1, 809 rate(uvmexp.pdreact - ouvmexp.pdreact)); 810 PRWORD(ovflw, " %*ld", 4, 1, 811 rate(uvmexp.pageins - ouvmexp.pageins)); 812 PRWORD(ovflw, " %*ld", 5, 1, 813 rate(uvmexp.pgswapout - ouvmexp.pgswapout)); 814 PRWORD(ovflw, " %*ld", 5, 1, 815 rate(uvmexp.pdfreed - ouvmexp.pdfreed)); 816 PRWORD(ovflw, " %*ld", 6, 2, 817 rate(uvmexp.pdscans - ouvmexp.pdscans)); 818 drvstats(&ovflw); 819 PRWORD(ovflw, " %*ld", 5, 1, 820 rate(cpucounter.nintr - ocpucounter.nintr)); 821 PRWORD(ovflw, " %*ld", 5, 1, 822 rate(cpucounter.nsyscall - ocpucounter.nsyscall)); 823 PRWORD(ovflw, " %*ld", 4, 1, 824 rate(cpucounter.nswtch - ocpucounter.nswtch)); 825 cpustats(&ovflw); 826 (void)putchar('\n'); 827 (void)fflush(stdout); 828 if (reps >= 0 && --reps <= 0) 829 break; 830 ouvmexp = uvmexp; 831 ocpucounter = cpucounter; 832 uptime = interval->tv_sec; 833 /* 834 * We round upward to avoid losing low-frequency events 835 * (i.e., >= 1 per interval but < 1 per second). 836 */ 837 halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2; 838 (void)nanosleep(interval, NULL); 839 } 840} 841 842void 843printhdr(void) 844{ 845 size_t i; 846 847 (void)printf(" procs memory page%*s", 23, ""); 848 if (ndrives > 0) 849 (void)printf("%s %*sfaults cpu\n", 850 ((ndrives > 1) ? "disks" : "disk"), 851 ((ndrives > 1) ? ndrives * 3 - 4 : 0), ""); 852 else 853 (void)printf("%*s faults cpu\n", 854 ndrives * 3, ""); 855 856 (void)printf(" r b avm fre flt re pi po fr sr "); 857 for (i = 0; i < ndrive; i++) 858 if (drv_select[i]) 859 (void)printf("%c%c ", dr_name[i][0], 860 dr_name[i][strlen(dr_name[i]) - 1]); 861 (void)printf(" in sy cs us sy id\n"); 862 hdrcnt = winlines - 2; 863} 864 865/* 866 * Force a header to be prepended to the next output. 867 */ 868void 869/*ARGSUSED*/ 870needhdr(int dummy) 871{ 872 873 hdrcnt = 1; 874} 875 876long 877pct(u_long top, u_long bot) 878{ 879 long ans; 880 881 if (bot == 0) 882 return (0); 883 ans = (long)((quad_t)top * 100 / bot); 884 return (ans); 885} 886 887#define PCT(top, bot) (int)pct((u_long)(top), (u_long)(bot)) 888 889void 890dosum(void) 891{ 892 struct nchstats nch_stats; 893 uint64_t nchtotal; 894 size_t ssize; 895 int active_kernel; 896 struct cpu_counter cc; 897 898 /* 899 * The "active" and "inactive" variables 900 * are now estimated by the kernel and sadly 901 * can not easily be dug out of a crash dump. 902 */ 903 ssize = sizeof(uvmexp); 904 memset(&uvmexp, 0, ssize); 905 active_kernel = (memf == NULL); 906 if (active_kernel) { 907 /* only on active kernel */ 908 if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp, 909 &ssize, NULL, 0) == -1) 910 warn("sysctl vm.uvmexp2 failed"); 911 } else { 912 struct uvmexp uvmexp_kernel; 913 struct pool pool, *pp = &pool; 914 struct pool_allocator pa; 915 TAILQ_HEAD(,pool) pool_head; 916 void *addr; 917 uint64_t bytes; 918 919 kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel)); 920#define COPY(field) uvmexp.field = uvmexp_kernel.field 921 COPY(pagesize); 922 COPY(ncolors); 923 COPY(npages); 924 COPY(free); 925 COPY(paging); 926 COPY(wired); 927 COPY(zeropages); 928 COPY(reserve_pagedaemon); 929 COPY(reserve_kernel); 930 COPY(anonpages); 931 COPY(filepages); 932 COPY(execpages); 933 COPY(freemin); 934 COPY(freetarg); 935 COPY(wiredmax); 936 COPY(nswapdev); 937 COPY(swpages); 938 COPY(swpginuse); 939 COPY(nswget); 940 COPY(pageins); 941 COPY(pdpageouts); 942 COPY(pgswapin); 943 COPY(pgswapout); 944 COPY(forks); 945 COPY(forks_ppwait); 946 COPY(forks_sharevm); 947 COPY(pga_zerohit); 948 COPY(pga_zeromiss); 949 COPY(zeroaborts); 950 COPY(colorhit); 951 COPY(colormiss); 952 COPY(cpuhit); 953 COPY(cpumiss); 954 COPY(fltnoram); 955 COPY(fltnoanon); 956 COPY(fltpgwait); 957 COPY(fltpgrele); 958 COPY(fltrelck); 959 COPY(fltrelckok); 960 COPY(fltanget); 961 COPY(fltanretry); 962 COPY(fltamcopy); 963 COPY(fltamcopy); 964 COPY(fltnomap); 965 COPY(fltlget); 966 COPY(fltget); 967 COPY(flt_anon); 968 COPY(flt_acow); 969 COPY(flt_obj); 970 COPY(flt_prcopy); 971 COPY(flt_przero); 972 COPY(pdwoke); 973 COPY(pdrevs); 974 COPY(pdfreed); 975 COPY(pdscans); 976 COPY(pdanscan); 977 COPY(pdobscan); 978 COPY(pdreact); 979 COPY(pdbusy); 980 COPY(pdpending); 981 COPY(pddeact); 982 COPY(bootpages); 983#undef COPY 984 kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head)); 985 addr = TAILQ_FIRST(&pool_head); 986 uvmexp.poolpages = 0; 987 for (; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist)) { 988 deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed"); 989 deref_kptr(pp->pr_alloc, &pa, sizeof(pa), 990 "pool allocator trashed"); 991 bytes = pp->pr_npages * pa.pa_pagesz; 992 if ((pp->pr_roflags & PR_RECURSIVE) != 0) 993 bytes -= (pp->pr_nout * pp->pr_size); 994 uvmexp.poolpages += bytes / uvmexp.pagesize; 995 } 996 } 997 998 999 (void)printf("%9" PRIu64 " bytes per page\n", uvmexp.pagesize); 1000 1001 (void)printf("%9" PRIu64 " page color%s\n", 1002 uvmexp.ncolors, uvmexp.ncolors == 1 ? "" : "s"); 1003 1004 (void)printf("%9" PRIu64 " pages managed\n", uvmexp.npages); 1005 (void)printf("%9" PRIu64 " pages free\n", uvmexp.free); 1006 if (active_kernel) { 1007 (void)printf("%9" PRIu64 " pages active\n", uvmexp.active); 1008 (void)printf("%9" PRIu64 " pages inactive\n", uvmexp.inactive); 1009 } 1010 (void)printf("%9" PRIu64 " pages paging\n", uvmexp.paging); 1011 (void)printf("%9" PRIu64 " pages wired\n", uvmexp.wired); 1012 (void)printf("%9" PRIu64 " zero pages\n", uvmexp.zeropages); 1013 (void)printf("%9" PRIu64 " reserve pagedaemon pages\n", 1014 uvmexp.reserve_pagedaemon); 1015 (void)printf("%9" PRIu64 " reserve kernel pages\n", uvmexp.reserve_kernel); 1016 (void)printf("%9" PRIu64 " boot kernel pages\n", uvmexp.bootpages); 1017 (void)printf("%9" PRIu64 " kernel pool pages\n", uvmexp.poolpages); 1018 (void)printf("%9" PRIu64 " anonymous pages\n", uvmexp.anonpages); 1019 (void)printf("%9" PRIu64 " cached file pages\n", uvmexp.filepages); 1020 (void)printf("%9" PRIu64 " cached executable pages\n", uvmexp.execpages); 1021 1022 (void)printf("%9" PRIu64 " minimum free pages\n", uvmexp.freemin); 1023 (void)printf("%9" PRIu64 " target free pages\n", uvmexp.freetarg); 1024 (void)printf("%9" PRIu64 " maximum wired pages\n", uvmexp.wiredmax); 1025 1026 (void)printf("%9" PRIu64 " swap devices\n", uvmexp.nswapdev); 1027 (void)printf("%9" PRIu64 " swap pages\n", uvmexp.swpages); 1028 (void)printf("%9" PRIu64 " swap pages in use\n", uvmexp.swpginuse); 1029 (void)printf("%9" PRIu64 " swap allocations\n", uvmexp.nswget); 1030 1031 cpucounters(&cc); 1032 1033 (void)printf("%9" PRIu64 " total faults taken\n", cc.nfault); 1034 (void)printf("%9" PRIu64 " traps\n", cc.ntrap); 1035 (void)printf("%9" PRIu64 " device interrupts\n", cc.nintr); 1036 (void)printf("%9" PRIu64 " CPU context switches\n", cc.nswtch); 1037 (void)printf("%9" PRIu64 " software interrupts\n", cc.nsoft); 1038 (void)printf("%9" PRIu64 " system calls\n", cc.nsyscall); 1039 (void)printf("%9" PRIu64 " pagein requests\n", uvmexp.pageins); 1040 (void)printf("%9" PRIu64 " pageout requests\n", uvmexp.pdpageouts); 1041 (void)printf("%9" PRIu64 " pages swapped in\n", uvmexp.pgswapin); 1042 (void)printf("%9" PRIu64 " pages swapped out\n", uvmexp.pgswapout); 1043 (void)printf("%9" PRIu64 " forks total\n", uvmexp.forks); 1044 (void)printf("%9" PRIu64 " forks blocked parent\n", uvmexp.forks_ppwait); 1045 (void)printf("%9" PRIu64 " forks shared address space with parent\n", 1046 uvmexp.forks_sharevm); 1047 (void)printf("%9" PRIu64 " pagealloc zero wanted and avail\n", 1048 uvmexp.pga_zerohit); 1049 (void)printf("%9" PRIu64 " pagealloc zero wanted and not avail\n", 1050 uvmexp.pga_zeromiss); 1051 (void)printf("%9" PRIu64 " aborts of idle page zeroing\n", 1052 uvmexp.zeroaborts); 1053 (void)printf("%9" PRIu64 " pagealloc desired color avail\n", 1054 uvmexp.colorhit); 1055 (void)printf("%9" PRIu64 " pagealloc desired color not avail\n", 1056 uvmexp.colormiss); 1057 (void)printf("%9" PRIu64 " pagealloc local cpu avail\n", 1058 uvmexp.cpuhit); 1059 (void)printf("%9" PRIu64 " pagealloc local cpu not avail\n", 1060 uvmexp.cpumiss); 1061 1062 (void)printf("%9" PRIu64 " faults with no memory\n", uvmexp.fltnoram); 1063 (void)printf("%9" PRIu64 " faults with no anons\n", uvmexp.fltnoanon); 1064 (void)printf("%9" PRIu64 " faults had to wait on pages\n", uvmexp.fltpgwait); 1065 (void)printf("%9" PRIu64 " faults found released page\n", uvmexp.fltpgrele); 1066 (void)printf("%9" PRIu64 " faults relock (%" PRIu64 " ok)\n", uvmexp.fltrelck, 1067 uvmexp.fltrelckok); 1068 (void)printf("%9" PRIu64 " anon page faults\n", uvmexp.fltanget); 1069 (void)printf("%9" PRIu64 " anon retry faults\n", uvmexp.fltanretry); 1070 (void)printf("%9" PRIu64 " amap copy faults\n", uvmexp.fltamcopy); 1071 (void)printf("%9" PRIu64 " neighbour anon page faults\n", uvmexp.fltnamap); 1072 (void)printf("%9" PRIu64 " neighbour object page faults\n", uvmexp.fltnomap); 1073 (void)printf("%9" PRIu64 " locked pager get faults\n", uvmexp.fltlget); 1074 (void)printf("%9" PRIu64 " unlocked pager get faults\n", uvmexp.fltget); 1075 (void)printf("%9" PRIu64 " anon faults\n", uvmexp.flt_anon); 1076 (void)printf("%9" PRIu64 " anon copy on write faults\n", uvmexp.flt_acow); 1077 (void)printf("%9" PRIu64 " object faults\n", uvmexp.flt_obj); 1078 (void)printf("%9" PRIu64 " promote copy faults\n", uvmexp.flt_prcopy); 1079 (void)printf("%9" PRIu64 " promote zero fill faults\n", uvmexp.flt_przero); 1080 1081 (void)printf("%9" PRIu64 " times daemon wokeup\n",uvmexp.pdwoke); 1082 (void)printf("%9" PRIu64 " revolutions of the clock hand\n", uvmexp.pdrevs); 1083 (void)printf("%9" PRIu64 " pages freed by daemon\n", uvmexp.pdfreed); 1084 (void)printf("%9" PRIu64 " pages scanned by daemon\n", uvmexp.pdscans); 1085 (void)printf("%9" PRIu64 " anonymous pages scanned by daemon\n", 1086 uvmexp.pdanscan); 1087 (void)printf("%9" PRIu64 " object pages scanned by daemon\n", uvmexp.pdobscan); 1088 (void)printf("%9" PRIu64 " pages reactivated\n", uvmexp.pdreact); 1089 (void)printf("%9" PRIu64 " pages found busy by daemon\n", uvmexp.pdbusy); 1090 (void)printf("%9" PRIu64 " total pending pageouts\n", uvmexp.pdpending); 1091 (void)printf("%9" PRIu64 " pages deactivated\n", uvmexp.pddeact); 1092 1093 if (active_kernel) { 1094 ssize = sizeof(nch_stats); 1095 if (sysctlbyname("vfs.namecache_stats", &nch_stats, &ssize, 1096 NULL, 0)) { 1097 warn("vfs.namecache_stats failed"); 1098 memset(&nch_stats, 0, sizeof(nch_stats)); 1099 } 1100 } else { 1101 kread(namelist, X_NCHSTATS, &nch_stats, sizeof(nch_stats)); 1102 } 1103 1104 nchtotal = nch_stats.ncs_goodhits + nch_stats.ncs_neghits + 1105 nch_stats.ncs_badhits + nch_stats.ncs_falsehits + 1106 nch_stats.ncs_miss + nch_stats.ncs_long; 1107 (void)printf("%9" PRIu64 " total name lookups\n", nchtotal); 1108 (void)printf("%9" PRIu64 " good hits\n", nch_stats.ncs_goodhits); 1109 (void)printf("%9" PRIu64 " negative hits\n", nch_stats.ncs_neghits); 1110 (void)printf("%9" PRIu64 " bad hits\n", nch_stats.ncs_badhits); 1111 (void)printf("%9" PRIu64 " false hits\n", nch_stats.ncs_falsehits); 1112 (void)printf("%9" PRIu64 " miss\n", nch_stats.ncs_miss); 1113 (void)printf("%9" PRIu64 " too long\n", nch_stats.ncs_long); 1114 (void)printf("%9" PRIu64 " pass2 hits\n", nch_stats.ncs_pass2); 1115 (void)printf("%9" PRIu64 " 2passes\n", nch_stats.ncs_2passes); 1116 (void)printf("%9" PRIu64 " reverse hits\n", nch_stats.ncs_revhits); 1117 (void)printf( 1118 "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n", 1119 "", PCT(nch_stats.ncs_goodhits, nchtotal), 1120 PCT(nch_stats.ncs_neghits, nchtotal), 1121 PCT(nch_stats.ncs_pass2, nchtotal)); 1122 (void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "", 1123 PCT(nch_stats.ncs_badhits, nchtotal), 1124 PCT(nch_stats.ncs_falsehits, nchtotal), 1125 PCT(nch_stats.ncs_long, nchtotal)); 1126} 1127 1128void 1129doforkst(void) 1130{ 1131 if (memf != NULL) { 1132 struct uvmexp uvmexp_kernel; 1133 kread(namelist, X_UVMEXP, &uvmexp_kernel, sizeof(uvmexp_kernel)); 1134#define COPY(field) uvmexp.field = uvmexp_kernel.field 1135 COPY(forks); 1136 COPY(forks_ppwait); 1137 COPY(forks_sharevm); 1138#undef COPY 1139 } else { 1140 size_t size = sizeof(uvmexp); 1141 if (sysctl(uvmexp2_mib, __arraycount(uvmexp2_mib), &uvmexp, 1142 &size, NULL, 0) == -1) 1143 warn("sysctl vm.uvmexp2 failed"); 1144 } 1145 1146 (void)printf("%" PRIu64 " forks total\n", uvmexp.forks); 1147 (void)printf("%" PRIu64 " forks blocked parent\n", uvmexp.forks_ppwait); 1148 (void)printf("%" PRIu64 " forks shared address space with parent\n", 1149 uvmexp.forks_sharevm); 1150} 1151 1152void 1153drvstats(int *ovflwp) 1154{ 1155 size_t dn; 1156 double dtime; 1157 int ovflw = *ovflwp; 1158 1159 /* Calculate disk stat deltas. */ 1160 cpuswap(); 1161 drvswap(); 1162 tkswap(); 1163 1164 for (dn = 0; dn < ndrive; ++dn) { 1165 /* elapsed time for disk stats */ 1166 dtime = cur.cp_etime; 1167 if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) { 1168 dtime = (double)cur.timestamp[dn].tv_sec + 1169 ((double)cur.timestamp[dn].tv_usec / (double)1000000); 1170 } 1171 1172 if (!drv_select[dn]) 1173 continue; 1174 PRWORD(ovflw, " %*.0f", 3, 1, 1175 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); 1176 } 1177 *ovflwp = ovflw; 1178} 1179 1180void 1181cpucounters(struct cpu_counter *cc) 1182{ 1183 static struct cpu_info **cpu_infos; 1184 static int initialised; 1185 struct cpu_info **slot; 1186 1187 if (memf == NULL) { 1188 cc->nintr = uvmexp.intrs; 1189 cc->nsyscall = uvmexp.syscalls; 1190 cc->nswtch = uvmexp.swtch; 1191 cc->nfault = uvmexp.faults; 1192 cc->ntrap = uvmexp.traps; 1193 cc->nsoft = uvmexp.softs; 1194 return; 1195 } 1196 1197 if (!initialised) { 1198 kread(namelist, X_CPU_INFOS, &cpu_infos, sizeof(cpu_infos)); 1199 initialised = 1; 1200 } 1201 1202 slot = cpu_infos; 1203 1204 memset(cc, 0, sizeof(*cc)); 1205 1206 for (;;) { 1207 struct cpu_info tci, *ci = NULL; 1208 1209 deref_kptr(slot++, &ci, sizeof(ci), "CPU array trashed"); 1210 if (!ci) { 1211 break; 1212 } 1213 1214 if ((size_t)kvm_read(kd, (u_long)ci, &tci, sizeof(tci)) 1215 != sizeof(tci)) { 1216 warnx("Can't read cpu info from %p (%s)", 1217 ci, kvm_geterr(kd)); 1218 memset(cc, 0, sizeof(*cc)); 1219 return; 1220 } 1221 cc->nintr += tci.ci_data.cpu_nintr; 1222 cc->nsyscall += tci.ci_data.cpu_nsyscall; 1223 cc->nswtch = tci.ci_data.cpu_nswtch; 1224 cc->nfault = tci.ci_data.cpu_nfault; 1225 cc->ntrap = tci.ci_data.cpu_ntrap; 1226 cc->nsoft = tci.ci_data.cpu_nsoft; 1227 } 1228} 1229 1230void 1231cpustats(int *ovflwp) 1232{ 1233 int state; 1234 double pcnt, total; 1235 double stat_us, stat_sy, stat_id; 1236 int ovflw = *ovflwp; 1237 1238 total = 0; 1239 for (state = 0; state < CPUSTATES; ++state) 1240 total += cur.cp_time[state]; 1241 if (total) 1242 pcnt = 100 / total; 1243 else 1244 pcnt = 0; 1245 stat_us = (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * pcnt; 1246 stat_sy = (cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * pcnt; 1247 stat_id = cur.cp_time[CP_IDLE] * pcnt; 1248 PRWORD(ovflw, " %*.0f", ((stat_sy >= 100) ? 2 : 3), 1, stat_us); 1249 PRWORD(ovflw, " %*.0f", ((stat_us >= 100 || stat_id >= 100) ? 2 : 3), 1, 1250 stat_sy); 1251 PRWORD(ovflw, " %*.0f", 3, 1, stat_id); 1252 *ovflwp = ovflw; 1253} 1254 1255void 1256dointr(int verbose) 1257{ 1258 unsigned long *intrcnt, *ointrcnt; 1259 unsigned long long inttotal, uptime; 1260 int nintr, inamlen; 1261 char *intrname, *ointrname; 1262 1263 inttotal = 0; 1264 uptime = getuptime(); 1265 nintr = intrnl[X_EINTRCNT].n_value - intrnl[X_INTRCNT].n_value; 1266 inamlen = intrnl[X_EINTRNAMES].n_value - intrnl[X_INTRNAMES].n_value; 1267 if (nintr != 0 && inamlen != 0) { 1268 (void)printf("%-34s %16s %8s\n", "interrupt", "total", "rate"); 1269 1270 ointrcnt = intrcnt = malloc((size_t)nintr); 1271 ointrname = intrname = malloc((size_t)inamlen); 1272 if (intrcnt == NULL || intrname == NULL) 1273 errx(1, "%s", ""); 1274 kread(intrnl, X_INTRCNT, intrcnt, (size_t)nintr); 1275 kread(intrnl, X_INTRNAMES, intrname, (size_t)inamlen); 1276 nintr /= sizeof(long); 1277 while (--nintr >= 0) { 1278 if (*intrcnt || verbose) 1279 (void)printf("%-34s %16llu %8llu\n", intrname, 1280 (unsigned long long)*intrcnt, 1281 (unsigned long long) 1282 (*intrcnt / uptime)); 1283 intrname += strlen(intrname) + 1; 1284 inttotal += *intrcnt++; 1285 } 1286 free(ointrcnt); 1287 free(ointrname); 1288 } 1289 1290 doevcnt(verbose, EVCNT_TYPE_INTR); 1291} 1292 1293void 1294doevcnt(int verbose, int type) 1295{ 1296 static const char * const evtypes [] = { "misc", "intr", "trap" }; 1297 uint64_t counttotal, uptime; 1298 struct evcntlist allevents; 1299 struct evcnt evcnt, *evptr; 1300 size_t evlen_max, total_max, rate_max; 1301 char evgroup[EVCNT_STRING_MAX], evname[EVCNT_STRING_MAX]; 1302 1303 counttotal = 0; 1304 uptime = getuptime(); 1305 1306 if (memf == NULL) do { 1307 const int mib[4] = { CTL_KERN, KERN_EVCNT, type, 1308 verbose ? KERN_EVCNT_COUNT_ANY : KERN_EVCNT_COUNT_NONZERO }; 1309 size_t buflen0, buflen = 0; 1310 void *buf0, *buf = NULL; 1311 const struct evcnt_sysctl *evs, *last_evs; 1312 for (;;) { 1313 size_t newlen; 1314 int error; 1315 if (buflen) 1316 buf = malloc(buflen); 1317 error = sysctl(mib, __arraycount(mib), 1318 buf, &newlen, NULL, 0); 1319 if (error) { 1320 err(1, "kern.evcnt"); 1321 if (buf) 1322 free(buf); 1323 return; 1324 } 1325 if (newlen <= buflen) { 1326 buflen = newlen; 1327 break; 1328 } 1329 if (buf) 1330 free(buf); 1331 buflen = newlen; 1332 } 1333 buflen0 = buflen; 1334 evs = buf0 = buf; 1335 last_evs = (void *)((char *)buf + buflen); 1336 buflen /= sizeof(uint64_t); 1337 /* calc columns */ 1338 evlen_max = 0; 1339 total_max = sizeof("total") - 1; 1340 rate_max = sizeof("rate") - 1; 1341 while (evs < last_evs 1342 && buflen >= sizeof(*evs)/sizeof(uint64_t) 1343 && buflen >= evs->ev_len) { 1344 char cbuf[64]; 1345 size_t len; 1346 len = strlen(evs->ev_strings + evs->ev_grouplen + 1); 1347 len += evs->ev_grouplen + 1; 1348 if (evlen_max < len) 1349 evlen_max= len; 1350 len = snprintf(cbuf, sizeof(cbuf), "%"PRIu64, 1351 evs->ev_count); 1352 if (total_max < len) 1353 total_max = len; 1354 len = snprintf(cbuf, sizeof(cbuf), "%"PRIu64, 1355 evs->ev_count / uptime); 1356 if (rate_max < len) 1357 rate_max = len; 1358 buflen -= evs->ev_len; 1359 evs = (const void *) 1360 ((const uint64_t *)evs + evs->ev_len); 1361 } 1362 1363 (void)printf(type == EVCNT_TYPE_ANY ? 1364 "%-*s %*s %*s %s\n" : 1365 "%-*s %*s %*s\n", 1366 (int)evlen_max, "interrupt", 1367 (int)total_max, "total", 1368 (int)rate_max, "rate", 1369 "type"); 1370 1371 buflen = buflen0; 1372 evs = buf0; 1373 last_evs = (void *)((char *)buf + buflen); 1374 buflen /= sizeof(uint64_t); 1375 while (evs < last_evs 1376 && buflen >= sizeof(*evs)/sizeof(uint64_t) 1377 && buflen >= evs->ev_len) { 1378 (void)printf(type == EVCNT_TYPE_ANY ? 1379 "%s %s%*s %*"PRIu64" %*"PRIu64" %s\n" : 1380 "%s %s%*s %*"PRIu64" %*"PRIu64"\n", 1381 evs->ev_strings, 1382 evs->ev_strings + evs->ev_grouplen + 1, 1383 (int)evlen_max - (evs->ev_grouplen + 1 1384 + evs->ev_namelen), "", 1385 (int)total_max, evs->ev_count, 1386 (int)rate_max, evs->ev_count / uptime, 1387 (evs->ev_type < __arraycount(evtypes) ? 1388 evtypes[evs->ev_type] : "?")); 1389 buflen -= evs->ev_len; 1390 counttotal += evs->ev_count; 1391 evs = (const void *) 1392 ((const uint64_t *)evs + evs->ev_len); 1393 } 1394 free(buf); 1395 if (type != EVCNT_TYPE_ANY) 1396 (void)printf("%-*s %*"PRIu64" %*"PRIu64"\n", 1397 (int)evlen_max, "Total", 1398 (int)total_max, counttotal, 1399 (int)rate_max, counttotal / uptime); 1400 return; 1401 } while (/*CONSTCOND*/ 0); 1402 1403 if (type == EVCNT_TYPE_ANY) 1404 (void)printf("%-34s %16s %8s %s\n", "event", "total", "rate", 1405 "type"); 1406 1407 kread(namelist, X_ALLEVENTS, &allevents, sizeof allevents); 1408 evptr = TAILQ_FIRST(&allevents); 1409 while (evptr) { 1410 deref_kptr(evptr, &evcnt, sizeof(evcnt), "event chain trashed"); 1411 1412 evptr = TAILQ_NEXT(&evcnt, ev_list); 1413 if (evcnt.ev_count == 0 && !verbose) 1414 continue; 1415 if (type != EVCNT_TYPE_ANY && evcnt.ev_type != type) 1416 continue; 1417 1418 deref_kptr(evcnt.ev_group, evgroup, 1419 (size_t)evcnt.ev_grouplen + 1, "event chain trashed"); 1420 deref_kptr(evcnt.ev_name, evname, 1421 (size_t)evcnt.ev_namelen + 1, "event chain trashed"); 1422 1423 (void)printf(type == EVCNT_TYPE_ANY ? 1424 "%s %s%*s %16"PRIu64" %8"PRIu64" %s\n" : 1425 "%s %s%*s %16"PRIu64" %8"PRIu64"\n", 1426 evgroup, evname, 1427 34 - (evcnt.ev_grouplen + 1 + evcnt.ev_namelen), "", 1428 evcnt.ev_count, 1429 (evcnt.ev_count / uptime), 1430 (evcnt.ev_type < __arraycount(evtypes) ? 1431 evtypes[evcnt.ev_type] : "?")); 1432 1433 counttotal += evcnt.ev_count; 1434 } 1435 if (type != EVCNT_TYPE_ANY) 1436 (void)printf("%-34s %16"PRIu64" %8"PRIu64"\n", 1437 "Total", counttotal, counttotal / uptime); 1438} 1439 1440static void 1441dopool_sysctl(int verbose, int wide) 1442{ 1443 uint64_t total, inuse, this_total, this_inuse; 1444 struct { 1445 uint64_t pt_nget; 1446 uint64_t pt_nfail; 1447 uint64_t pt_nput; 1448 uint64_t pt_nout; 1449 uint64_t pt_nitems; 1450 uint64_t pt_npagealloc; 1451 uint64_t pt_npagefree; 1452 uint64_t pt_npages; 1453 } pool_totals; 1454 size_t i, len; 1455 int name_len, ovflw; 1456 struct pool_sysctl *pp, *data; 1457 char maxp[32]; 1458 1459 data = asysctlbyname("kern.pool", &len); 1460 if (data == NULL) 1461 err(1, "failed to read kern.pool"); 1462 1463 memset(&pool_totals, 0, sizeof pool_totals); 1464 total = inuse = 0; 1465 len /= sizeof(*data); 1466 1467 (void)printf("Memory resource pool statistics\n"); 1468 (void)printf( 1469 "%-*s%*s%*s%*s%*s%s%s%*s%*s%*s%s%*s%6s%*s%5s%s%s\n", 1470 wide ? 16 : 11, "Name", 1471 wide ? 7 : 5, "Size", 1472 wide ? 12 : 9, "Requests", 1473 wide ? 8 : 5, "Fail", 1474 wide ? 12 : 9, "Releases", 1475 wide ? " InUse" : "", 1476 wide ? " Avail" : "", 1477 wide ? 11 : 6, "Pgreq", 1478 wide ? 11 : 6, "Pgrel", 1479 wide ? 8 : 6, "Npage", 1480 wide ? " PageSz" : "", 1481 wide ? 7 : 6, "Hiwat", 1482 "Minpg", 1483 wide ? 7 : 6, "Maxpg", 1484 "Idle", 1485 wide ? " Flags" : "", 1486 wide ? " Util" : ""); 1487 1488 name_len = MIN((int)sizeof(pp->pr_wchan), wide ? 16 : 11); 1489 for (i = 0; i < len; ++i) { 1490 pp = &data[i]; 1491 if (pp->pr_nget == 0 && !verbose) 1492 continue; 1493 if (pp->pr_maxpages == UINT_MAX) 1494 (void)snprintf(maxp, sizeof(maxp), "inf"); 1495 else 1496 (void)snprintf(maxp, sizeof(maxp), "%" PRIu64, 1497 pp->pr_maxpages); 1498 ovflw = 0; 1499 PRWORD(ovflw, "%-*s", name_len, 0, pp->pr_wchan); 1500 PRWORD(ovflw, " %*" PRIu64, wide ? 7 : 5, 1, pp->pr_size); 1501 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pp->pr_nget); 1502 pool_totals.pt_nget += pp->pr_nget; 1503 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pp->pr_nfail); 1504 pool_totals.pt_nfail += pp->pr_nfail; 1505 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pp->pr_nput); 1506 pool_totals.pt_nput += pp->pr_nput; 1507 if (wide) { 1508 PRWORD(ovflw, " %*" PRIu64, 9, 1, pp->pr_nout); 1509 pool_totals.pt_nout += pp->pr_nout; 1510 PRWORD(ovflw, " %*" PRIu64, 9, 1, pp->pr_nitems); 1511 pool_totals.pt_nitems += pp->pr_nitems; 1512 } 1513 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pp->pr_npagealloc); 1514 pool_totals.pt_npagealloc += pp->pr_npagealloc; 1515 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pp->pr_npagefree); 1516 pool_totals.pt_npagefree += pp->pr_npagefree; 1517 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 6, 1, pp->pr_npages); 1518 pool_totals.pt_npages += pp->pr_npages; 1519 if (wide) 1520 PRWORD(ovflw, " %*" PRIu64, 7, 1, pp->pr_pagesize); 1521 PRWORD(ovflw, " %*" PRIu64, wide ? 7 : 6, 1, pp->pr_hiwat); 1522 PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_minpages); 1523 PRWORD(ovflw, " %*s", wide ? 7 : 6, 1, maxp); 1524 PRWORD(ovflw, " %*" PRIu64, 5, 1, pp->pr_nidle); 1525 if (wide) 1526 PRWORD(ovflw, " 0x%0*" PRIx64, 5, 1, 1527 pp->pr_flags); 1528 1529 this_inuse = pp->pr_nout * pp->pr_size; 1530 this_total = pp->pr_npages * pp->pr_pagesize; 1531 if (pp->pr_flags & PR_RECURSIVE) { 1532 /* 1533 * Don't count in-use memory, since it's part 1534 * of another pool and will be accounted for 1535 * there. 1536 */ 1537 total += (this_total - this_inuse); 1538 } else { 1539 inuse += this_inuse; 1540 total += this_total; 1541 } 1542 if (wide) { 1543 if (this_total == 0) 1544 (void)printf(" ---"); 1545 else 1546 (void)printf(" %5.1f%%", 1547 (100.0 * this_inuse) / this_total); 1548 } 1549 (void)printf("\n"); 1550 } 1551 ovflw = 0; 1552 PRWORD(ovflw, "%-*s", name_len, 0, "Totals"); 1553 PRWORD(ovflw, " %*s", wide ? 7 : 5, 1, ""); 1554 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pool_totals.pt_nget); 1555 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pool_totals.pt_nfail); 1556 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pool_totals.pt_nput); 1557 if (wide) { 1558 PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nout); 1559 PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nitems); 1560 } 1561 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagealloc); 1562 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagefree); 1563 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 6, 1, pool_totals.pt_npages); 1564 (void)printf("\n"); 1565 1566 inuse /= KILO; 1567 total /= KILO; 1568 (void)printf( 1569 "\nIn use %" PRIu64 "K, " 1570 "total allocated %" PRIu64 "K; utilization %.1f%%\n", 1571 inuse, total, (100.0 * inuse) / total); 1572 1573 free(data); 1574} 1575 1576void 1577dopool(int verbose, int wide) 1578{ 1579 int first, ovflw; 1580 void *addr; 1581 long total, inuse, this_total, this_inuse; 1582 struct { 1583 uint64_t pt_nget; 1584 uint64_t pt_nfail; 1585 uint64_t pt_nput; 1586 uint64_t pt_nout; 1587 uint64_t pt_nitems; 1588 uint64_t pt_npagealloc; 1589 uint64_t pt_npagefree; 1590 uint64_t pt_npages; 1591 } pool_totals; 1592 TAILQ_HEAD(,pool) pool_head; 1593 struct pool pool, *pp = &pool; 1594 struct pool_allocator pa; 1595 char maxp[32], name[32]; 1596 1597 if (memf == NULL) 1598 return dopool_sysctl(verbose, wide); 1599 1600 memset(&pool_totals, 0, sizeof pool_totals); 1601 kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head)); 1602 addr = TAILQ_FIRST(&pool_head); 1603 1604 total = inuse = 0; 1605 1606 for (first = 1; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist) ) { 1607 deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed"); 1608 deref_kptr(pp->pr_alloc, &pa, sizeof(pa), 1609 "pool allocator trashed"); 1610 deref_kptr(pp->pr_wchan, name, sizeof(name), 1611 "pool wait channel trashed"); 1612 name[sizeof(name)-1] = '\0'; 1613 1614 if (first) { 1615 (void)printf("Memory resource pool statistics\n"); 1616 (void)printf( 1617 "%-*s%*s%*s%*s%*s%s%s%*s%*s%*s%s%*s%6s%*s%5s%s%s\n", 1618 wide ? 16 : 11, "Name", 1619 wide ? 7 : 5, "Size", 1620 wide ? 12 : 9, "Requests", 1621 wide ? 8 : 5, "Fail", 1622 wide ? 12 : 9, "Releases", 1623 wide ? " InUse" : "", 1624 wide ? " Avail" : "", 1625 wide ? 11 : 6, "Pgreq", 1626 wide ? 11 : 6, "Pgrel", 1627 wide ? 8 : 6, "Npage", 1628 wide ? " PageSz" : "", 1629 wide ? 7 : 6, "Hiwat", 1630 "Minpg", 1631 wide ? 7 : 6, "Maxpg", 1632 "Idle", 1633 wide ? " Flags" : "", 1634 wide ? " Util" : ""); 1635 first = 0; 1636 } 1637 if (pp->pr_nget == 0 && !verbose) 1638 continue; 1639 if (pp->pr_maxpages == UINT_MAX) 1640 (void)snprintf(maxp, sizeof(maxp), "inf"); 1641 else 1642 (void)snprintf(maxp, sizeof(maxp), "%u", 1643 pp->pr_maxpages); 1644 ovflw = 0; 1645 PRWORD(ovflw, "%-*s", wide ? 16 : 11, 0, name); 1646 PRWORD(ovflw, " %*u", wide ? 7 : 5, 1, pp->pr_size); 1647 PRWORD(ovflw, " %*lu", wide ? 12 : 9, 1, pp->pr_nget); 1648 pool_totals.pt_nget += pp->pr_nget; 1649 PRWORD(ovflw, " %*lu", wide ? 8 : 5, 1, pp->pr_nfail); 1650 pool_totals.pt_nfail += pp->pr_nfail; 1651 PRWORD(ovflw, " %*lu", wide ? 12 : 9, 1, pp->pr_nput); 1652 pool_totals.pt_nput += pp->pr_nput; 1653 if (wide) { 1654 PRWORD(ovflw, " %*u", 9, 1, pp->pr_nout); 1655 pool_totals.pt_nout += pp->pr_nout; 1656 PRWORD(ovflw, " %*u", 9, 1, pp->pr_nitems); 1657 pool_totals.pt_nitems += pp->pr_nitems; 1658 } 1659 PRWORD(ovflw, " %*lu", wide ? 11 : 6, 1, pp->pr_npagealloc); 1660 pool_totals.pt_npagealloc += pp->pr_npagealloc; 1661 PRWORD(ovflw, " %*lu", wide ? 11 : 6, 1, pp->pr_npagefree); 1662 pool_totals.pt_npagefree += pp->pr_npagefree; 1663 PRWORD(ovflw, " %*u", wide ? 8 : 6, 1, pp->pr_npages); 1664 pool_totals.pt_npages += pp->pr_npages; 1665 if (wide) 1666 PRWORD(ovflw, " %*u", 7, 1, pa.pa_pagesz); 1667 PRWORD(ovflw, " %*u", wide ? 7 : 6, 1, pp->pr_hiwat); 1668 PRWORD(ovflw, " %*u", 6, 1, pp->pr_minpages); 1669 PRWORD(ovflw, " %*s", wide ? 7 : 6, 1, maxp); 1670 PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nidle); 1671 if (wide) 1672 PRWORD(ovflw, " 0x%0*x", 5, 1, 1673 pp->pr_flags | pp->pr_roflags); 1674 1675 this_inuse = pp->pr_nout * pp->pr_size; 1676 this_total = pp->pr_npages * pa.pa_pagesz; 1677 if (pp->pr_roflags & PR_RECURSIVE) { 1678 /* 1679 * Don't count in-use memory, since it's part 1680 * of another pool and will be accounted for 1681 * there. 1682 */ 1683 total += (this_total - this_inuse); 1684 } else { 1685 inuse += this_inuse; 1686 total += this_total; 1687 } 1688 if (wide) { 1689 if (this_total == 0) 1690 (void)printf(" ---"); 1691 else 1692 (void)printf(" %5.1f%%", 1693 (100.0 * this_inuse) / this_total); 1694 } 1695 (void)printf("\n"); 1696 } 1697 ovflw = 0; 1698 PRWORD(ovflw, "%-*s", wide ? 16 : 11, 0, "Totals"); 1699 PRWORD(ovflw, " %*s", wide ? 7 : 5, 1, ""); 1700 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pool_totals.pt_nget); 1701 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 5, 1, pool_totals.pt_nfail); 1702 PRWORD(ovflw, " %*" PRIu64, wide ? 12 : 9, 1, pool_totals.pt_nput); 1703 if (wide) { 1704 PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nout); 1705 PRWORD(ovflw, " %*" PRIu64, 9, 1, pool_totals.pt_nitems); 1706 } 1707 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagealloc); 1708 PRWORD(ovflw, " %*" PRIu64, wide ? 11 : 6, 1, pool_totals.pt_npagefree); 1709 PRWORD(ovflw, " %*" PRIu64, wide ? 8 : 6, 1, pool_totals.pt_npages); 1710 (void)printf("\n"); 1711 1712 inuse /= KILO; 1713 total /= KILO; 1714 (void)printf( 1715 "\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n", 1716 inuse, total, (100.0 * inuse) / total); 1717} 1718 1719static void 1720dopoolcache_sysctl(int verbose) 1721{ 1722 struct pool_sysctl *data, *pp; 1723 size_t i, len; 1724 bool first = true; 1725 int ovflw; 1726 uint64_t tot; 1727 double p; 1728 1729 data = asysctlbyname("kern.pool", &len); 1730 if (data == NULL) 1731 err(1, "failed to read kern.pool"); 1732 len /= sizeof(*data); 1733 1734 for (i = 0; i < len; ++i) { 1735 pp = &data[i]; 1736 if (pp->pr_cache_meta_size == 0) 1737 continue; 1738 1739 if (pp->pr_cache_nmiss_global == 0 && !verbose) 1740 continue; 1741 1742 if (first) { 1743 (void)printf("Pool cache statistics.\n"); 1744 (void)printf("%-*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 1745 12, "Name", 1746 6, "Spin", 1747 6, "GrpSz", 1748 5, "Full", 1749 5, "Emty", 1750 10, "PoolLayer", 1751 11, "CacheLayer", 1752 6, "Hit%", 1753 12, "CpuLayer", 1754 6, "Hit%" 1755 ); 1756 first = false; 1757 } 1758 1759 ovflw = 0; 1760 PRWORD(ovflw, "%-*s", MIN((int)sizeof(pp->pr_wchan), 13), 1, 1761 pp->pr_wchan); 1762 PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_cache_ncontended); 1763 PRWORD(ovflw, " %*" PRIu64, 6, 1, pp->pr_cache_meta_size); 1764 PRWORD(ovflw, " %*" PRIu64, 5, 1, pp->pr_cache_nfull); 1765 PRWORD(ovflw, " %*" PRIu64, 5, 1, pp->pr_cache_nempty); 1766 PRWORD(ovflw, " %*" PRIu64, 10, 1, pp->pr_cache_nmiss_global); 1767 1768 tot = pp->pr_cache_nhit_global + pp->pr_cache_nmiss_global; 1769 p = pp->pr_cache_nhit_global * 100.0 / tot; 1770 PRWORD(ovflw, " %*" PRIu64, 11, 1, tot); 1771 PRWORD(ovflw, " %*.1f", 6, 1, p); 1772 1773 tot = pp->pr_cache_nhit_pcpu + pp->pr_cache_nmiss_pcpu; 1774 p = pp->pr_cache_nhit_pcpu * 100.0 / tot; 1775 PRWORD(ovflw, " %*" PRIu64, 12, 1, tot); 1776 PRWORD(ovflw, " %*.1f", 6, 1, p); 1777 printf("\n"); 1778 } 1779} 1780 1781void 1782dopoolcache(int verbose) 1783{ 1784 struct pool_cache pool_cache, *pc = &pool_cache; 1785 pool_cache_cpu_t cache_cpu, *cc = &cache_cpu; 1786 TAILQ_HEAD(,pool) pool_head; 1787 struct pool pool, *pp = &pool; 1788 char name[32]; 1789 uint64_t cpuhit, cpumiss, tot; 1790 void *addr; 1791 int first, ovflw; 1792 size_t i; 1793 double p; 1794 1795 if (memf == NULL) 1796 return dopoolcache_sysctl(verbose); 1797 1798 kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head)); 1799 addr = TAILQ_FIRST(&pool_head); 1800 1801 for (first = 1; addr != NULL; addr = TAILQ_NEXT(pp, pr_poollist) ) { 1802 deref_kptr(addr, pp, sizeof(*pp), "pool chain trashed"); 1803 if (pp->pr_cache == NULL) 1804 continue; 1805 deref_kptr(pp->pr_wchan, name, sizeof(name), 1806 "pool wait channel trashed"); 1807 deref_kptr(pp->pr_cache, pc, sizeof(*pc), "pool cache trashed"); 1808 if (pc->pc_misses == 0 && !verbose) 1809 continue; 1810 name[sizeof(name)-1] = '\0'; 1811 1812 cpuhit = 0; 1813 cpumiss = 0; 1814 for (i = 0; i < __arraycount(pc->pc_cpus); i++) { 1815 if ((addr = pc->pc_cpus[i]) == NULL) 1816 continue; 1817 deref_kptr(addr, cc, sizeof(*cc), 1818 "pool cache cpu trashed"); 1819 cpuhit += cc->cc_hits; 1820 cpumiss += cc->cc_misses; 1821 } 1822 1823 if (first) { 1824 (void)printf("Pool cache statistics.\n"); 1825 (void)printf("%-*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 1826 12, "Name", 1827 6, "Spin", 1828 6, "GrpSz", 1829 5, "Full", 1830 5, "Emty", 1831 10, "PoolLayer", 1832 11, "CacheLayer", 1833 6, "Hit%", 1834 12, "CpuLayer", 1835 6, "Hit%" 1836 ); 1837 first = 0; 1838 } 1839 1840 ovflw = 0; 1841 PRWORD(ovflw, "%-*s", 13, 1, name); 1842 PRWORD(ovflw, " %*llu", 6, 1, (long long)pc->pc_contended); 1843 PRWORD(ovflw, " %*u", 6, 1, pc->pc_pcgsize); 1844 PRWORD(ovflw, " %*u", 5, 1, pc->pc_nfull); 1845 PRWORD(ovflw, " %*u", 5, 1, pc->pc_nempty); 1846 PRWORD(ovflw, " %*llu", 10, 1, (long long)pc->pc_misses); 1847 1848 tot = pc->pc_hits + pc->pc_misses; 1849 p = pc->pc_hits * 100.0 / (tot); 1850 PRWORD(ovflw, " %*llu", 11, 1, (long long)tot); 1851 PRWORD(ovflw, " %*.1f", 6, 1, p); 1852 1853 tot = cpuhit + cpumiss; 1854 p = cpuhit * 100.0 / (tot); 1855 PRWORD(ovflw, " %*llu", 12, 1, (long long)tot); 1856 PRWORD(ovflw, " %*.1f", 6, 1, p); 1857 printf("\n"); 1858 } 1859} 1860 1861enum hashtype { /* from <sys/systm.h> */ 1862 HASH_LIST, 1863 HASH_SLIST, 1864 HASH_TAILQ, 1865 HASH_PSLIST 1866}; 1867 1868struct uidinfo { /* XXX: no kernel header file */ 1869 LIST_ENTRY(uidinfo) ui_hash; 1870 uid_t ui_uid; 1871 long ui_proccnt; 1872}; 1873 1874struct kernel_hash { 1875 const char * description; /* description */ 1876 int hashsize; /* nlist index for hash size */ 1877 int hashtbl; /* nlist index for hash table */ 1878 enum hashtype type; /* type of hash table */ 1879 size_t offset; /* offset of {LIST,TAILQ}_NEXT */ 1880} khashes[] = 1881{ 1882 { 1883 "buffer hash", 1884 X_BUFHASH, X_BUFHASHTBL, 1885 HASH_LIST, offsetof(struct buf, b_hash) 1886 }, { 1887 "ipv4 address -> interface hash", 1888 X_IFADDRHASH, X_IFADDRHASHTBL, 1889 HASH_LIST, offsetof(struct in_ifaddr, ia_hash), 1890 }, { 1891 "name cache hash", 1892 X_NCHASH, X_NCHASHTBL, 1893 HASH_LIST, offsetof(struct namecache, nc_hash), 1894 }, { 1895 "user info (uid -> used processes) hash", 1896 X_UIHASH, X_UIHASHTBL, 1897 HASH_LIST, offsetof(struct uidinfo, ui_hash), 1898 }, { 1899 "vnode cache hash", 1900 X_NCVCACHEHASH, X_NCVCACHETBL, 1901 HASH_SLIST, offsetof(struct vnode_impl, vi_hash), 1902 }, { 1903 NULL, -1, -1, 0, 0, 1904 } 1905}; 1906 1907void 1908dohashstat(int verbose, int todo, const char *hashname) 1909{ 1910 LIST_HEAD(, generic) *hashtbl_list; 1911 SLIST_HEAD(, generic) *hashtbl_slist; 1912 TAILQ_HEAD(, generic) *hashtbl_tailq; 1913 struct kernel_hash *curhash; 1914 void *hashaddr, *hashbuf, *nhashbuf, *nextaddr; 1915 size_t elemsize, hashbufsize, thissize; 1916 u_long hashsize, i; 1917 int used, items, chain, maxchain; 1918 1919 hashbuf = NULL; 1920 hashbufsize = 0; 1921 1922 if (todo & HASHLIST) { 1923 (void)printf("Supported hashes:\n"); 1924 for (curhash = khashes; curhash->description; curhash++) { 1925 if (hashnl[curhash->hashsize].n_value == 0 || 1926 hashnl[curhash->hashtbl].n_value == 0) 1927 continue; 1928 (void)printf("\t%-16s%s\n", 1929 hashnl[curhash->hashsize].n_name + 1, 1930 curhash->description); 1931 } 1932 return; 1933 } 1934 1935 if (hashname != NULL) { 1936 for (curhash = khashes; curhash->description; curhash++) { 1937 if (strcmp(hashnl[curhash->hashsize].n_name + 1, 1938 hashname) == 0 && 1939 hashnl[curhash->hashsize].n_value != 0 && 1940 hashnl[curhash->hashtbl].n_value != 0) 1941 break; 1942 } 1943 if (curhash->description == NULL) { 1944 warnx("%s: no such hash", hashname); 1945 return; 1946 } 1947 } 1948 1949 (void)printf( 1950 "%-16s %8s %8s %8s %8s %8s %8s\n" 1951 "%-16s %8s %8s %8s %8s %8s %8s\n", 1952 "", "total", "used", "util", "num", "average", "maximum", 1953 "hash table", "buckets", "buckets", "%", "items", "chain", 1954 "chain"); 1955 1956 for (curhash = khashes; curhash->description; curhash++) { 1957 if (hashnl[curhash->hashsize].n_value == 0 || 1958 hashnl[curhash->hashtbl].n_value == 0) 1959 continue; 1960 if (hashname != NULL && 1961 strcmp(hashnl[curhash->hashsize].n_name + 1, hashname)) 1962 continue; 1963 elemsize = curhash->type == HASH_LIST ? 1964 sizeof(*hashtbl_list) : sizeof(*hashtbl_tailq); 1965 deref_kptr((void *)hashnl[curhash->hashsize].n_value, 1966 &hashsize, sizeof(hashsize), 1967 hashnl[curhash->hashsize].n_name); 1968 hashsize++; 1969 deref_kptr((void *)hashnl[curhash->hashtbl].n_value, 1970 &hashaddr, sizeof(hashaddr), 1971 hashnl[curhash->hashtbl].n_name); 1972 if (verbose) 1973 (void)printf( 1974 "%s %lu, %s %p, offset %ld, elemsize %llu\n", 1975 hashnl[curhash->hashsize].n_name + 1, hashsize, 1976 hashnl[curhash->hashtbl].n_name + 1, hashaddr, 1977 (long)curhash->offset, 1978 (unsigned long long)elemsize); 1979 thissize = hashsize * elemsize; 1980 if (hashbuf == NULL || thissize > hashbufsize) { 1981 if ((nhashbuf = realloc(hashbuf, thissize)) == NULL) 1982 errx(1, "malloc hashbuf %llu", 1983 (unsigned long long)hashbufsize); 1984 hashbuf = nhashbuf; 1985 hashbufsize = thissize; 1986 } 1987 deref_kptr(hashaddr, hashbuf, thissize, 1988 hashnl[curhash->hashtbl].n_name); 1989 used = 0; 1990 items = maxchain = 0; 1991 if (curhash->type == HASH_LIST) { 1992 hashtbl_list = hashbuf; 1993 hashtbl_slist = NULL; 1994 hashtbl_tailq = NULL; 1995 } else if (curhash->type == HASH_SLIST) { 1996 hashtbl_list = NULL; 1997 hashtbl_slist = hashbuf; 1998 hashtbl_tailq = NULL; 1999 } else { 2000 hashtbl_list = NULL; 2001 hashtbl_slist = NULL; 2002 hashtbl_tailq = hashbuf; 2003 } 2004 for (i = 0; i < hashsize; i++) { 2005 if (curhash->type == HASH_LIST) 2006 nextaddr = LIST_FIRST(&hashtbl_list[i]); 2007 else if (curhash->type == HASH_SLIST) 2008 nextaddr = SLIST_FIRST(&hashtbl_slist[i]); 2009 else 2010 nextaddr = TAILQ_FIRST(&hashtbl_tailq[i]); 2011 if (nextaddr == NULL) 2012 continue; 2013 if (verbose) 2014 (void)printf("%5lu: %p\n", i, nextaddr); 2015 used++; 2016 chain = 0; 2017 do { 2018 chain++; 2019 deref_kptr((char *)nextaddr + curhash->offset, 2020 &nextaddr, sizeof(void *), 2021 "hash chain corrupted"); 2022 if (verbose > 1) 2023 (void)printf("got nextaddr as %p\n", 2024 nextaddr); 2025 } while (nextaddr != NULL); 2026 items += chain; 2027 if (verbose && chain > 1) 2028 (void)printf("\tchain = %d\n", chain); 2029 if (chain > maxchain) 2030 maxchain = chain; 2031 } 2032 (void)printf("%-16s %8ld %8d %8.2f %8d %8.2f %8d\n", 2033 hashnl[curhash->hashsize].n_name + 1, 2034 hashsize, used, used * 100.0 / hashsize, 2035 items, used ? (double)items / used : 0.0, maxchain); 2036 } 2037} 2038 2039/* 2040 * kreadc like kread but returns 1 if successful, 0 otherwise 2041 */ 2042int 2043kreadc(struct nlist *nl, int nlx, void *addr, size_t size) 2044{ 2045 const char *sym; 2046 2047 sym = nl[nlx].n_name; 2048 if (*sym == '_') 2049 ++sym; 2050 if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0) 2051 return 0; 2052 deref_kptr((void *)nl[nlx].n_value, addr, size, sym); 2053 return 1; 2054} 2055 2056/* 2057 * kread reads something from the kernel, given its nlist index in namelist[]. 2058 */ 2059void 2060kread(struct nlist *nl, int nlx, void *addr, size_t size) 2061{ 2062 const char *sym; 2063 2064 sym = nl[nlx].n_name; 2065 if (*sym == '_') 2066 ++sym; 2067 if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0) 2068 errx(1, "symbol %s not defined", sym); 2069 deref_kptr((void *)nl[nlx].n_value, addr, size, sym); 2070} 2071 2072/* 2073 * Dereference the kernel pointer `kptr' and fill in the local copy 2074 * pointed to by `ptr'. The storage space must be pre-allocated, 2075 * and the size of the copy passed in `len'. 2076 */ 2077void 2078deref_kptr(const void *kptr, void *ptr, size_t len, const char *msg) 2079{ 2080 2081 if (*msg == '_') 2082 msg++; 2083 if ((size_t)kvm_read(kd, (u_long)kptr, (char *)ptr, len) != len) 2084 errx(1, "kptr %lx: %s: %s", (u_long)kptr, msg, kvm_geterr(kd)); 2085} 2086 2087/* 2088 * Traverse the kernel history buffers, performing the requested action. 2089 * 2090 * Note, we assume that if we're not listing, we're dumping. 2091 */ 2092void 2093hist_traverse(int todo, const char *histname) 2094{ 2095 struct kern_history_head histhead; 2096 struct kern_history hist, *histkva; 2097 char *name = NULL; 2098 size_t namelen = 0; 2099 2100 if (histnl[0].n_value == 0) { 2101 warnx("kernel history is not compiled into the kernel."); 2102 return; 2103 } 2104 2105 deref_kptr((void *)histnl[X_KERN_HISTORIES].n_value, &histhead, 2106 sizeof(histhead), histnl[X_KERN_HISTORIES].n_name); 2107 2108 if (histhead.lh_first == NULL) { 2109 warnx("No active kernel history logs."); 2110 return; 2111 } 2112 2113 if (todo & HISTLIST) 2114 (void)printf("Active kernel histories:"); 2115 2116 for (histkva = LIST_FIRST(&histhead); histkva != NULL; 2117 histkva = LIST_NEXT(&hist, list)) { 2118 deref_kptr(histkva, &hist, sizeof(hist), "histkva"); 2119 if (name == NULL || hist.namelen > namelen) { 2120 if (name != NULL) 2121 free(name); 2122 namelen = hist.namelen; 2123 if ((name = malloc(namelen + 1)) == NULL) 2124 err(1, "malloc history name"); 2125 } 2126 2127 deref_kptr(hist.name, name, namelen, "history name"); 2128 name[namelen] = '\0'; 2129 if (todo & HISTLIST) 2130 (void)printf(" %s", name); 2131 else { 2132 /* 2133 * If we're dumping all histories, do it, else 2134 * check to see if this is the one we want. 2135 */ 2136 if (histname == NULL || strcmp(histname, name) == 0) { 2137 if (histname == NULL) 2138 (void)printf( 2139 "\nkernel history `%s':\n", name); 2140 hist_dodump(&hist); 2141 } 2142 } 2143 } 2144 2145 if (todo & HISTLIST) 2146 (void)putchar('\n'); 2147 2148 if (name != NULL) 2149 free(name); 2150} 2151 2152/* 2153 * Actually dump the history buffer at the specified KVA. 2154 */ 2155void 2156hist_dodump(struct kern_history *histp) 2157{ 2158 struct kern_history_ent *histents, *e; 2159 struct timeval tv; 2160 size_t histsize; 2161 char *fmt = NULL, *fn = NULL; 2162 size_t fmtlen = 0, fnlen = 0; 2163 unsigned i; 2164 2165 histsize = sizeof(struct kern_history_ent) * histp->n; 2166 2167 if ((histents = malloc(histsize)) == NULL) 2168 err(1, "malloc history entries"); 2169 2170 (void)memset(histents, 0, histsize); 2171 2172 (void)printf("%"PRIu32" entries, next is %"PRIu32"\n", 2173 histp->n, histp->f); 2174 2175 deref_kptr(histp->e, histents, histsize, "history entries"); 2176 i = histp->f; 2177 do { 2178 e = &histents[i]; 2179 if (e->fmt != NULL) { 2180 if (fmt == NULL || e->fmtlen > fmtlen) { 2181 free(fmt); 2182 fmtlen = e->fmtlen; 2183 if ((fmt = malloc(fmtlen + 1)) == NULL) 2184 err(1, "malloc printf format"); 2185 } 2186 if (fn == NULL || e->fnlen > fnlen) { 2187 free(fn); 2188 fnlen = e->fnlen; 2189 if ((fn = malloc(fnlen + 1)) == NULL) 2190 err(1, "malloc function name"); 2191 } 2192 2193 deref_kptr(e->fmt, fmt, fmtlen, "printf format"); 2194 fmt[fmtlen] = '\0'; 2195 for (unsigned z = 0; z < fmtlen - 1; z++) { 2196 if (fmt[z] == '%' && fmt[z+1] == 's') 2197 fmt[z+1] = 'p'; 2198 } 2199 2200 deref_kptr(e->fn, fn, fnlen, "function name"); 2201 fn[fnlen] = '\0'; 2202 2203 bintime2timeval(&e->bt, &tv); 2204 (void)printf("%06ld.%06ld ", (long int)tv.tv_sec, 2205 (long int)tv.tv_usec); 2206 (void)printf("%s#%" PRId32 "@%" PRId32 "d: ", 2207 fn, e->call, e->cpunum); 2208 (void)printf(fmt, e->v[0], e->v[1], e->v[2], e->v[3]); 2209 (void)putchar('\n'); 2210 } 2211 i = (i + 1) % histp->n; 2212 } while (i != histp->f); 2213 2214 free(histents); 2215 free(fmt); 2216 free(fn); 2217} 2218 2219void 2220hist_traverse_sysctl(int todo, const char *histname) 2221{ 2222 int error; 2223 int mib[4]; 2224 unsigned int i; 2225 size_t len, miblen; 2226 struct sysctlnode query, histnode[32]; 2227 2228 /* retrieve names of available histories */ 2229 miblen = __arraycount(mib); 2230 error = sysctlnametomib("kern.hist", mib, &miblen); 2231 if (error != 0) { 2232 if (errno == ENOENT) { 2233 warnx("kernel history is not compiled into the kernel."); 2234 return; 2235 } else 2236 err(EXIT_FAILURE, "nametomib failed"); 2237 } 2238 2239 /* get the list of nodenames below kern.hist */ 2240 mib[2] = CTL_QUERY; 2241 memset(&query, 0, sizeof(query)); 2242 query.sysctl_flags = SYSCTL_VERSION; 2243 len = sizeof(histnode); 2244 error = sysctl(mib, 3, &histnode[0], &len, &query, sizeof(query)); 2245 if (error != 0) { 2246 err(1, "query failed"); 2247 return; 2248 } 2249 if (len == 0) { 2250 warnx("No active kernel history logs."); 2251 return; 2252 } 2253 2254 len = len / sizeof(histnode[0]); /* get # of entries returned */ 2255 2256 if (todo & HISTLIST) 2257 (void)printf("Active kernel histories:"); 2258 2259 for (i = 0; i < len; i++) { 2260 if (todo & HISTLIST) 2261 (void)printf(" %s", histnode[i].sysctl_name); 2262 else { 2263 /* 2264 * If we're dumping all histories, do it, else 2265 * check to see if this is the one we want. 2266 */ 2267 if (histname == NULL || 2268 strcmp(histname, histnode[i].sysctl_name) == 0) { 2269 if (histname == NULL) 2270 (void)printf( 2271 "\nkernel history `%s':\n", 2272 histnode[i].sysctl_name); 2273 mib[2] = histnode[i].sysctl_num; 2274 mib[3] = CTL_EOL; 2275 hist_dodump_sysctl(mib, 4); 2276 } 2277 } 2278 } 2279 2280 if (todo & HISTLIST) 2281 (void)putchar('\n'); 2282 else if (mib[2] == CTL_QUERY) 2283 warnx("history %s not found", histname); 2284 } 2285 2286 /* 2287 * Actually dump the history buffer at the specified KVA. 2288 */ 2289void 2290hist_dodump_sysctl(int mib[], unsigned int miblen) 2291{ 2292 struct sysctl_history *hist; 2293 struct timeval tv; 2294 struct sysctl_history_event *e; 2295 size_t histsize; 2296 char *strp; 2297 unsigned i; 2298 char *fmt = NULL, *fn = NULL; 2299 2300 hist = NULL; 2301 histsize = 0; 2302 do { 2303 errno = 0; 2304 if (sysctl(mib, miblen, hist, &histsize, NULL, 0) == 0) 2305 break; 2306 if (errno != ENOMEM) 2307 break; 2308 if ((hist = realloc(hist, histsize)) == NULL) 2309 errx(1, "realloc history buffer"); 2310 } while (errno == ENOMEM); 2311 if (errno != 0) 2312 err(1, "sysctl failed"); 2313 2314 strp = (char *)(&hist->sh_events[hist->sh_numentries]); 2315 2316 (void)printf("%"PRIu32" entries, next is %"PRIu32"\n", 2317 hist->sh_numentries, 2318 hist->sh_nextfree); 2319 2320 i = hist->sh_nextfree; 2321 2322 do { 2323 e = &hist->sh_events[i]; 2324 if (e->she_fmtoffset != 0) { 2325 fmt = &strp[e->she_fmtoffset]; 2326 size_t fmtlen = strlen(fmt); 2327 for (unsigned z = 0; z < fmtlen - 1; z++) { 2328 if (fmt[z] == '%' && fmt[z+1] == 's') 2329 fmt[z+1] = 'p'; 2330 } 2331 fn = &strp[e->she_funcoffset]; 2332 bintime2timeval(&e->she_bintime, &tv); 2333 (void)printf("%06ld.%06ld %s#%"PRIu32"@%"PRIu32": ", 2334 (long int)tv.tv_sec, (long int)tv.tv_usec, 2335 fn, e->she_callnumber, e->she_cpunum); 2336 (void)printf(fmt, e->she_values[0], e->she_values[1], 2337 e->she_values[2], e->she_values[3]); 2338 (void)putchar('\n'); 2339 } 2340 i = (i + 1) % hist->sh_numentries; 2341 } while (i != hist->sh_nextfree); 2342 2343 free(hist); 2344 } 2345 2346static void 2347usage(void) 2348{ 2349 2350 (void)fprintf(stderr, 2351 "usage: %s [-CefHiLlmstUvW] [-c count] [-h hashname] [-M core] [-N system]\n" 2352 "\t\t[-u histname] [-w wait] [disks]\n", getprogname()); 2353 exit(1); 2354} 2355