/*- * Copyright (c) 2004-2005 Robert N M Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include static void usage(void) { fprintf(stderr, "waitstat attach pid\n"); fprintf(stderr, "waitstat dettach pid\n"); fprintf(stderr, "waitstat trace pid\n"); exit(-1); } static void waitstat_attach(pid_t pid) { if (sysctlbyname("kern.waitstat.register", NULL, NULL, &pid, sizeof(pid)) < 0) errx(-1, "sysctlbyname: kern.waitstat.register: %s", strerror(errno)); } static void waitstat_detach(pid_t pid) { if (sysctlbyname("kern.waitstat.deregister", NULL, NULL, &pid, sizeof(pid)) < 0) errx(-1, "sysctlbyname: kern.waitstat.register: %s", strerror(errno)); } #define BUFLEN 128 static int waitstat(pid_t pid, int *mib, int miblen) { struct waitstat_channel *wpc; struct waitstat_proc wp; struct timespec ts; struct bintime bt; char buf[BUFLEN]; int i, totallen; size_t len; printf("\n"); len = sizeof(wp); if (sysctl(mib, miblen, &wp, &len, NULL, 0) < 0) errx(-1, "waitstat: sysctl: %s", strerror(errno)); if (len != sizeof(wp)) errx(-1, "waitstat: sysctl: data wrong size (%d / %d)", len, sizeof(wp)); if (wp.wp_session_started.sec != 0) { totallen = wp.wp_session_current.sec - wp.wp_session_started.sec; printf("Process %d traced %ds waited %ds (%%%2.2f waited)\n", pid, totallen, wp.wp_waited_time.sec, 100 * (float)wp.wp_waited_time.sec / totallen); } else { totallen = 0; printf("Process %d\n", pid); } printf("%16s %10s %12s %12s %12s %6s\n", "Wait channel", "Count", "Total wait", "Average wait", "Max wait", "%Time"); for (i = 0; i < WAITSTAT_MAX_STATES; i++) { wpc = &wp.wp_stats[i]; if (wpc->wpc_wmesg[0] == '\0') break; /* wmesg */ printf("%16s ", wpc->wpc_wmesg); /* number of waits */ printf("%10d ", wpc->wpc_numwaits); /* total wait for the channel */ bt = wpc->wpc_totwait; bintime2timespec(&bt, &ts); snprintf(buf, 12, "%d.%09ld", ts.tv_sec, ts.tv_nsec); printf("%12s ", buf); /* average wait for the channel */ bt = wpc->wpc_totwait; bintime_divx(&bt, wpc->wpc_numwaits); bintime2timespec(&bt, &ts); snprintf(buf, 12, "%d.%09ld ", ts.tv_sec, ts.tv_nsec); printf("%12s ", buf); /* max wait for the channel */ bt = wpc->wpc_maxwait; bintime2timespec(&bt, &ts); snprintf(buf, 12, "%d.%09ld", ts.tv_sec, ts.tv_nsec); printf("%12s", buf); #if 0 /* %time */ bt = wpc->wpc_totwait; bintime_divx(&bt, wp.wp_waited_time.sec); /* bintime_mulx(&bt, 100); */ bintime2timespec(&bt, &ts); snprintf(buf, 6, "%d.%02ld", ts.tv_sec, ts.tv_nsec); printf("%6s", buf); #endif printf("\n"); } return (0); } int main(int argc, char *argv[]) { int mib[CTL_MAXNAME]; size_t miblen; char *dummy; pid_t pid; long l; if (argc != 3) usage(); /* * XXX: Possibly poor assumption about the scope of valid pid values. */ l = strtol(argv[2], &dummy, 10); if (l < 0 || l > 65535 || *dummy != '\0') usage(); pid = l; miblen = CTL_MAXNAME; if (sysctlnametomib("kern.waitstat.stats", mib, &miblen) == -1) { if (errno == ENOENT) { errno = EOPNOTSUPP; errx(-1, "WAITSTAT: %s", strerror(errno)); } errx(-1, "kern.waitstat.stats: %s", strerror(errno)); } if (miblen == CTL_MAXNAME) errx(-1, "sysctlnametomib: mib too large"); mib[miblen] = pid; miblen++; if (strcmp(argv[1], "attach") == 0) waitstat_attach(pid); else if (strcmp(argv[1], "detach") == 0) waitstat_detach(pid); else if (strcmp(argv[1], "trace") == 0 ){ while (1) { sleep(1); waitstat(pid, mib, miblen); } } else usage(); return (0); }