diff options
Diffstat (limited to 'usr.sbin/iostat/iostat.c')
| -rw-r--r-- | usr.sbin/iostat/iostat.c | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c new file mode 100644 index 000000000000..ec18f4fd35b3 --- /dev/null +++ b/usr.sbin/iostat/iostat.c @@ -0,0 +1,1028 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +/* + * Parts of this program are derived from the original FreeBSD iostat + * program: + */ +/*- + * Copyright (c) 1986, 1991, 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ +/* + * Ideas for the new iostat statistics output modes taken from the NetBSD + * version of iostat: + */ +/* + * Copyright (c) 1996 John M. Vinopal + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by John M. Vinopal. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/resource.h> +#include <sys/sysctl.h> + +#include <ctype.h> +#include <devstat.h> +#include <err.h> +#include <fcntl.h> +#include <inttypes.h> +#include <kvm.h> +#include <limits.h> +#include <math.h> +#include <nlist.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +static struct nlist namelist[] = { +#define X_TTY_NIN 0 + { .n_name = "_tty_nin", + .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 }, +#define X_TTY_NOUT 1 + { .n_name = "_tty_nout", + .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 }, +#define X_BOOTTIME 2 + { .n_name = "_boottime", + .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 }, +#define X_END 2 + { .n_name = NULL, + .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 }, +}; + +#define IOSTAT_DEFAULT_ROWS 20 /* Traditional default `wrows' */ + +static struct statinfo cur, last; +static int num_devices; +static struct device_selection *dev_select; +static int maxshowdevs; +static volatile sig_atomic_t headercount; +static volatile sig_atomic_t wresized; /* Tty resized, when non-zero. */ +static volatile sig_atomic_t alarm_rang; +static volatile sig_atomic_t return_requested; +static unsigned short wrows; /* Current number of tty rows. */ +static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0; +static int xflag = 0, zflag = 0; + +/* local function declarations */ +static void usage(void) __dead2; +static void needhdr(int signo); +static void needresize(int signo); +static void needreturn(int signo); +static void alarm_clock(int signo); +static void doresize(void); +static void phdr(void); +static void devstats(int perf_select, long double etime, int havelast); +static void cpustats(void); +static int readvar(kvm_t *kd, const char *name, int nlid, void *ptr, + size_t len); + +static void +usage(void) +{ + /* + * We also support the following 'traditional' syntax: + * iostat [drives] [wait [count]] + * This isn't mentioned in the man page, or the usage statement, + * but it is supported. + */ + fprintf(stderr, "usage: iostat [-CdhIKoTxz] [-c count] [-M core]" + " [-n devs] [-N system]\n" + "\t [-t type,if,pass] [-w wait] [drives]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + int c, i; + int hflag = 0, cflag = 0, wflag = 0, nflag = 0; + int count = 0, waittime = 0; + char *memf = NULL, *nlistf = NULL; + struct devstat_match *matches; + struct itimerval alarmspec; + int num_matches = 0; + char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = NULL; + long generation; + int num_devices_specified; + int num_selected, num_selections; + long select_generation; + char **specified_devices; + devstat_select_mode select_mode; + float f; + int havelast = 0; + + matches = NULL; + maxshowdevs = 3; + + while ((c = getopt(argc, argv, "Cc:dhIKM:N:n:oTt:w:xz")) != -1) { + switch (c) { + case 'C': + Cflag++; + break; + case 'c': + cflag++; + count = atoi(optarg); + if (count < 1) + errx(1, "count %d is < 1", count); + break; + case 'd': + dflag++; + break; + case 'h': + hflag++; + break; + case 'I': + Iflag++; + break; + case 'K': + Kflag++; + break; + case 'M': + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case 'n': + nflag++; + maxshowdevs = atoi(optarg); + if (maxshowdevs < 0) + errx(1, "number of devices %d is < 0", + maxshowdevs); + break; + case 'o': + oflag++; + break; + case 'T': + Tflag++; + break; + case 't': + if (devstat_buildmatch(optarg, &matches, + &num_matches) != 0) + errx(1, "%s", devstat_errbuf); + break; + case 'w': + wflag++; + f = atof(optarg); + waittime = f * 1000; + if (waittime < 1) + errx(1, "wait time is < 1ms"); + break; + case 'x': + xflag++; + break; + case 'z': + zflag++; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (nlistf != NULL || memf != NULL) { + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); + + if (kd == NULL) + errx(1, "kvm_openfiles: %s", errbuf); + + if (kvm_nlist(kd, namelist) == -1) + errx(1, "kvm_nlist: %s", kvm_geterr(kd)); + } + + /* + * Make sure that the userland devstat version matches the kernel + * devstat version. If not, exit and print a message informing + * the user of his mistake. + */ + if (devstat_checkversion(kd) < 0) + errx(1, "%s", devstat_errbuf); + + /* + * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is + * greater than 0, they may be 0 or non-zero. + */ + if (dflag == 0 && xflag == 0) { + Cflag = 1; + Tflag = 1; + } + + /* find out how many devices we have */ + if ((num_devices = devstat_getnumdevs(kd)) < 0) + err(1, "can't get number of devices"); + + /* + * Figure out how many devices we should display. + */ + if (nflag == 0) { + if (xflag > 0) + maxshowdevs = num_devices; + else if (oflag > 0) { + if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) + maxshowdevs = 5; + else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) + maxshowdevs = 5; + else + maxshowdevs = 4; + } else { + if ((dflag > 0) && (Cflag == 0)) + maxshowdevs = 4; + else + maxshowdevs = 3; + } + } + + cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); + if (cur.dinfo == NULL) + err(1, "calloc failed"); + + last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); + if (last.dinfo == NULL) + err(1, "calloc failed"); + + /* + * Grab all the devices. We don't look to see if the list has + * changed here, since it almost certainly has. We only look for + * errors. + */ + if (devstat_getdevs(kd, &cur) == -1) + errx(1, "%s", devstat_errbuf); + + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + + /* + * If the user specified any devices on the command line, see if + * they are in the list of devices we have now. + */ + specified_devices = (char **)malloc(sizeof(char *)); + if (specified_devices == NULL) + err(1, "malloc failed"); + + for (num_devices_specified = 0; *argv; ++argv) { + if (isdigit(**argv)) + break; + num_devices_specified++; + specified_devices = (char **)realloc(specified_devices, + sizeof(char *) * + num_devices_specified); + if (specified_devices == NULL) + err(1, "realloc failed"); + + specified_devices[num_devices_specified - 1] = *argv; + + } + if (nflag == 0 && maxshowdevs < num_devices_specified) + maxshowdevs = num_devices_specified; + + dev_select = NULL; + + if ((num_devices_specified == 0) && (num_matches == 0)) + select_mode = DS_SELECT_ADD; + else + select_mode = DS_SELECT_ONLY; + + /* + * At this point, selectdevs will almost surely indicate that the + * device list has changed, so we don't look for return values of 0 + * or 1. If we get back -1, though, there is an error. + */ + if (devstat_selectdevs(&dev_select, &num_selected, + &num_selections, &select_generation, generation, + cur.dinfo->devices, num_devices, matches, + num_matches, specified_devices, + num_devices_specified, select_mode, maxshowdevs, + hflag) == -1) + errx(1, "%s", devstat_errbuf); + + /* + * Look for the traditional wait time and count arguments. + */ + if (*argv) { + f = atof(*argv); + waittime = f * 1000; + + /* Let the user know he goofed, but keep going anyway */ + if (wflag != 0) + warnx("discarding previous wait interval, using" + " %g instead", waittime / 1000.0); + wflag++; + + if (*++argv) { + count = atoi(*argv); + if (cflag != 0) + warnx("discarding previous count, using %d" + " instead", count); + cflag++; + } else + count = -1; + } + + /* + * If the user specified a count, but not an interval, we default + * to an interval of 1 second. + */ + if ((wflag == 0) && (cflag > 0)) + waittime = 1 * 1000; + + /* + * If the user specified a wait time, but not a count, we want to + * go on ad infinitum. This can be redundant if the user uses the + * traditional method of specifying the wait, since in that case we + * already set count = -1 above. Oh well. + */ + if ((wflag > 0) && (cflag == 0)) + count = -1; + + bzero(cur.cp_time, sizeof(cur.cp_time)); + cur.tk_nout = 0; + cur.tk_nin = 0; + + /* + * Set the snap time to the system boot time (ie: zero), so the + * stats are calculated since system boot. + */ + cur.snap_time = 0; + + /* + * If the user stops the program (control-Z) and then resumes it, + * print out the header again. + */ + (void)signal(SIGCONT, needhdr); + + /* + * If our standard output is a tty, then install a SIGWINCH handler + * and set wresized so that our first iteration through the main + * iostat loop will peek at the terminal's current rows to find out + * how many lines can fit in a screenful of output. + */ + if (isatty(fileno(stdout)) != 0) { + wresized = 1; + (void)signal(SIGWINCH, needresize); + } else { + wresized = 0; + wrows = IOSTAT_DEFAULT_ROWS; + } + + /* + * Register a SIGINT handler so that we can print out final statistics + * when we get that signal + */ + (void)signal(SIGINT, needreturn); + + /* + * Register a SIGALRM handler to implement sleeps if the user uses the + * -c or -w options + */ + (void)signal(SIGALRM, alarm_clock); + alarmspec.it_interval.tv_sec = waittime / 1000; + alarmspec.it_interval.tv_usec = 1000 * (waittime % 1000); + alarmspec.it_value.tv_sec = waittime / 1000; + alarmspec.it_value.tv_usec = 1000 * (waittime % 1000); + setitimer(ITIMER_REAL, &alarmspec, NULL); + + for (headercount = 1;;) { + struct devinfo *tmp_dinfo; + long tmp; + long double etime; + sigset_t sigmask, oldsigmask; + + if (Tflag > 0) { + if ((readvar(kd, "kern.tty_nin", X_TTY_NIN, &cur.tk_nin, + sizeof(cur.tk_nin)) != 0) + || (readvar(kd, "kern.tty_nout", X_TTY_NOUT, + &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) { + Tflag = 0; + warnx("disabling TTY statistics"); + } + } + + if (Cflag > 0) { + if (kd == NULL) { + if (readvar(kd, "kern.cp_time", 0, + &cur.cp_time, sizeof(cur.cp_time)) != 0) + Cflag = 0; + } else { + if (kvm_getcptime(kd, cur.cp_time) < 0) { + warnx("kvm_getcptime: %s", + kvm_geterr(kd)); + Cflag = 0; + } + } + if (Cflag == 0) + warnx("disabling CPU time statistics"); + } + + if (!--headercount) { + phdr(); + if (wresized != 0) + doresize(); + headercount = wrows; + } + + tmp_dinfo = last.dinfo; + last.dinfo = cur.dinfo; + cur.dinfo = tmp_dinfo; + + last.snap_time = cur.snap_time; + + /* + * Here what we want to do is refresh our device stats. + * devstat_getdevs() returns 1 when the device list has changed. + * If the device list has changed, we want to go through + * the selection process again, in case a device that we + * were previously displaying has gone away. + */ + switch (devstat_getdevs(kd, &cur)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: { + int retval; + + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + retval = devstat_selectdevs(&dev_select, &num_selected, + &num_selections, + &select_generation, + generation, + cur.dinfo->devices, + num_devices, matches, + num_matches, + specified_devices, + num_devices_specified, + select_mode, maxshowdevs, + hflag); + switch(retval) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + phdr(); + if (wresized != 0) + doresize(); + headercount = wrows; + break; + default: + break; + } + break; + } + default: + break; + } + + /* + * We only want to re-select devices if we're in 'top' + * mode. This is the only mode where the devices selected + * could actually change. + */ + if (hflag > 0) { + int retval; + retval = devstat_selectdevs(&dev_select, &num_selected, + &num_selections, + &select_generation, + generation, + cur.dinfo->devices, + num_devices, matches, + num_matches, + specified_devices, + num_devices_specified, + select_mode, maxshowdevs, + hflag); + switch(retval) { + case -1: + errx(1,"%s", devstat_errbuf); + break; + case 1: + phdr(); + if (wresized != 0) + doresize(); + headercount = wrows; + break; + default: + break; + } + } + + if (Tflag > 0) { + tmp = cur.tk_nin; + cur.tk_nin -= last.tk_nin; + last.tk_nin = tmp; + tmp = cur.tk_nout; + cur.tk_nout -= last.tk_nout; + last.tk_nout = tmp; + } + + etime = cur.snap_time - last.snap_time; + + if (etime == 0.0) + etime = 1.0; + + for (i = 0; i < CPUSTATES; i++) { + tmp = cur.cp_time[i]; + cur.cp_time[i] -= last.cp_time[i]; + last.cp_time[i] = tmp; + } + + if (xflag == 0 && Tflag > 0) + printf("%4.0Lf %5.0Lf ", cur.tk_nin / etime, + cur.tk_nout / etime); + + devstats(hflag, etime, havelast); + + if (xflag == 0) { + if (Cflag > 0) + cpustats(); + + printf("\n"); + } + fflush(stdout); + + if ((count >= 0 && --count <= 0) || return_requested) + break; + + /* + * Use sigsuspend to safely sleep until either signal is + * received + */ + alarm_rang = 0; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGALRM); + sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask); + while (! (alarm_rang || return_requested) ) { + sigsuspend(&oldsigmask); + } + sigprocmask(SIG_UNBLOCK, &sigmask, NULL); + + havelast = 1; + } + + exit(0); +} + +/* + * Force a header to be prepended to the next output. + */ +void +needhdr(int signo __unused) +{ + + headercount = 1; +} + +/* + * When the terminal is resized, force an update of the maximum number of rows + * printed between each header repetition. Then force a new header to be + * prepended to the next output. + */ +void +needresize(int signo __unused) +{ + + wresized = 1; + headercount = 1; +} + +/* + * Record the alarm so the main loop can break its sleep + */ +void +alarm_clock(int signo __unused) +{ + alarm_rang = 1; +} + +/* + * Request that the main loop exit soon + */ +void +needreturn(int signo __unused) +{ + return_requested = 1; +} + +/* + * Update the global `wrows' count of terminal rows. + */ +void +doresize(void) +{ + int status; + struct winsize w; + + for (;;) { + status = ioctl(fileno(stdout), TIOCGWINSZ, &w); + if (status == -1 && errno == EINTR) + continue; + else if (status == -1) + err(1, "ioctl"); + if (w.ws_row > 3) + wrows = w.ws_row - 3; + else + wrows = IOSTAT_DEFAULT_ROWS; + break; + } + + /* + * Inhibit doresize() calls until we are rescheduled by SIGWINCH. + */ + wresized = 0; +} + +static void +phdr(void) +{ + int i, printed; + char devbuf[256]; + + /* + * If xflag is set, we need a per-loop header, not a page header, so + * just return. We'll print the header in devstats(). + */ + if (xflag > 0) + return; + + if (Tflag > 0) + (void)printf(" tty "); + for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ + int di; + if ((dev_select[i].selected != 0) + && (dev_select[i].selected <= maxshowdevs)) { + di = dev_select[i].position; + snprintf(devbuf, sizeof(devbuf), "%s%d", + cur.dinfo->devices[di].device_name, + cur.dinfo->devices[di].unit_number); + if (oflag > 0) + (void)printf("%13.6s ", devbuf); + else + printf("%16.6s ", devbuf); + printed++; + } + } + if (Cflag > 0) + (void)printf(" cpu\n"); + else + (void)printf("\n"); + + if (Tflag > 0) + (void)printf(" tin tout "); + + for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ + if ((dev_select[i].selected != 0) + && (dev_select[i].selected <= maxshowdevs)) { + if (oflag > 0) { + if (Iflag == 0) + (void)printf(" sps tps msps "); + else + (void)printf(" blk xfr msps "); + } else { + if (Iflag == 0) + printf("KB/t tps MB/s "); + else + printf("KB/t xfrs MB "); + } + printed++; + } + } + if (Cflag > 0) + (void)printf(" us ni sy in id\n"); + else + printf("\n"); + +} + +static void +devstats(int perf_select, long double etime, int havelast) +{ + int dn; + long double transfers_per_second, transfers_per_second_read; + long double transfers_per_second_write; + long double kb_per_transfer, mb_per_second, mb_per_second_read; + long double mb_per_second_write; + u_int64_t total_bytes, total_transfers, total_blocks; + u_int64_t total_bytes_read, total_transfers_read; + u_int64_t total_bytes_write, total_transfers_write; + long double busy_pct, busy_time; + u_int64_t queue_len; + long double total_mb, blocks_per_second, total_duration; + long double ms_per_other, ms_per_read, ms_per_write, ms_per_transaction; + int firstline = 1; + char *devicename; + + if (xflag > 0) { + if (Cflag > 0) { + printf(" cpu\n"); + printf(" us ni sy in id\n"); + cpustats(); + printf("\n"); + } + printf(" extended device statistics "); + if (Tflag > 0) + printf(" tty "); + printf("\n"); + if (Iflag == 0) { + printf("device r/s w/s kr/s kw/s " + " ms/r ms/w ms/o ms/t qlen %%b "); + } else { + printf("device r/i w/i kr/i" + " kw/i qlen tsvc_t/i sb/i "); + } + if (Tflag > 0) + printf("tin tout "); + printf("\n"); + } + + for (dn = 0; dn < num_devices; dn++) { + int di; + + if (((perf_select == 0) && (dev_select[dn].selected == 0)) + || (dev_select[dn].selected > maxshowdevs)) + continue; + + di = dev_select[dn].position; + + if (devstat_compute_statistics(&cur.dinfo->devices[di], + havelast ? &last.dinfo->devices[di] : NULL, etime, + DSM_TOTAL_BYTES, &total_bytes, + DSM_TOTAL_BYTES_READ, &total_bytes_read, + DSM_TOTAL_BYTES_WRITE, &total_bytes_write, + DSM_TOTAL_TRANSFERS, &total_transfers, + DSM_TOTAL_TRANSFERS_READ, &total_transfers_read, + DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write, + DSM_TOTAL_BLOCKS, &total_blocks, + DSM_KB_PER_TRANSFER, &kb_per_transfer, + DSM_TRANSFERS_PER_SECOND, &transfers_per_second, + DSM_TRANSFERS_PER_SECOND_READ, &transfers_per_second_read, + DSM_TRANSFERS_PER_SECOND_WRITE, &transfers_per_second_write, + DSM_MB_PER_SECOND, &mb_per_second, + DSM_MB_PER_SECOND_READ, &mb_per_second_read, + DSM_MB_PER_SECOND_WRITE, &mb_per_second_write, + DSM_BLOCKS_PER_SECOND, &blocks_per_second, + DSM_MS_PER_TRANSACTION, &ms_per_transaction, + DSM_MS_PER_TRANSACTION_READ, &ms_per_read, + DSM_MS_PER_TRANSACTION_WRITE, &ms_per_write, + DSM_MS_PER_TRANSACTION_OTHER, &ms_per_other, + DSM_BUSY_PCT, &busy_pct, + DSM_QUEUE_LENGTH, &queue_len, + DSM_TOTAL_DURATION, &total_duration, + DSM_TOTAL_BUSY_TIME, &busy_time, + DSM_NONE) != 0) + errx(1, "%s", devstat_errbuf); + + if (perf_select != 0) { + dev_select[dn].bytes = total_bytes; + if ((dev_select[dn].selected == 0) + || (dev_select[dn].selected > maxshowdevs)) + continue; + } + + if (Kflag > 0 || xflag > 0) { + int block_size = cur.dinfo->devices[di].block_size; + total_blocks = total_blocks * (block_size ? + block_size : 512) / 1024; + } + + if (xflag > 0) { + if (asprintf(&devicename, "%s%d", + cur.dinfo->devices[di].device_name, + cur.dinfo->devices[di].unit_number) == -1) + err(1, "asprintf"); + /* + * If zflag is set, skip any devices with zero I/O. + */ + if (zflag == 0 || transfers_per_second_read > 0.05 || + transfers_per_second_write > 0.05 || + mb_per_second_read > ((long double).0005)/1024 || + mb_per_second_write > ((long double).0005)/1024 || + busy_pct > 0.5) { + if (Iflag == 0) + printf("%-8.8s %7.0Lf %7.0Lf %8.1Lf " + "%8.1Lf %5.0Lf %5.0Lf %5.0Lf %5.0Lf" + " %4" PRIu64 " %3.0Lf ", + devicename, + transfers_per_second_read, + transfers_per_second_write, + mb_per_second_read * 1024, + mb_per_second_write * 1024, + ms_per_read, ms_per_write, + ms_per_other, + ms_per_transaction, + queue_len, busy_pct); + else + printf("%-8.8s %11.1Lf %11.1Lf " + "%12.1Lf %12.1Lf %4" PRIu64 + " %10.1Lf %9.1Lf ", + devicename, + (long double)total_transfers_read, + (long double)total_transfers_write, + (long double) + total_bytes_read / 1024, + (long double) + total_bytes_write / 1024, + queue_len, + total_duration, busy_time); + if (firstline) { + /* + * If this is the first device + * we're printing, also print + * CPU or TTY stats if requested. + */ + firstline = 0; + if (Tflag > 0) + printf("%4.0Lf%5.0Lf", + cur.tk_nin / etime, + cur.tk_nout / etime); + } + printf("\n"); + } + free(devicename); + } else if (oflag > 0) { + int msdig = (ms_per_transaction < 99.94) ? 1 : 0; + + if (Iflag == 0) + printf("%4.0Lf%4.0Lf%5.*Lf ", + blocks_per_second, + transfers_per_second, + msdig, + ms_per_transaction); + else + printf("%4" PRIu64 "%4" PRIu64 "%5.*Lf ", + total_blocks, + total_transfers, + msdig, + ms_per_transaction); + } else { + if (Iflag == 0) + printf("%4.*Lf %5.0Lf %5.*Lf ", + kb_per_transfer >= 100 ? 0 : 1, + kb_per_transfer, + transfers_per_second, + mb_per_second >= 1000 ? 0 : + (total_mb >= 100 ? 1 : 2), + mb_per_second); + else { + total_mb = total_bytes; + total_mb /= 1024 * 1024; + + printf("%4.*Lf %5" PRIu64 " %5.*Lf ", + kb_per_transfer >= 100 ? 0 : 1, + kb_per_transfer, + total_transfers, + total_mb >= 1000 ? 0 : + (total_mb >= 100 ? 1 : 2), + total_mb); + } + } + } + if (xflag > 0 && zflag > 0 && firstline == 1 && + (Tflag > 0 || Cflag > 0)) { + /* + * If zflag is set and we did not print any device + * lines I/O because they were all zero, + * print TTY/CPU stats. + */ + printf("%52s",""); + if (Tflag > 0) + printf("%4.0Lf %5.0Lf", cur.tk_nin / etime, + cur.tk_nout / etime); + if (Cflag > 0) + cpustats(); + printf("\n"); + } +} + +static void +cpustats(void) +{ + int state; + double cptime; + + cptime = 0.0; + + for (state = 0; state < CPUSTATES; ++state) + cptime += cur.cp_time[state]; + for (state = 0; state < CPUSTATES; ++state) + printf(" %2.0f", + rint(100. * cur.cp_time[state] / (cptime ? cptime : 1))); +} + +static int +readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len) +{ + if (kd != NULL) { + ssize_t nbytes; + + nbytes = kvm_read(kd, namelist[nlid].n_value, ptr, len); + + if (nbytes < 0) { + warnx("kvm_read(%s): %s", namelist[nlid].n_name, + kvm_geterr(kd)); + return (1); + } else if ((size_t)nbytes != len) { + warnx("kvm_read(%s): expected %zu bytes, got %zd bytes", + namelist[nlid].n_name, len, nbytes); + return (1); + } + } else { + size_t nlen = len; + + if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { + warn("sysctl(%s...) failed", name); + return (1); + } + if (nlen != len) { + warnx("sysctl(%s...): expected %lu, got %lu", name, + (unsigned long)len, (unsigned long)nlen); + return (1); + } + } + return (0); +} |
