aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/iostat/iostat.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/iostat/iostat.c')
-rw-r--r--usr.sbin/iostat/iostat.c1028
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);
+}