Module Name: src Committed By: matt Date: Sat Jan 29 18:10:08 UTC 2011
Modified Files: src/usr.bin/vmstat: vmstat.c Log Message: Defer nlist processing until we know we need to it. If everything can be obtained via sysctl, we can skip it entirely. This means we can run even if not setgid. getuptime will now use sysctl/clock_gettime if memf is NULL. doevcnt now sues sysctl(kern.evcnt) is memf is NULL. It falls back to groveling if sysctl returns an error of ENOENT. dointr will call doevcnt to evcnt based intr stats. To generate a diff of this commit: cvs rdiff -u -r1.175 -r1.176 src/usr.bin/vmstat/vmstat.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/vmstat/vmstat.c diff -u src/usr.bin/vmstat/vmstat.c:1.175 src/usr.bin/vmstat/vmstat.c:1.176 --- src/usr.bin/vmstat/vmstat.c:1.175 Sat Dec 25 23:36:59 2010 +++ src/usr.bin/vmstat/vmstat.c Sat Jan 29 18:10:08 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: vmstat.c,v 1.175 2010/12/25 23:36:59 christos Exp $ */ +/* $NetBSD: vmstat.c,v 1.176 2011/01/29 18:10:08 matt Exp $ */ /*- * Copyright (c) 1998, 2000, 2001, 2007 The NetBSD Foundation, Inc. @@ -70,7 +70,7 @@ #if 0 static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/95"; #else -__RCSID("$NetBSD: vmstat.c,v 1.175 2010/12/25 23:36:59 christos Exp $"); +__RCSID("$NetBSD: vmstat.c,v 1.176 2011/01/29 18:10:08 matt Exp $"); #endif #endif /* not lint */ @@ -298,7 +298,7 @@ void cpucounters(struct cpu_counter *); void deref_kptr(const void *, void *, size_t, const char *); void drvstats(int *); -void doevcnt(int verbose); +void doevcnt(int verbose, int type); void dohashstat(int, int, const char *); void dointr(int verbose); void domem(void); @@ -311,6 +311,7 @@ void kread(struct nlist *, int, void *, size_t); int kreadc(struct nlist *, int, void *, size_t); void needhdr(int); +void getnlist(int); long getuptime(void); void printhdr(void); long pct(long, long); @@ -331,6 +332,8 @@ static const int vmmeter_mib[] = { CTL_VM, VM_METER }; static const int uvmexp2_mib[] = { CTL_VM, VM_UVMEXP2 }; +static const int boottime_mib[] = { CTL_KERN, KERN_BOOTTIME }; +static char kvm_errbuf[_POSIX2_LINE_MAX]; int main(int argc, char *argv[]) @@ -338,10 +341,8 @@ int c, todo, verbose, wide; struct timespec interval; int reps; - char errbuf[_POSIX2_LINE_MAX]; gid_t egid = getegid(); const char *histname, *hashname; - size_t i; histname = hashname = NULL; (void)setegid(getgid()); @@ -430,39 +431,16 @@ else (void)setegid(egid); - kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); - if (kd == NULL) - errx(1, "kvm_openfiles: %s", errbuf); + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, kvm_errbuf); + if (kd == NULL) { + if (nlistf != NULL || memf != NULL) { + errx(1, "kvm_openfiles: %s", kvm_errbuf); + } + } if (nlistf == NULL && memf == NULL) (void)setgid(getgid()); - if ((c = kvm_nlist(kd, namelist)) != 0) { - int doexit = 0; - if (c == -1) - errx(1, "kvm_nlist: %s %s", "namelist", kvm_geterr(kd)); - for (i = 0; i < sizeof(namelist) / sizeof(namelist[0])-1; i++) - if (namelist[i].n_type == 0 && - i != X_TIME_SECOND && - i != X_TIME) { - if (doexit++ == 0) - (void)fprintf(stderr, - "%s: undefined symbols:", - getprogname()); - (void)fprintf(stderr, " %s", - namelist[i].n_name); - } - if (doexit) { - (void)fputc('\n', stderr); - exit(1); - } - } - if (todo & INTRSTAT) - (void) kvm_nlist(kd, intrnl); - if ((c = kvm_nlist(kd, hashnl)) == -1 || c == X_HASHNL_SIZE) - errx(1, "kvm_nlist: %s %s", "hashnl", kvm_geterr(kd)); - if (kvm_nlist(kd, histnl) == -1) - errx(1, "kvm_nlist: %s %s", "histnl", kvm_geterr(kd)); if (todo & VMSTAT) { struct winsize winsize; @@ -531,7 +509,7 @@ (void)putchar('\n'); } if (todo & EVCNTSTAT) { - doevcnt(verbose); + doevcnt(verbose, EVCNT_TYPE_ANY); (void)putchar('\n'); } if (todo & (HASHLIST|HASHSTAT)) { @@ -560,6 +538,59 @@ return 0; } +void +getnlist(int todo) +{ + static int namelist_done = 0; + static int hash_done = 0; + static int hist_done = 0; + static int intr_done = 0; + int c; + size_t i; + + if (kd == NULL) + errx(1, "kvm_openfiles: %s", kvm_errbuf); + + if (!namelist_done) { + namelist_done = 1; + if ((c = kvm_nlist(kd, namelist)) != 0) { + int doexit = 0; + if (c == -1) + errx(1, "kvm_nlist: %s %s", + "namelist", kvm_geterr(kd)); + for (i = 0; i < __arraycount(namelist)-1; i++) + if (namelist[i].n_type == 0 && + i != X_TIME_SECOND && + i != X_TIME) { + if (doexit++ == 0) + (void)fprintf(stderr, + "%s: undefined symbols:", + getprogname()); + (void)fprintf(stderr, " %s", + namelist[i].n_name); + } + if (doexit) { + (void)fputc('\n', stderr); + exit(1); + } + } + } + if ((todo & INTRSTAT) && !intr_done) { + intr_done = 1; + (void) kvm_nlist(kd, intrnl); + } + if ((todo & HASHLIST) && !hash_done) { + hash_done = 1; + if ((c = kvm_nlist(kd, hashnl)) == -1 || c == X_HASHNL_SIZE) + errx(1, "kvm_nlist: %s %s", "hashnl", kvm_geterr(kd)); + } + if ((todo & (HISTLIST|HISTDUMP)) && !hist_done) { + hist_done = 1; + if (kvm_nlist(kd, histnl) == -1) + errx(1, "kvm_nlist: %s %s", "histnl", kvm_geterr(kd)); + } +} + char ** choosedrives(char **argv) { @@ -598,21 +629,32 @@ long getuptime(void) { - static struct timeval boottime; - struct timeval now; + static struct timespec boottime; + struct timespec now; time_t uptime, nowsec; - if (boottime.tv_sec == 0) - kread(namelist, X_BOOTTIME, &boottime, sizeof(boottime)); - if (kreadc(namelist, X_TIME_SECOND, &nowsec, sizeof(nowsec))) { - /* - * XXX this assignment dance can be removed once timeval tv_sec - * is SUS mandated time_t - */ - now.tv_sec = nowsec; - now.tv_usec = 0; + if (memf == NULL) { + if (boottime.tv_sec == 0) { + size_t buflen = sizeof(boottime); + if (sysctl(boottime_mib, __arraycount(boottime_mib), + &boottime, &buflen, NULL, 0) == -1) + warn("Can't get boottime"); + } + clock_gettime(CLOCK_REALTIME, &now); } else { - kread(namelist, X_TIME, &now, sizeof(now)); + if (boottime.tv_sec == 0) + kread(namelist, X_BOOTTIME, &boottime, + sizeof(boottime)); + if (kreadc(namelist, X_TIME_SECOND, &nowsec, sizeof(nowsec))) { + /* + * XXX this assignment dance can be removed once + * timeval tv_sec is SUS mandated time_t + */ + now.tv_sec = nowsec; + now.tv_nsec = 0; + } else { + kread(namelist, X_TIME, &now, sizeof(now)); + } } uptime = now.tv_sec - boottime.tv_sec; if (uptime <= 0 || uptime > 60*60*24*365*10) @@ -1049,10 +1091,8 @@ unsigned long long inttotal, uptime; int nintr, inamlen; char *intrname, *ointrname; - struct evcntlist allevents; - struct evcnt evcnt, *evptr; - char evgroup[EVCNT_STRING_MAX], evname[EVCNT_STRING_MAX]; + getnlist(INTRSTAT); inttotal = 0; uptime = getuptime(); (void)printf("%-34s %16s %8s\n", "interrupt", "total", "rate"); @@ -1079,46 +1119,78 @@ free(ointrname); } - kread(namelist, X_ALLEVENTS, &allevents, sizeof allevents); - evptr = TAILQ_FIRST(&allevents); - while (evptr) { - deref_kptr(evptr, &evcnt, sizeof(evcnt), "event chain trashed"); - evptr = TAILQ_NEXT(&evcnt, ev_list); - if (evcnt.ev_type != EVCNT_TYPE_INTR) - continue; - - if (evcnt.ev_count == 0 && !verbose) - continue; - - deref_kptr(evcnt.ev_group, evgroup, - (size_t)evcnt.ev_grouplen + 1, "event chain trashed"); - deref_kptr(evcnt.ev_name, evname, - (size_t)evcnt.ev_namelen + 1, "event chain trashed"); - - (void)printf("%s %s%*s %16llu %8llu\n", evgroup, evname, - 34 - (evcnt.ev_grouplen + 1 + evcnt.ev_namelen), "", - (unsigned long long)evcnt.ev_count, - (unsigned long long)(evcnt.ev_count / uptime)); - - inttotal += evcnt.ev_count++; - } - (void)printf("%-34s %16llu %8llu\n", "Total", inttotal, - (unsigned long long)(inttotal / uptime)); + doevcnt(verbose, EVCNT_TYPE_INTR); } void -doevcnt(int verbose) +doevcnt(int verbose, int type) { - static const char * evtypes [] = { "misc", "intr", "trap" }; - unsigned long long uptime; + static const char * const evtypes [] = { "misc", "intr", "trap" }; + uint64_t counttotal, uptime; struct evcntlist allevents; struct evcnt evcnt, *evptr; char evgroup[EVCNT_STRING_MAX], evname[EVCNT_STRING_MAX]; - /* XXX should print type! */ - + counttotal = 0; uptime = getuptime(); (void)printf("%-34s %16s %8s %s\n", "event", "total", "rate", "type"); + + if (memf == NULL) do { + const int mib[4] = { CTL_KERN, KERN_EVCNT, type, + verbose ? KERN_EVCNT_COUNT_ANY : KERN_EVCNT_COUNT_NONZERO }; + size_t buflen = 0; + void *buf = NULL; + const struct evcnt_sysctl *evs, *last_evs; + for (;;) { + size_t newlen; + int error; + if (buflen) + buf = malloc(buflen); + error = sysctl(mib, __arraycount(mib), + buf, &newlen, NULL, 0); + if (error) { + /* if the sysctl is unknown, try groveling */ + if (error == ENOENT) + break; + perror("sysctl: kern.evcnt"); + if (buf) + free(buf); + return; + } + if (newlen <= buflen) { + buflen = newlen; + break; + } + if (buf) + free(buf); + buflen = newlen; + } + evs = buf; + last_evs = (void *)((char *)buf + buflen); + buflen /= sizeof(uint64_t); + while (evs < last_evs + && buflen >= sizeof(*evs)/sizeof(uint64_t) + && buflen >= evs->ev_len) { + (void)printf("%s %s%*s %16"PRIu64" %8"PRIu64" %s\n", + evs->ev_strings, + evs->ev_strings + evs->ev_grouplen + 1, + 34 - (evs->ev_grouplen + 1 + evs->ev_namelen), "", + evs->ev_count, + evs->ev_count / uptime, + (evs->ev_type < __arraycount(evtypes) ? + evtypes[evs->ev_type] : "?")); + buflen -= evs->ev_len; + evs = (const void *)((const uint64_t *)evs + evs->ev_len); + counttotal += evcnt.ev_count++; + } + free(buf); + if (type != EVCNT_TYPE_ANY) + (void)printf("%-34s %16"PRIu64" %8"PRIu64"\n", + "Total", counttotal, counttotal / uptime); + return; + } while (/*CONSTCOND*/ 0); + + getnlist(EVCNTSTAT); kread(namelist, X_ALLEVENTS, &allevents, sizeof allevents); evptr = TAILQ_FIRST(&allevents); while (evptr) { @@ -1127,19 +1199,27 @@ evptr = TAILQ_NEXT(&evcnt, ev_list); if (evcnt.ev_count == 0 && !verbose) continue; + if (type != EVCNT_TYPE_ANY && evcnt.ev_type != type) + continue; deref_kptr(evcnt.ev_group, evgroup, (size_t)evcnt.ev_grouplen + 1, "event chain trashed"); deref_kptr(evcnt.ev_name, evname, (size_t)evcnt.ev_namelen + 1, "event chain trashed"); - (void)printf("%s %s%*s %16llu %8llu %s\n", evgroup, evname, + (void)printf("%s %s%*s %16"PRIu64" %8"PRIu64" %s\n", + evgroup, evname, 34 - (evcnt.ev_grouplen + 1 + evcnt.ev_namelen), "", - (unsigned long long)evcnt.ev_count, - (unsigned long long)(evcnt.ev_count / uptime), - (evcnt.ev_type < sizeof(evtypes)/sizeof(evtypes[0]) ? + evcnt.ev_count, + (evcnt.ev_count / uptime), + (evcnt.ev_type < __arraycount(evtypes) ? evtypes[evcnt.ev_type] : "?")); + + counttotal += evcnt.ev_count++; } + if (type != EVCNT_TYPE_ANY) + (void)printf("%-34s %16"PRIu64" %8"PRIu64"\n", + "Total", counttotal, counttotal / uptime); } static char memname[64]; @@ -1154,6 +1234,7 @@ long totuse = 0, totfree = 0, totreq = 0; struct kmembuckets buckets[MINBUCKET + 16]; + getnlist(MEMSTAT); kread(namelist, X_KMEMBUCKETS, buckets, sizeof(buckets)); for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) { @@ -1266,6 +1347,7 @@ struct pool_allocator pa; char name[32], maxp[32]; + getnlist(MEMSTAT); kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head)); addr = TAILQ_FIRST(&pool_head); @@ -1376,6 +1458,7 @@ size_t i; double p; + getnlist(POOLCACHESTAT); kread(namelist, X_POOLHEAD, &pool_head, sizeof(pool_head)); addr = TAILQ_FIRST(&pool_head); @@ -1500,6 +1583,7 @@ hashbuf = NULL; hashbufsize = 0; + getnlist(todo); if (todo & HASHLIST) { (void)printf("Supported hashes:\n"); for (curhash = khashes; curhash->description; curhash++) { @@ -1670,6 +1754,7 @@ char *name = NULL; size_t namelen = 0; + getnlist(todo); if (histnl[0].n_value == 0) { warnx("UVM history is not compiled into the kernel."); return;