From e8eb724f936dcf65365a97b6e6fe8cbfadaecf2f Mon Sep 17 00:00:00 2001 From: gibbs Date: Tue, 15 Sep 1998 08:16:45 +0000 Subject: [PATCH] Update system to new device statistics code. Submitted by: "Kenneth D. Merry" --- usr.bin/systat/devs.c | 320 ++++++++++++++++ usr.bin/systat/extern.h | 7 + usr.bin/systat/iostat.c | 253 +++++++------ usr.bin/systat/systat.1 | 40 +- usr.bin/systat/vmstat.c | 270 +++++++++----- usr.bin/vmstat/Makefile | 5 +- usr.bin/vmstat/vmstat.8 | 97 ++++- usr.bin/vmstat/vmstat.c | 319 ++++++++++------ usr.sbin/iostat/Makefile | 7 +- usr.sbin/iostat/iostat.8 | 281 +++++++++++++- usr.sbin/iostat/iostat.c | 781 ++++++++++++++++++++++++++------------- 11 files changed, 1767 insertions(+), 613 deletions(-) create mode 100644 usr.bin/systat/devs.c diff --git a/usr.bin/systat/devs.c b/usr.bin/systat/devs.c new file mode 100644 index 000000000000..42cc88ae7ea0 --- /dev/null +++ b/usr.bin/systat/devs.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 1998 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. + * + * $Id$ + */ +/* + * Some code and ideas taken from the old disks.c. + * static char sccsid[] = "@(#)disks.c 8.1 (Berkeley) 6/6/93"; + */ +/*- + * Copyright (c) 1980, 1992, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + */ + +#include +#include +#include + +#include +#include +#include "systat.h" +#include "extern.h" + +typedef enum { + DS_MATCHTYPE_NONE, + DS_MATCHTYPE_SPEC, + DS_MATCHTYPE_PATTERN +} last_match_type; + +last_match_type last_type; +struct device_selection *dev_select; +int generation, num_devices, num_selected; +int num_selections, select_generation; +struct devstat_match *matches = NULL; +int num_matches = 0; +char **specified_devices; +int num_devices_specified = 0; + +static int dsmatchselect(char *args, devstat_select_mode select_mode, + int maxshowdevs, struct statinfo *s1); +static int dsselect(char *args, devstat_select_mode select_mode, + int maxshowdevs, struct statinfo *s1); + +int +dsinit(int maxshowdevs, struct statinfo *s1, struct statinfo *s2, + struct statinfo *s3) +{ + + /* + * 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 (checkversion() < 0) + errx(1, "%s", devstat_errbuf); + + generation = 0; + num_devices = 0; + num_selected = 0; + num_selections = 0; + select_generation = 0; + last_type = DS_MATCHTYPE_NONE; + + if (getdevs(s1) == -1) + errx(1, "%s", devstat_errbuf); + + num_devices = s1->dinfo->numdevs; + generation = s1->dinfo->generation; + + dev_select = NULL; + + /* + * 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 (selectdevs(&dev_select, &num_selected, &num_selections, + &select_generation, generation, s1->dinfo->devices, + num_devices, NULL, 0, NULL, 0, DS_SELECT_ADD, + maxshowdevs, 0) == -1) + errx(1, "%s", devstat_errbuf); + + return(1); +} + +int +dscmd(char *cmd, char *args, int maxshowdevs, struct statinfo *s1) +{ + int retval; + + if (prefix(cmd, "display") || prefix(cmd, "add")) + return(dsselect(args, DS_SELECT_ADDONLY, maxshowdevs, s1)); + if (prefix(cmd, "ignore") || prefix(cmd, "delete")) + return(dsselect(args, DS_SELECT_REMOVE, maxshowdevs, s1)); + if (prefix(cmd, "show") || prefix(cmd, "only")) + return(dsselect(args, DS_SELECT_ONLY, maxshowdevs, s1)); + if (prefix(cmd, "type") || prefix(cmd, "match")) + return(dsmatchselect(args, DS_SELECT_ONLY, maxshowdevs, s1)); + if (prefix(cmd, "refresh")) { + retval = selectdevs(&dev_select, &num_selected, &num_selections, + &select_generation, generation, + s1->dinfo->devices, num_devices, + (last_type == DS_MATCHTYPE_PATTERN) ? + matches : NULL, + (last_type == DS_MATCHTYPE_PATTERN) ? + num_matches : 0, + (last_type == DS_MATCHTYPE_SPEC) ? + specified_devices : NULL, + (last_type == DS_MATCHTYPE_SPEC) ? + num_devices_specified : 0, + (last_type == DS_MATCHTYPE_NONE) ? + DS_SELECT_ADD : DS_SELECT_ADDONLY, + maxshowdevs, 0); + if (retval == -1) { + warnx("%s", devstat_errbuf); + return(0); + } else if (retval == 1) + return(2); + } + if (prefix(cmd, "drives")) { + register int i; + move(CMDLINE, 0); + clrtoeol(); + for (i = 0; i < num_devices; i++) { + printw("%s%d ", s1->dinfo->devices[i].device_name, + s1->dinfo->devices[i].unit_number); + } + return(1); + } + return(0); +} + +static int +dsmatchselect(char *args, devstat_select_mode select_mode, int maxshowdevs, + struct statinfo *s1) +{ + char **tempstr; + char *tstr[100]; + char *err_str; + int num_args = 0; + register int i; + int retval = 0; + + /* + * Break the (pipe delimited) input string out into separate + * strings. + */ + for (tempstr = tstr, num_args = 0; + (*tempstr = strsep(&args, "|")) != NULL && (num_args < 100); + num_args++) + if (**tempstr != '\0') + if (++tempstr >= &tstr[100]) + break; + + if (num_args > 99) { + warnx("dsmatchselect: too many match arguments"); + return(0); + } + + /* + * If we've gone through the matching code before, clean out + * previously used memory. + */ + if (num_matches > 0) { + free(matches); + matches = NULL; + num_matches = 0; + } + + for (i = 0; i < num_args; i++) { + if (buildmatch(tstr[i], &matches, &num_matches) != 0) { + warnx("%s", devstat_errbuf); + return(0); + } + } + if (num_args > 0) { + + last_type = DS_MATCHTYPE_PATTERN; + + retval = selectdevs(&dev_select, &num_selected, &num_selections, + &select_generation, generation, + s1->dinfo->devices, num_devices, matches, + num_matches, NULL, 0, select_mode, + maxshowdevs, 0); + if (retval == -1) + err(1, "device selection error"); + else if (retval == 1) + return(2); + } + return(1); +} + +static int +dsselect(char *args, devstat_select_mode select_mode, int maxshowdevs, + struct statinfo *s1) +{ + register char *cp; + register int i; + int retval = 0; + char *index(); + + /* + * If we've gone through this code before, free previously + * allocated resources. + */ + if (num_devices_specified > 0) { + for (i = 0; i < num_devices_specified; i++) + free(specified_devices[i]); + free(specified_devices); + specified_devices = NULL; + num_devices_specified = 0; + } + + /* do an initial malloc */ + specified_devices = (char **)malloc(sizeof(char *)); + + cp = index(args, '\n'); + if (cp) + *cp = '\0'; + for (;;) { + for (cp = args; *cp && isspace(*cp); cp++) + ; + args = cp; + for (; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (cp - args == 0) + break; + for (i = 0; i < num_devices; i++) { + char tmpstr[80]; + + sprintf(tmpstr, "%s%d", dev_select[i].device_name, + dev_select[i].unit_number); + if (strcmp(args, tmpstr) == 0) { + + num_devices_specified++; + + specified_devices =(char **)realloc( + specified_devices, + sizeof(char *) * + num_devices_specified); + specified_devices[num_devices_specified -1]= + strdup(args); + + break; + } + } + if (i >= num_devices) + error("%s: unknown drive", args); + args = cp; + } + + if (num_devices_specified > 0) { + last_type = DS_MATCHTYPE_SPEC; + + retval = selectdevs(&dev_select, &num_selected, &num_selections, + &select_generation, generation, + s1->dinfo->devices, num_devices, NULL, 0, + specified_devices, num_devices_specified, + select_mode, maxshowdevs, 0); + if (retval == -1) + err(1, "%s", devstat_errbuf); + else if (retval == 1) + return(2); + } + return(1); +} diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h index c6d581295f6a..3b44cd965b4d 100644 --- a/usr.bin/systat/extern.h +++ b/usr.bin/systat/extern.h @@ -60,6 +60,13 @@ extern int verbose; struct inpcb; +extern struct device_selection *dev_select; +extern int generation; +extern int num_devices; +extern int num_selected; +extern int num_selections; +extern int select_generation; + int checkhost __P((struct inpcb *)); int checkport __P((struct inpcb *)); void closeiostat __P((WINDOW *)); diff --git a/usr.bin/systat/iostat.c b/usr.bin/systat/iostat.c index b2bd40d511f0..5e6112cfd3c0 100644 --- a/usr.bin/systat/iostat.c +++ b/usr.bin/systat/iostat.c @@ -1,3 +1,32 @@ +/* + * Copyright (c) 1998 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. + * + * $Id$ + */ /* * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -36,28 +65,19 @@ static char sccsid[] = "@(#)iostat.c 8.1 (Berkeley) 6/6/93"; #endif not lint #include -#include #include +#include #include #include #include #include +#include #include "systat.h" #include "extern.h" static struct nlist namelist[] = { -#define X_DK_BUSY 0 - { "_dk_busy" }, -#define X_DK_TIME 1 - { "_dk_time" }, -#define X_DK_XFER 2 - { "_dk_xfer" }, -#define X_DK_WDS 3 - { "_dk_wds" }, -#define X_DK_SEEK 4 - { "_dk_seek" }, -#define X_CP_TIME 5 +#define X_CP_TIME 0 { "_cp_time" }, #ifdef vax #define X_MBDINIT (X_CP_TIME+1) @@ -72,27 +92,20 @@ static struct nlist namelist[] = { { "" }, }; -static struct { - int dk_busy; - long cp_time[CPUSTATES]; - long *dk_time; - long *dk_wds; - long *dk_seek; - long *dk_xfer; -} s, s1; +struct statinfo cur, last; static int linesperregion; static double etime; static int numbers = 0; /* default display bar graphs */ -static int msps = 0; /* default ms/seek shown */ +static int kbpt = 0; /* default ms/seek shown */ static int barlabels __P((int)); -static void histogram __P((double, int, double)); +static void histogram __P((long double, int, double)); static int numlabels __P((int)); +static int devstats __P((int, int, int)); static int stats __P((int, int, int)); static void stat1 __P((int, int)); - WINDOW * openiostat() { @@ -113,42 +126,61 @@ closeiostat(w) int initiostat() { - if (namelist[X_DK_BUSY].n_type == 0) { - if (kvm_nlist(kd, namelist)) { - nlisterr(namelist); - return(0); - } - if (namelist[X_DK_BUSY].n_type == 0) { - error("Disk init information isn't in namelist"); - return(0); - } - } - if (! dkinit()) + if (num_devices = getnumdevs() < 0) + return(0); + + cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + bzero(cur.dinfo, sizeof(struct devinfo)); + bzero(last.dinfo, sizeof(struct devinfo)); + + /* + * This value for maxshowdevs (100) is bogus. I'm not sure exactly + * how to calculate it, though. + */ + if (dsinit(100, &cur, &last, NULL) != 1) + return(0); + + if (kvm_nlist(kd, namelist)) { + nlisterr(namelist); return(0); - if (dk_ndrive) { -#define allocate(e, t) \ - s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \ - s1./**/e = (t *)calloc(dk_ndrive, sizeof (t)); - allocate(dk_time, long); - allocate(dk_wds, long); - allocate(dk_seek, long); - allocate(dk_xfer, long); -#undef allocate } + return(1); } void fetchiostat() { - if (namelist[X_DK_BUSY].n_type == 0) - return; - NREAD(X_DK_BUSY, &s.dk_busy, sizeof(s.dk_busy)); - NREAD(X_DK_TIME, s.dk_time, dk_ndrive * LONG); - NREAD(X_DK_XFER, s.dk_xfer, dk_ndrive * LONG); - NREAD(X_DK_WDS, s.dk_wds, dk_ndrive * LONG); - NREAD(X_DK_SEEK, s.dk_seek, dk_ndrive * LONG); - NREAD(X_CP_TIME, s.cp_time, sizeof s.cp_time); + struct devinfo *tmp_dinfo; + + NREAD(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time)); + tmp_dinfo = last.dinfo; + last.dinfo = cur.dinfo; + cur.dinfo = tmp_dinfo; + + last.busy_time = cur.busy_time; + + /* + * Here what we want to do is refresh our device stats. + * 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 (getdevs(&cur)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + cmdiostat("refresh", NULL); + break; + default: + break; + } + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + } #define INSET 10 @@ -158,10 +190,6 @@ labeliostat() { int row; - if (namelist[X_DK_BUSY].n_type == 0) { - error("No dk_busy defined."); - return; - } row = 0; wmove(wnd, row, 0); wclrtobot(wnd); mvwaddstr(wnd, row++, INSET, @@ -182,11 +210,12 @@ numlabels(row) int row; { int i, col, regions, ndrives; + char tmpstr[10]; -#define COLWIDTH 14 +#define COLWIDTH 17 #define DRIVESPERLINE ((wnd->maxx - INSET) / COLWIDTH) - for (ndrives = 0, i = 0; i < dk_ndrive; i++) - if (dk_select[i]) + for (ndrives = 0, i = 0; i < num_devices; i++) + if (dev_select[i].selected) ndrives++; regions = howmany(ndrives, DRIVESPERLINE); /* @@ -200,15 +229,17 @@ numlabels(row) if (linesperregion < 3) linesperregion = 3; col = INSET; - for (i = 0; i < dk_ndrive; i++) - if (dk_select[i] && dk_mspw[i] != 0.0) { + for (i = 0; i < num_devices; i++) + if (dev_select[i].selected) { if (col + COLWIDTH >= wnd->maxx - INSET) { col = INSET, row += linesperregion + 1; if (row > wnd->maxy - (linesperregion + 1)) break; } - mvwaddstr(wnd, row, col + 4, dr_name[i]); - mvwaddstr(wnd, row + 1, col, "bps tps msps"); + sprintf(tmpstr, "%s%d", dev_select[i].device_name, + dev_select[i].unit_number); + mvwaddstr(wnd, row, col + 4, tmpstr); + mvwaddstr(wnd, row + 1, col, " KB/t tps MB/s "); col += COLWIDTH; } if (col) @@ -221,18 +252,22 @@ barlabels(row) int row; { int i; + char tmpstr[10]; mvwaddstr(wnd, row++, INSET, "/0 /5 /10 /15 /20 /25 /30 /35 /40 /45 /50"); - linesperregion = 2 + msps; - for (i = 0; i < dk_ndrive; i++) - if (dk_select[i] && dk_mspw[i] != 0.0) { + linesperregion = 2 + kbpt; + for (i = 0; i < num_devices; i++) + if (dev_select[i].selected) { if (row > wnd->maxy - linesperregion) break; - mvwprintw(wnd, row++, 0, "%-4.4s bps|", dr_name[i]); + sprintf(tmpstr, "%s%d", dev_select[i].device_name, + dev_select[i].unit_number); + mvwprintw(wnd, row++, 0, "%-5.5s MB/s|", + tmpstr); mvwaddstr(wnd, row++, 0, " tps|"); - if (msps) - mvwaddstr(wnd, row++, 0, " msps|"); + if (kbpt) + mvwaddstr(wnd, row++, 0, " KB/t|"); } return (row); } @@ -244,16 +279,11 @@ showiostat() register long t; register int i, row, col; - if (namelist[X_DK_BUSY].n_type == 0) - return; - for (i = 0; i < dk_ndrive; i++) { -#define X(fld) t = s.fld[i]; s.fld[i] -= s1.fld[i]; s1.fld[i] = t - X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time); - } +#define X(fld) t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t etime = 0; for(i = 0; i < CPUSTATES; i++) { X(cp_time); - etime += s.cp_time[i]; + etime += cur.cp_time[i]; } if (etime == 0.0) etime = 1.0; @@ -263,11 +293,11 @@ showiostat() stat1(row++, i); if (!numbers) { row += 2; - for (i = 0; i < dk_ndrive; i++) - if (dk_select[i] && dk_mspw[i] != 0.0) { + for (i = 0; i < num_devices; i++) + if (dev_select[i].selected) { if (row > wnd->maxy - linesperregion) break; - row = stats(row, INSET, i); + row = devstats(row, INSET, i); } return; } @@ -276,8 +306,8 @@ showiostat() wdeleteln(wnd); wmove(wnd, row + 3, 0); winsertln(wnd); - for (i = 0; i < dk_ndrive; i++) - if (dk_select[i] && dk_mspw[i] != 0.0) { + for (i = 0; i < num_devices; i++) + if (dev_select[i].selected) { if (col + COLWIDTH >= wnd->maxx - INSET) { col = INSET, row += linesperregion + 1; if (row > wnd->maxy - (linesperregion + 1)) @@ -287,42 +317,47 @@ showiostat() wmove(wnd, row + 3, 0); winsertln(wnd); } - (void) stats(row + 3, col, i); + (void) devstats(row + 3, col, i); col += COLWIDTH; } } static int -stats(row, col, dn) +devstats(row, col, dn) int row, col, dn; { - double atime, words, xtime, itime; + long double transfers_per_second; + long double kb_per_transfer, mb_per_second; + long double busy_seconds; + int di; + + di = dev_select[dn].position; + + busy_seconds = compute_etime(cur.busy_time, last.busy_time); + + if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di], + busy_seconds, NULL, NULL, NULL, + &kb_per_transfer, &transfers_per_second, + &mb_per_second, NULL, NULL) != 0) + errx(1, "%s", devstat_errbuf); - atime = s.dk_time[dn]; - atime /= hertz; - words = s.dk_wds[dn]*32.0; /* number of words transferred */ - xtime = dk_mspw[dn]*words; /* transfer time */ - itime = atime - xtime; /* time not transferring */ - if (xtime < 0) - itime += xtime, xtime = 0; - if (itime < 0) - xtime += itime, itime = 0; if (numbers) { - mvwprintw(wnd, row, col, "%3.0f%4.0f%5.1f", - words / 512 / etime, s.dk_xfer[dn] / etime, - s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0.0); - return (row); + mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ", + kb_per_transfer, transfers_per_second, + mb_per_second); + return(row); } wmove(wnd, row++, col); - histogram(words / 512 / etime, 50, 1.0); + histogram(mb_per_second, 50, 1.0); wmove(wnd, row++, col); - histogram(s.dk_xfer[dn] / etime, 50, 1.0); - if (msps) { + histogram(transfers_per_second, 50, 1.0); + if (kbpt) { wmove(wnd, row++, col); - histogram(s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0, - 50, 1.0); + histogram(kb_per_transfer, 50, 1.0); } - return (row); + + return(row); + } static void @@ -334,17 +369,17 @@ stat1(row, o) time = 0; for (i = 0; i < CPUSTATES; i++) - time += s.cp_time[i]; + time += cur.cp_time[i]; if (time == 0.0) time = 1.0; wmove(wnd, row, INSET); #define CPUSCALE 0.5 - histogram(100.0 * s.cp_time[o] / time, 50, CPUSCALE); + histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE); } static void histogram(val, colwidth, scale) - double val; + long double val; int colwidth; double scale; { @@ -354,7 +389,7 @@ histogram(val, colwidth, scale) k = MIN(v, colwidth); if (v > colwidth) { - snprintf(buf, sizeof(buf), "%4.1f", val); + snprintf(buf, sizeof(buf), "%5.2Lf", val); k -= strlen(buf); while (k--) waddch(wnd, 'X'); @@ -371,13 +406,13 @@ cmdiostat(cmd, args) char *cmd, *args; { - if (prefix(cmd, "msps")) - msps = !msps; + if (prefix(cmd, "kbpt")) + kbpt = !kbpt; else if (prefix(cmd, "numbers")) numbers = 1; else if (prefix(cmd, "bars")) numbers = 0; - else if (!dkcmd(cmd, args)) + else if (!dscmd(cmd, args, 100, &cur)) return (0); wclear(wnd); labeliostat(); diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1 index 4ee5e2dc0a5f..b035eb98fd18 100644 --- a/usr.bin/systat/systat.1 +++ b/usr.bin/systat/systat.1 @@ -217,10 +217,10 @@ bar graphs of the amount of time executing in user mode (``user''), in user mode running low priority processes (``nice''), in system mode (``system''), in interrupt mode (``interrupt''), and idle (``idle''). Statistics -on disk throughput show, for each drive, kilobytes of data transferred, -number of disk transactions performed, and average seek time -(in milliseconds). This information may be displayed as -bar graphs or as rows of numbers which scroll downward. Bar +on disk throughput show, for each drive, megabytes per second, +average number of disk transactions per second, and +average kilobytes of data per transaction. This information may be +displayed as bar graphs or as rows of numbers which scroll downward. Bar graphs are shown by default. .Pp The following commands are specific to the @@ -237,9 +237,9 @@ displayed in numeric columns which scroll downward. Show the disk .Tn I/O statistics in bar graph form (default). -.It Cm msps -Toggle the display of average seek time (the default is to -not display seek times). +.It Cm kbpt +Toggle the display of kilobytes per transaction. (the default is to +not display kilobytes per transaction). .El .It Ic swap Show information about swap space usage on all the @@ -452,6 +452,32 @@ drives may be specified, separated by spaces. .It Cm display Op Ar drives Display information about the drives indicated. Multiple drives may be specified, separated by spaces. +.It Cm only Op Ar drives +Display only the specified drives. Multiple drives may be specified, +separated by spaces. +.It Cm drives +Display a list of available devices. +.It Cm match Ar type,if,pass Op Ar | ... +Display devivces matching the given pattern. The basic matching +expressions are the same as those used in +.Xr iostat 8 +with one difference. Instead of specifying multiple +.Fl t +arguments which are then ORed together, the user instead specifys multiple +matching expressions joined by the pipe ( | ) character. The comma +separated arguments within each matching expression are ANDed together, and +then the pipe separated matching expressions are ORed together. Any +device matching the combined expression will be displayed, if there is room +to display it. For example: +.Pp +.Dl match da,scsi | cd,ide +.Pp +This will display all SCSI Direct Access devices and all IDE CDROM devices. +.Pp +.Dl match da | sa | cd,pass +.Pp +This will display all Direct Access devices, all Sequential Access devices, +and all passthrough devices that provide access to CDROM drives. .El .Sh SEE ALSO .Xr iostat 1 , diff --git a/usr.bin/systat/vmstat.c b/usr.bin/systat/vmstat.c index 90cff801b2a1..79968f38dd9c 100644 --- a/usr.bin/systat/vmstat.c +++ b/usr.bin/systat/vmstat.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 1/12/94"; #endif static const char rcsid[] = - "$Id: vmstat.c,v 1.25 1998/06/09 04:17:29 imp Exp $"; + "$Id: vmstat.c,v 1.26 1998/07/06 22:08:00 bde Exp $"; #endif /* not lint */ /* @@ -44,7 +44,6 @@ static const char rcsid[] = */ #include -#include #include #include #include @@ -52,6 +51,7 @@ static const char rcsid[] = #include #include #include +#include #include #include @@ -66,6 +66,7 @@ static const char rcsid[] = #include #include #include +#include #include "systat.h" #include "extern.h" @@ -73,11 +74,6 @@ static struct Info { long time[CPUSTATES]; struct vmmeter Cnt; struct vmtotal Total; - long *dk_time; - long *dk_wds; - long *dk_seek; - long *dk_xfer; - int dk_busy; struct nchstats nchstats; long nchcount; long *intrcnt; @@ -87,6 +83,8 @@ static struct Info { long freevnodes; } s, s1, s2, z; +struct statinfo cur, last, run; + #define cnt s.Cnt #define oldcnt s1.Cnt #define total s.Total @@ -98,10 +96,11 @@ static enum state { BOOT, TIME, RUN } state = TIME; static void allocinfo __P((struct Info *)); static void copyinfo __P((struct Info *, struct Info *)); static float cputime __P((int)); -static void dinfo __P((int, int)); +static void dinfo __P((int, int, struct statinfo *, struct statinfo *)); static void getinfo __P((struct Info *, enum state)); static void putint __P((int, int, int, int)); static void putfloat __P((double, int, int, int, int, int)); +static void putlongdouble __P((long double, int, int, int, int, int)); static int ucount __P((void)); static int ncpu; @@ -147,31 +146,21 @@ static struct nlist namelist[] = { { "_cnt" }, #define X_BUFFERSPACE 2 { "_bufspace" }, -#define X_DK_BUSY 3 - { "_dk_busy" }, -#define X_DK_TIME 4 - { "_dk_time" }, -#define X_DK_XFER 5 - { "_dk_xfer" }, -#define X_DK_WDS 6 - { "_dk_wds" }, -#define X_DK_SEEK 7 - { "_dk_seek" }, -#define X_NCHSTATS 8 +#define X_NCHSTATS 3 { "_nchstats" }, -#define X_INTRNAMES 9 +#define X_INTRNAMES 4 { "_intrnames" }, -#define X_EINTRNAMES 10 +#define X_EINTRNAMES 5 { "_eintrnames" }, -#define X_INTRCNT 11 +#define X_INTRCNT 6 { "_intrcnt" }, -#define X_EINTRCNT 12 +#define X_EINTRCNT 7 { "_eintrcnt" }, -#define X_DESIREDVNODES 13 +#define X_DESIREDVNODES 8 { "_desiredvnodes" }, -#define X_NUMVNODES 14 +#define X_NUMVNODES 9 { "_numvnodes" }, -#define X_FREEVNODES 15 +#define X_FREEVNODES 10 { "_freevnodes" }, { "" }, }; @@ -200,13 +189,9 @@ static struct nlist namelist[] = { #define DISKROW 18 /* uses 5 rows and 50 cols (for 9 drives) */ #define DISKCOL 0 -#define DRIVESPACE 9 /* max # for space */ +#define DRIVESPACE 7 /* max # for space */ -#if DK_NDRIVE > DRIVESPACE #define MAXDRIVES DRIVESPACE /* max # to display */ -#else -#define MAXDRIVES DK_NDRIVE /* max # to display */ -#endif int initkre() @@ -225,21 +210,22 @@ initkre() return(0); } } - if (! dkinit()) + + if (num_devices = getnumdevs() < 0) { + warnx("%s", devstat_errbuf); return(0); - if (dk_ndrive && !once) { -#define allocate(e, t) \ - s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \ - s1./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \ - s2./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \ - z./**/e = (t *)calloc(dk_ndrive, sizeof (t)); - allocate(dk_time, long); - allocate(dk_wds, long); - allocate(dk_seek, long); - allocate(dk_xfer, long); - once = 1; -#undef allocate } + + cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + run.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + bzero(cur.dinfo, sizeof(struct devinfo)); + bzero(last.dinfo, sizeof(struct devinfo)); + bzero(run.dinfo, sizeof(struct devinfo)); + + if (dsinit(MAXDRIVES, &cur, &last, &run) != 1) + return(0); + if (nintr == 0) { nintr = (namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value) / sizeof (long); @@ -341,15 +327,23 @@ labelkre() mvprintw(NAMEIROW + 1, NAMEICOL, " Calls hits %% hits %%"); mvprintw(DISKROW, DISKCOL, "Discs"); - mvprintw(DISKROW + 1, DISKCOL, "seeks"); - mvprintw(DISKROW + 2, DISKCOL, "xfers"); - mvprintw(DISKROW + 3, DISKCOL, " blks"); - mvprintw(DISKROW + 4, DISKCOL, " msps"); + mvprintw(DISKROW + 1, DISKCOL, "KB/t"); + mvprintw(DISKROW + 2, DISKCOL, "tps"); + mvprintw(DISKROW + 3, DISKCOL, "MB/s"); + /* + * For now, we don't support a fourth disk statistic. So there's + * no point in providing a label for it. If someone can think of a + * fourth useful disk statistic, there is room to add it. + */ + /* mvprintw(DISKROW + 4, DISKCOL, " msps"); */ j = 0; - for (i = 0; i < dk_ndrive && j < MAXDRIVES; i++) - if (dk_select[i]) { - mvprintw(DISKROW, DISKCOL + 5 + 5 * j, - " %4.4s", dr_name[j]); + for (i = 0; i < num_devices && j < MAXDRIVES; i++) + if (dev_select[i].selected) { + char tmpstr[80]; + sprintf(tmpstr, "%s%d", dev_select[i].device_name, + dev_select[i].unit_number); + mvprintw(DISKROW, DISKCOL + 5 + 6 * j, + " %5.5s", tmpstr); j++; } for (i = 0; i < nintr; i++) { @@ -360,6 +354,7 @@ labelkre() } #define X(fld) {t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;} +#define Q(fld) {t=cur.fld[i]; cur.fld[i]-=last.fld[i]; if(state==TIME) last.fld[i]=t;} #define Y(fld) {t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;} #define Z(fld) {t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \ if(state == TIME) s1.nchstats.fld = t;} @@ -380,12 +375,10 @@ showkre() int i, l, c; static int failcnt = 0; - for (i = 0; i < dk_ndrive; i++) { - X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time); - } etime = 0; for(i = 0; i < CPUSTATES; i++) { X(time); + Q(cp_time); etime += s.time[i]; } if (etime < 5.0) { /* < 5 ticks - ignore this trash */ @@ -496,11 +489,24 @@ showkre() PUTRATE(Cnt.v_soft, GENSTATROW + 1, GENSTATCOL + 20, 5); PUTRATE(Cnt.v_vm_faults, GENSTATROW + 1, GENSTATCOL + 25, 5); mvprintw(DISKROW, DISKCOL + 5, " "); - for (i = 0, c = 0; i < dk_ndrive && c < MAXDRIVES; i++) - if (dk_select[i]) { - mvprintw(DISKROW, DISKCOL + 5 + 5 * c, - " %4.4s", dr_name[i]); - dinfo(i, ++c); + for (i = 0, c = 0; i < num_devices && c < MAXDRIVES; i++) + if (dev_select[i].selected) { + char tmpstr[80]; + sprintf(tmpstr, "%s%d", dev_select[i].device_name, + dev_select[i].unit_number); + mvprintw(DISKROW, DISKCOL + 5 + 6 * c, + " %5.5s", tmpstr); + switch(state) { + case TIME: + dinfo(i, ++c, &cur, &last); + break; + case RUN: + dinfo(i, ++c, &cur, &run); + break; + case BOOT: + dinfo(i, ++c, &cur, NULL); + break; + } } putint(s.nchcount, NAMEIROW + 2, NAMEICOL, 9); putint((nchtotal.ncs_goodhits + nchtotal.ncs_neghits), @@ -519,11 +525,27 @@ int cmdkre(cmd, args) char *cmd, *args; { + int retval; if (prefix(cmd, "run")) { + retval = 1; copyinfo(&s2, &s1); + switch (getdevs(&run)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + num_devices = run.dinfo->numdevs; + generation = run.dinfo->generation; + retval = dscmd("refresh", NULL, MAXDRIVES, &cur); + if (retval == 2) + labelkre(); + break; + default: + break; + } state = RUN; - return (1); + return (retval); } if (prefix(cmd, "boot")) { state = BOOT; @@ -535,11 +557,32 @@ cmdkre(cmd, args) return (1); } if (prefix(cmd, "zero")) { - if (state == RUN) + retval = 1; + if (state == RUN) { getinfo(&s1, RUN); - return (1); + switch (getdevs(&run)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + num_devices = run.dinfo->numdevs; + generation = run.dinfo->generation; + retval = dscmd("refresh",NULL, MAXDRIVES, &cur); + if (retval == 2) + labelkre(); + break; + default: + break; + } + } + return (retval); } - return (dkcmd(cmd, args)); + retval = dscmd(cmd, args, MAXDRIVES, &cur); + + if (retval == 2) + labelkre(); + + return(retval); } /* calculate number of users on the system */ @@ -616,25 +659,44 @@ putfloat(f, l, c, w, d, nz) addstr(b); } +static void +putlongdouble(f, l, c, w, d, nz) + long double f; + int l, c, w, d, nz; +{ + char b[128]; + + move(l, c); + if (nz && f == 0.0) { + while (--w >= 0) + addch(' '); + return; + } + sprintf(b, "%*.*Lf", w, d, f); + if (strlen(b) > w) { + while (--w >= 0) + addch('*'); + return; + } + addstr(b); +} + static void getinfo(s, st) struct Info *s; enum state st; { + struct devinfo *tmp_dinfo; int mib[2], size; extern int errno; NREAD(X_CPTIME, s->time, sizeof s->time); + NREAD(X_CPTIME, cur.cp_time, sizeof(cur.cp_time)); NREAD(X_CNT, &s->Cnt, sizeof s->Cnt); NREAD(X_BUFFERSPACE, &s->bufspace, sizeof(s->bufspace)); NREAD(X_DESIREDVNODES, &s->desiredvnodes, sizeof(s->desiredvnodes)); NREAD(X_NUMVNODES, &s->numvnodes, LONG); NREAD(X_FREEVNODES, &s->freevnodes, LONG); - NREAD(X_DK_BUSY, &s->dk_busy, sizeof(s->dk_busy)); - NREAD(X_DK_TIME, s->dk_time, dk_ndrive * LONG); - NREAD(X_DK_XFER, s->dk_xfer, dk_ndrive * LONG); - NREAD(X_DK_WDS, s->dk_wds, dk_ndrive * LONG); - NREAD(X_DK_SEEK, s->dk_seek, dk_ndrive * LONG); NREAD(X_NCHSTATS, &s->nchstats, sizeof s->nchstats); NREAD(X_INTRCNT, s->intrcnt, nintr * LONG); size = sizeof(s->Total); @@ -647,6 +709,24 @@ getinfo(s, st) size = sizeof(ncpu); if (sysctlbyname("hw.ncpu", &ncpu, &size, NULL, 0) < 0) ncpu = 1; + + tmp_dinfo = last.dinfo; + last.dinfo = cur.dinfo; + cur.dinfo = tmp_dinfo; + + last.busy_time = cur.busy_time; + switch (getdevs(&cur)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + cmdkre("refresh", NULL); + break; + default: + break; + } } static void @@ -663,45 +743,45 @@ static void copyinfo(from, to) register struct Info *from, *to; { - long *time, *wds, *seek, *xfer; long *intrcnt; + struct devinfo tmp_dinfo; /* * time, wds, seek, and xfer are malloc'd so we have to * save the pointers before the structure copy and then * copy by hand. */ - time = to->dk_time; wds = to->dk_wds; seek = to->dk_seek; - xfer = to->dk_xfer; intrcnt = to->intrcnt; + intrcnt = to->intrcnt; *to = *from; - bcopy(from->dk_time, to->dk_time = time, dk_ndrive * sizeof (long)); - bcopy(from->dk_wds, to->dk_wds = wds, dk_ndrive * sizeof (long)); - bcopy(from->dk_seek, to->dk_seek = seek, dk_ndrive * sizeof (long)); - bcopy(from->dk_xfer, to->dk_xfer = xfer, dk_ndrive * sizeof (long)); + bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int)); } static void -dinfo(dn, c) +dinfo(dn, c, now, then) int dn, c; + struct statinfo *now, *then; { - double words, atime, itime, xtime; + long double transfers_per_second; + long double kb_per_transfer, mb_per_second; + long double busy_seconds; + int di; - c = DISKCOL + c * 5; - atime = s.dk_time[dn]; - atime /= hertz; - words = s.dk_wds[dn]*32.0; /* number of words transferred */ - xtime = dk_mspw[dn]*words; /* transfer time */ - itime = atime - xtime; /* time not transferring */ - if (xtime < 0) - itime += xtime, xtime = 0; - if (itime < 0) - xtime += itime, itime = 0; - putint((int)((float)s.dk_seek[dn]/etime+0.5), DISKROW + 1, c, 5); - putint((int)((float)s.dk_xfer[dn]/etime+0.5), DISKROW + 2, c, 5); - putint((int)(words/etime/512.0 + 0.5), DISKROW + 3, c, 5); - if (s.dk_seek[dn]) - putfloat(itime*1000.0/s.dk_seek[dn], DISKROW + 4, c, 5, 1, 1); - else - putint(0, DISKROW + 4, c, 5); + di = dev_select[dn].position; + + busy_seconds = compute_etime(now->busy_time, then ? + then->busy_time : + then->dinfo->devices[di].dev_creation_time); + + if (compute_stats(&now->dinfo->devices[di], then ? + &then->dinfo->devices[di] : NULL, busy_seconds, + NULL, NULL, NULL, + &kb_per_transfer, &transfers_per_second, + &mb_per_second, NULL, NULL) != 0) + errx(1, "%s", devstat_errbuf); + + c = DISKCOL + c * 6; + putlongdouble(kb_per_transfer, DISKROW + 1, c, 5, 2, 0); + putlongdouble(transfers_per_second, DISKROW + 2, c, 5, 0, 0); + putlongdouble(mb_per_second, DISKROW + 3, c, 5, 2, 0); } diff --git a/usr.bin/vmstat/Makefile b/usr.bin/vmstat/Makefile index 117c30d176ea..38e45e7ee4ad 100644 --- a/usr.bin/vmstat/Makefile +++ b/usr.bin/vmstat/Makefile @@ -2,10 +2,11 @@ PROG= vmstat CFLAGS+=-I${.CURDIR}/../../sys +SRCS= vmstat.c MAN8= vmstat.8 BINGRP= kmem BINMODE=2555 -DPADD= ${LIBKVM} -LDADD= -lkvm +DPADD= ${LIBKVM} ${LIBDEVSTAT} +LDADD= -lkvm -ldevstat .include diff --git a/usr.bin/vmstat/vmstat.8 b/usr.bin/vmstat/vmstat.8 index ec6c323a6dad..6c06202029f0 100644 --- a/usr.bin/vmstat/vmstat.8 +++ b/usr.bin/vmstat/vmstat.8 @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)vmstat.8 8.1 (Berkeley) 6/6/93 -.\" $Id: vmstat.8,v 1.8 1997/08/23 21:42:46 steve Exp $ +.\" $Id: vmstat.8,v 1.9 1997/08/25 06:40:05 charnier Exp $ .\" .Dd June 6, 1996 .Dt VMSTAT 8 @@ -46,6 +46,8 @@ .Op Fl M Ar core .Op Fl N Ar system .Op Fl w Ar wait +.Op Fl n Ar devs +.Op Fl p Ar type,if,pass .Op Ar disks .Sh DESCRIPTION .Nm Vmstat @@ -86,6 +88,76 @@ instead of the default .It Fl m Report on the usage of kernel dynamic memory listed first by size of allocation and then by type of usage. +.It Fl n +Change the maximum number of disks to display from the default of 3. +.It Fl p +Specify which types of devices to display. There are three different +categories of devices: + +.Bl -tag -width indent -compact +.It device type: +.Bl -tag -width 123456789 -compact +.It da +Direct Access devices +.It sa +Sequential Access devices +.It printer +Printers +.It proc +Processor devices +.It worm +Write Once Read Multiple devices +.It cd +CD devices +.It scanner +Scanner devices +.It optical +Optical Memory devices +.It changer +Medium Changer devices +.It comm +Communication devices +.It array +Storage Array devices +.It enclosure +Enclosure Services devices +.It floppy +Floppy devices +.El +.Pp +.It interface: +.Bl -tag -width 123456789 -compact +.It IDE +Integrated Drive Electronics devices +.It SCSI +Small Computer System Interface devices +.It other +Any other device interface +.El +.Pp +.It passthrough: +.Bl -tag -width 123456789 -compact +.It pass +Passthrough devices +.El +.El +.Pp +The user must specify at least one device type, and may specify at most +one device type from each category. Multiple device types in a single +device type statement must be separated by commas. +.Pp +Any number of +.Fl p +arguments may be specified on the command line. All +.Fl p +arguments are ORed together to form a matching expression against which +all devices in the system are compared. Any device that fully matches +any +.Fl p +argument will be included in the +.Nm +output, up to three devices, or the maximum number of devices specified +by the user. .It Fl s Display the contents of the .Em sum @@ -156,14 +228,25 @@ pages scanned by clock algorithm, per-second .It disks Disk operations per second (this field is system dependent). Typically paging will be split across the available drives. -The header of the field is the first character of the disk name and +The header of the field is the first two characters of the disk name and the unit number. -If more than four disk drives are configured in the system, +If more than three disk drives are configured in the system, .Nm -displays only the first four drives. +displays only the first three drives, unless the user specifies the +.Fl n +argument to increase the number of drives displayed. This will probably +cause the display to exceed 80 columns, however. To force .Nm to display specific drives, their names may be supplied on the command line. +.Nm +defaults to show disks first, and then various other random devices in the +system to add up to three devices, if there are that many devices in the +system. If devices are specified on the command line, or if a device type +matching pattern is specified (see above), +.Nm +will only display the given devices or the devices matching the pattern, +and will not randomly select other devices in the system. .It faults Trap/interrupt rate averages per second over last 5 seconds. .Pp @@ -195,6 +278,12 @@ seconds; this is a good choice of printing interval since this is how often some of the statistics are sampled in the system. Others vary every second and running the output for a while will make it apparent which are recomputed every second. +.Pp +The command: +.Dl vmstat -p da -p cd -w 1 +will tell vmstat to select the first three direct access or CDROM devices +and display statistics on those devices, as well as other systems +statistics every second. .Sh FILES .Bl -tag -width /dev/kmemxxx -compact .It Pa /kernel diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c index 3701ec4a5b65..7f37b95a3548 100644 --- a/usr.bin/vmstat/vmstat.c +++ b/usr.bin/vmstat/vmstat.c @@ -42,7 +42,7 @@ static const char copyright[] = static char sccsid[] = "@(#)vmstat.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = - "$Id: vmstat.c,v 1.23 1998/03/07 23:40:23 dyson Exp $"; + "$Id: vmstat.c,v 1.24 1998/07/06 21:01:54 bde Exp $"; #endif /* not lint */ #include @@ -74,48 +74,45 @@ static const char rcsid[] = #include #include #include +#include struct nlist namelist[] = { #define X_CPTIME 0 { "_cp_time" }, -#define X_DK_NDRIVE 1 - { "_dk_ndrive" }, -#define X_SUM 2 +#define X_SUM 1 { "_cnt" }, -#define X_BOOTTIME 3 +#define X_BOOTTIME 2 { "_boottime" }, -#define X_DKXFER 4 - { "_dk_xfer" }, -#define X_HZ 5 +#define X_HZ 3 { "_hz" }, -#define X_STATHZ 6 +#define X_STATHZ 4 { "_stathz" }, -#define X_NCHSTATS 7 +#define X_NCHSTATS 5 { "_nchstats" }, -#define X_INTRNAMES 8 +#define X_INTRNAMES 6 { "_intrnames" }, -#define X_EINTRNAMES 9 +#define X_EINTRNAMES 7 { "_eintrnames" }, -#define X_INTRCNT 10 +#define X_INTRCNT 8 { "_intrcnt" }, -#define X_EINTRCNT 11 +#define X_EINTRCNT 9 { "_eintrcnt" }, -#define X_KMEMSTATISTICS 12 +#define X_KMEMSTATISTICS 10 { "_kmemstatistics" }, -#define X_KMEMBUCKETS 13 +#define X_KMEMBUCKETS 11 { "_bucket" }, #ifdef notyet -#define X_DEFICIT 14 +#define X_DEFICIT 12 { "_deficit" }, -#define X_FORKSTAT 15 +#define X_FORKSTAT 13 { "_forkstat" }, -#define X_REC 16 +#define X_REC 14 { "_rectime" }, -#define X_PGIN 17 +#define X_PGIN 15 { "_pgintime" }, -#define X_XSTATS 18 +#define X_XSTATS 16 { "_xstats" }, -#define X_END 19 +#define X_END 17 #else #define X_END 14 #endif @@ -123,10 +120,6 @@ struct nlist namelist[] = { #define X_HPDINIT (X_END) { "_hp_dinit" }, #endif -#if defined(i386) -#define X_DK_NAMES (X_END) - { "_dk_names" }, -#endif #ifdef mips #define X_SCSI_DINIT (X_END) { "_scsi_dinit" }, @@ -148,14 +141,17 @@ struct nlist namelist[] = { { "" }, }; -struct _disk { - long time[CPUSTATES]; - long *xfer; -} cur, last; +struct statinfo cur, last; +int num_devices, maxshowdevs, generation; +struct device_selection *dev_select; +int num_selected; +struct devstat_match *matches; +int num_matches = 0; +int num_devices_specified, num_selections, select_generation; +char **specified_devices; +devstat_select_mode select_mode; struct vmmeter sum, osum; -char **dr_name; -int *dr_select, dk_ndrive, ndrives; int winlines = 20; @@ -168,14 +164,13 @@ kvm_t *kd; #define TIMESTAT 0x10 #define VMSTAT 0x20 -#include "names.c" /* disk names -- machine dependent */ - -void cpustats(), dkstats(), dointr(), domem(), dosum(); +void cpustats(), dointr(), domem(), dosum(); void dovmstat(), kread(), usage(); #ifdef notyet void dotimes(), doforkst(); #endif void printhdr __P((void)); +static void devstats(); int main(argc, argv) @@ -187,10 +182,12 @@ main(argc, argv) int reps; char *memf, *nlistf; char errbuf[_POSIX2_LINE_MAX]; + char *err_str; memf = nlistf = NULL; interval = reps = todo = 0; - while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != -1) { + maxshowdevs = 3; + while ((c = getopt(argc, argv, "c:fiM:mN:n:p:stw:")) != -1) { switch (c) { case 'c': reps = atoi(optarg); @@ -214,6 +211,16 @@ main(argc, argv) case 'N': nlistf = optarg; break; + case 'n': + maxshowdevs = atoi(optarg); + if (maxshowdevs < 0) + errx(1, "number of devices %d is < 0", + maxshowdevs); + break; + case 'p': + if (buildmatch(optarg, &matches, &num_matches) != 0) + errx(1, "%s", devstat_errbuf); + break; case 's': todo |= SUMSTAT; break; @@ -314,62 +321,64 @@ getdrivedata(argv) char **argv; { register int i; - register char **cp; char buf[30]; - kread(X_DK_NDRIVE, &dk_ndrive, sizeof(dk_ndrive)); - if (dk_ndrive < 0) - errx(1, "dk_ndrive %d", dk_ndrive); - dr_select = calloc((size_t)dk_ndrive, sizeof(int)); - dr_name = calloc((size_t)dk_ndrive, sizeof(char *)); - for (i = 0; i < dk_ndrive; i++) - dr_name[i] = NULL; - cur.xfer = calloc((size_t)dk_ndrive, sizeof(long)); - last.xfer = calloc((size_t)dk_ndrive, sizeof(long)); - if (!read_names()) - exit (1); - for (i = 0; i < dk_ndrive; i++) - if (dr_name[i] == NULL) { - (void)sprintf(buf, "??%d", i); - dr_name[i] = strdup(buf); - } + if ((num_devices = getnumdevs()) < 0) + errx(1, "%s", devstat_errbuf); - /* - * Choose drives to be displayed. Priority goes to (in order) drives - * supplied as arguments, default drives. If everything isn't filled - * in and there are drives not taken care of, display the first few - * that fit. - */ -#define BACKWARD_COMPATIBILITY - for (ndrives = 0; *argv; ++argv) { -#ifdef BACKWARD_COMPATIBILITY + cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + bzero(cur.dinfo, sizeof(struct devinfo)); + bzero(last.dinfo, sizeof(struct devinfo)); + + if (getdevs(&cur) == -1) + errx(1, "%s", devstat_errbuf); + + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + + specified_devices = (char **)malloc(sizeof(char *)); + for (num_devices_specified = 0; *argv; ++argv) { if (isdigit(**argv)) break; -#endif - for (i = 0; i < dk_ndrive; i++) { - if (strcmp(dr_name[i], *argv)) - continue; - dr_select[i] = 1; - ++ndrives; - break; - } - } - for (i = 0; i < dk_ndrive && ndrives < 4; i++) { - if (dr_select[i]) - continue; - for (cp = defdrives; *cp; cp++) - if (strcmp(dr_name[i], *cp) == 0) { - dr_select[i] = 1; - ++ndrives; - break; - } - } - for (i = 0; i < dk_ndrive && ndrives < 4; i++) { - if (dr_select[i]) - continue; - dr_select[i] = 1; - ++ndrives; + num_devices_specified++; + specified_devices = (char **)realloc(specified_devices, + sizeof(char *) * + num_devices_specified); + specified_devices[num_devices_specified - 1] = *argv; } + dev_select = NULL; + + /* + * People are generally only interested in disk statistics when + * they're running vmstat. So, that's what we're going to give + * them if they don't specify anything by default. We'll also give + * them any other random devices in the system so that we get to + * maxshowdevs devices, if that many devices exist. If the user + * specifies devices on the command line, either through a pattern + * match or by naming them explicitly, we will give the user only + * those devices. + */ + if ((num_devices_specified == 0) && (num_matches == 0)) { + if (buildmatch("da", &matches, &num_matches) != 0) + errx(1, "%s", devstat_errbuf); + + 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 (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, 0) == -1) + errx(1, "%s", devstat_errbuf); + return(argv); } @@ -397,6 +406,7 @@ dovmstat(interval, reps) { struct vmtotal total; time_t uptime, halfuptime; + struct devinfo *tmp_dinfo; void needhdr(); int mib[2], size; @@ -409,11 +419,63 @@ dovmstat(interval, reps) if (!hz) kread(X_HZ, &hz, sizeof(hz)); + /* + * 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 (checkversion() < 0) + errx(1, "%s", devstat_errbuf); + for (hdrcnt = 1;;) { if (!--hdrcnt) printhdr(); - kread(X_CPTIME, cur.time, sizeof(cur.time)); - kread(X_DKXFER, cur.xfer, sizeof(*cur.xfer) * dk_ndrive); + kread(X_CPTIME, cur.cp_time, sizeof(cur.cp_time)); + + tmp_dinfo = last.dinfo; + last.dinfo = cur.dinfo; + cur.dinfo = tmp_dinfo; + last.busy_time = cur.busy_time; + + /* + * Here what we want to do is refresh our device stats. + * 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 (getdevs(&cur)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: { + int retval; + + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + + retval = 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, 0); + switch (retval) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: + printhdr(); + break; + default: + break; + } + } + default: + break; + } + kread(X_SUM, &sum, sizeof(sum)); size = sizeof(total); mib[0] = CTL_VM; @@ -442,7 +504,7 @@ dovmstat(interval, reps) (u_long)rate(sum.v_tfree - osum.v_tfree)); (void)printf("%3lu ", (u_long)rate(sum.v_pdpages - osum.v_pdpages)); - dkstats(); + devstats(); (void)printf("%4lu %4lu %3lu ", (u_long)rate(sum.v_intr - osum.v_intr), (u_long)rate(sum.v_syscall - osum.v_syscall), @@ -471,17 +533,23 @@ printhdr() { register int i; - (void)printf(" procs memory page%*s", 20, ""); - if (ndrives > 1) + (void)printf(" procs memory page%*s", 19, ""); + if (num_selected > 1) (void)printf("disks %*s faults cpu\n", - ndrives * 3 - 6, ""); + ((num_selected < maxshowdevs) ? num_selected : + maxshowdevs ) * 4 - 7, ""); + else if (num_selected == 1) + (void)printf("disk faults cpu\n"); else - (void)printf("%*s faults cpu\n", ndrives * 3, ""); + (void)printf("%*s faults cpu\n", num_selected * 4, ""); + (void)printf(" r b w avm fre flt re pi po fr sr "); - for (i = 0; i < dk_ndrive; i++) - if (dr_select[i]) - (void)printf("%c%c ", dr_name[i][0], - dr_name[i][strlen(dr_name[i]) - 1]); + for (i = 0; i < num_devices; i++) + if ((dev_select[i].selected) + && (dev_select[i].selected <= maxshowdevs)) + (void)printf("%c%c%d ", dev_select[i].device_name[0], + dev_select[i].device_name[1], + dev_select[i].unit_number); (void)printf(" in sy cs us sy id\n"); hdrcnt = winlines - 2; } @@ -624,32 +692,39 @@ doforkst() } #endif -void -dkstats() +static void +devstats() { register int dn, state; - double etime; + long double transfers_per_second; + long double busy_seconds; long tmp; - - for (dn = 0; dn < dk_ndrive; ++dn) { - tmp = cur.xfer[dn]; - cur.xfer[dn] -= last.xfer[dn]; - last.xfer[dn] = tmp; - } - etime = 0; + for (state = 0; state < CPUSTATES; ++state) { - tmp = cur.time[state]; - cur.time[state] -= last.time[state]; - last.time[state] = tmp; - etime += cur.time[state]; + tmp = cur.cp_time[state]; + cur.cp_time[state] -= last.cp_time[state]; + last.cp_time[state] = tmp; } - if (etime == 0) - etime = 1; - etime /= hz; - for (dn = 0; dn < dk_ndrive; ++dn) { - if (!dr_select[dn]) + + busy_seconds = compute_etime(cur.busy_time, last.busy_time); + + for (dn = 0; dn < num_devices; dn++) { + int di; + + if ((dev_select[dn].selected == 0) + || (dev_select[dn].selected > maxshowdevs)) continue; - (void)printf("%2.0f ", cur.xfer[dn] / etime); + + di = dev_select[dn].position; + + if (compute_stats(&cur.dinfo->devices[di], + &last.dinfo->devices[di], busy_seconds, + NULL, NULL, NULL, + NULL, &transfers_per_second, NULL, + NULL, NULL) != 0) + errx(1, "%s", devstat_errbuf); + + printf("%3.0Lf ", transfers_per_second); } } @@ -661,14 +736,16 @@ cpustats() total = 0; for (state = 0; state < CPUSTATES; ++state) - total += cur.time[state]; + total += cur.cp_time[state]; if (total) pct = 100 / total; else pct = 0; - (void)printf("%2.0f ", (cur.time[CP_USER] + cur.time[CP_NICE]) * pct); - (void)printf("%2.0f ", (cur.time[CP_SYS] + cur.time[CP_INTR]) * pct); - (void)printf("%2.0f", cur.time[CP_IDLE] * pct); + (void)printf("%2.0f ", (cur.cp_time[CP_USER] + + cur.cp_time[CP_NICE]) * pct); + (void)printf("%2.0f ", (cur.cp_time[CP_SYS] + + cur.cp_time[CP_INTR]) * pct); + (void)printf("%2.0f", cur.cp_time[CP_IDLE] * pct); } void diff --git a/usr.sbin/iostat/Makefile b/usr.sbin/iostat/Makefile index b67da204bc2b..1caefc59f79f 100644 --- a/usr.sbin/iostat/Makefile +++ b/usr.sbin/iostat/Makefile @@ -1,10 +1,11 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 PROG= iostat -CFLAGS+=-I${.CURDIR}/../../usr.bin/vmstat -I${.CURDIR}/../../sys +CFLAGS+=-I${.CURDIR}/../../sys +SRCS= iostat.c MAN8= iostat.8 -DPADD= ${LIBKVM} -LDADD= -lkvm +DPADD= ${LIBKVM} ${LIBDEVSTAT} +LDADD= -lkvm -ldevstat BINGRP= kmem BINMODE=2555 diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8 index 861acefb301a..3df37cfdbe04 100644 --- a/usr.sbin/iostat/iostat.8 +++ b/usr.sbin/iostat/iostat.8 @@ -1,3 +1,32 @@ +.\" +.\" Copyright (c) 1997 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. +.\" +.\" $Id$ +.\" .\" Copyright (c) 1985, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -31,9 +60,9 @@ .\" .\" @(#)iostat.8 8.1 (Berkeley) 6/6/93 .\" -.Dd June 6, 1993 +.Dd December 22, 1997 .Dt IOSTAT 8 -.Os BSD 4 +.Os FreeBSD 3.0 .Sh NAME .Nm iostat .Nd report @@ -41,16 +70,19 @@ statistics .Sh SYNOPSIS .Nm iostat +.Op Fl CdhIoT? .Op Fl c Ar count .Op Fl M Ar core +.Op Fl n Ar devs .Op Fl N Ar system +.Op Fl t Ar type,if,pass .Op Fl w Ar wait .Op Ar drives .Sh DESCRIPTION .Nm Iostat displays kernel .Tn I/O -statistics on terminal, disk and cpu +statistics on terminal, device and cpu operations. .Pp The options are as follows: @@ -58,19 +90,123 @@ The options are as follows: .It Fl c Repeat the display .Ar count -times. -The first display is for the time since a reboot and each subsequent -report is for the time period since the last display. -If no +times. If no .Ar wait interval is specified, the default is 1 second. +.It Fl C +Display CPU statistics. This is on by default, unless +.Fl d +is specified. +.It Fl d +Display only device statistics. If this flag is turned on, only device +statistics will be displayed, unless +.Fl C +or +.Fl T +is also specfied to enable the display of CPU or TTY statistics. +.It Fl h +Put iostat in +.Sq top +mode. In this mode, iostat will show devices in order from highest to +lowest bytes per measurement cycle. +.It Fl I +Display total statstics for a given time period, rather than average +statistics for each second during that time period. .It Fl M Extract values associated with the name list from the specified core instead of the default .Dq Pa /dev/kmem . +.It Fl n +Display up to +.Ar devs +number of devices. +.Nm iostat +will display fewer devices if there aren't +.Ar devs +devices present. .It Fl N Extract the name list from the specified system instead of the default .Dq Pa /kernel . +.It Fl o +Display old-style +.Nm iostat +device statistics. Sectors per second, transfers per second, and miliseconds +per seek are displayed. If +.Fl I +is specified, total blocks/sectors, total transfers, and +miliseconds per seek are displayed. +.It Fl t +Specify which types of devices to display. There are three different +categories of devices: + +.Bl -tag -width indent -compact +.It device type: +.Bl -tag -width 123456789 -compact +.It da +Direct Access devices +.It sa +Sequential Access devices +.It printer +Printers +.It proc +Processor devices +.It worm +Write Once Read Multiple devices +.It cd +CD devices +.It scanner +Scanner devices +.It optical +Optical Memory devices +.It changer +Medium Changer devices +.It comm +Communication devices +.It array +Storage Array devices +.It enclosure +Enclosure Services devices +.It floppy +Floppy devices +.El +.Pp +.It interface: +.Bl -tag -width 123456789 -compact +.It IDE +Integrated Drive Electronics devices +.It SCSI +Small Computer System Interface devices +.It other +Any other device interface +.El +.Pp +.It passthrough: +.Bl -tag -width 123456789 -compact +.It pass +Passthrough devices +.El +.El +.Pp +The user must specify at least one device type, and may specify at most +one device type from each category. Multiple device types in a single +device type statement must be separated by commas. +.Pp +Any number of +.Fl t +arguments may be specified on the command line. All +.Fl t +arguments are ORed together to form a matching expression against which +all devices in the system are compared. Any device that fully matches +any +.Fl t +argument will be included in the +.Nm iostat +output, up to the number of devices that can be displayed in +80 columns, or the maximum number of devices specified by the user. +.It Fl T +Display TTY statistics. This is on by default, unless +.Fl d +is specified. .It Fl w Pause .Ar wait @@ -78,6 +214,8 @@ seconds between each display. If no repeat .Ar count is specified, the default is infinity. +.It Fl ? +Display a usage statement and exit. .El .Pp .Nm Iostat @@ -90,16 +228,60 @@ characters read from terminals .It tout characters written to terminals .El -.It disks -Disk operations (this field is system dependent). -The header of the field is the disk name and unit number. -If more than four disk drives are configured in the system, +.It devices +Device operations. The header of the field is the device name and unit number. .Nm iostat -displays only the first four drives. +will display as many devices as will fit in a standard 80 column screen, or +the maximum number of devices in the system, whichever is smaller. If +.Fl n +is specified on the command line, iostat will display the smaller of the +requested number of devices, and the maximum number of devices in the system. To force .Nm iostat to display specific drives, their names may be supplied on the command line. +.Nm iostat +will not display more devices than will fit in an 80 column screen, unless +the +.Fl n +argument is given on the command line to specify a maximum number of +devices to display. If fewer devices are specified on the command line +than will fit in an 80 column screen, iostat will show only the specified +devices. +.Pp +The standard +.Nm iostat +device display shows the following statistics: +.Pp +.Bl -tag -width indent -compact +.It KB/t +kilobytes per transfer +.It tps +transfers per second +.It MB/s +megabytes per second +.El +.Pp +The standard +.Nm iostat +device display, with the +.Fl I +flag specified, shows the following statistics: +.Pp +.Bl -tag -width indent -compact +.It KB/t +kilobytes per transfer +.It xfrs +total number of transfers +.It MB +total number of megabytes transferred +.El +.Pp +The old-style +.Nm iostat +display (using +.Fl o ) +shows the following statistics: .Pp .Bl -tag -width indent -compact .It sps @@ -107,8 +289,22 @@ sectors transferred per second .It tps transfers per second .It msps -milliseconds per average seek (including implied -seeks and rotational latency) +average milliseconds per transaction +.El +.Pp +The old-style +.Nm iostat +display, with the +.Fl I +flag specified, shows the following statistics: +.Pp +.Bl -tag -width indent -compact +.It blk +total blocks/sectors transferred +.It xfr +total transfers +.It msps +average milliseconds per transaction .El .It cpu .Bl -tag -width indent -compact @@ -131,6 +327,50 @@ Default kernel namelist. .It Pa /dev/kmem Default memory file. .El +.Sh EXAMPLES +.Dl iostat -w 1 da0 da1 cd0 +.Pp +Display statistics for the first two Direct Access devices and the first +CDROM device every second ad infinitum. +.Pp +.Dl iostat -c 2 +.Pp +Display the statistics for the first four devices in the system twice, with +a one second display interval. +.Pp +.Dl iostat -t da -t cd -w 1 +.Pp +Display statistics for all CDROM and Direct Access devices every second +ad infinitum. +.Pp +.Dl iostat -t da,scsi,pass -t cd,scsi,pass +.Pp +Display statistics once for all SCSI passthrough devices that provide access +to either Direct Access or CDROM devices. +.Pp +.Dl iostat -h -n 8 -w 1 +.Pp +Display up to 8 devices with the most I/O every second ad inifitum. +.Pp +.Dl iostat -dh -t da -w 1 +.Pp +Omit the TTY and CPU displays, show devices in order of performance and +show only Direct Access devices every second ad infinitum. +.Pp +.Dl iostat -Iw 3 +.Pp +Display total statistics every three seconds ad infinitum. +.Pp +.Dl iostat -odICTw 2 -c 9 +.Pp +Display total statistics using the old-style output format 9 times, with +a two second interval between each measurement/display. The +.Fl d +flag generally disables the TTY and CPU displays, but since the +.Fl T +and +.Fl C +flags are given, the TTY and CPU displays will be displayed. .Sh SEE ALSO .Xr fstat 1 , .Xr netstat 1 , @@ -142,3 +382,16 @@ Default memory file. .Pp The sections starting with ``Interpreting system activity'' in .%T "Installing and Operating 4.3BSD" . +.Sh HISTORY +This version of +.Nm iostat +first appeared in +.Fx 3.0 . +.Sh BUGS +.Pp +You cannot display device statistics for a non-running system, due to the +fact that the new device statistics interface is accessible only via +.Xr sysctl 3 , +which does not provide a way to access non-running systems. +.Sh AUTHOR +.An Kenneth Merry Aq ken@FreeBSD.ORG diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c index d1531ea8a8d2..7ea816aca247 100644 --- a/usr.sbin/iostat/iostat.c +++ b/usr.sbin/iostat/iostat.c @@ -1,3 +1,36 @@ +/* + * Copyright (c) 1997, 1998 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. + * + * $Id$ + */ +/* + * 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. @@ -30,146 +63,185 @@ * 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. + */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1986, 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94"; -#endif -static const char rcsid[] = - "$Id$"; -#endif /* not lint */ #include -#include +#include +#include #include #include #include #include #include -#include -#include -#include -#include #include #include #include #include +#include +#include struct nlist namelist[] = { -#define X_DK_TIME 0 - { "_dk_time" }, -#define X_DK_XFER 1 - { "_dk_xfer" }, -#define X_DK_WDS 2 - { "_dk_wds" }, -#define X_TK_NIN 3 +#define X_TK_NIN 0 { "_tk_nin" }, -#define X_TK_NOUT 4 +#define X_TK_NOUT 1 { "_tk_nout" }, -#define X_DK_SEEK 5 - { "_dk_seek" }, -#define X_CP_TIME 6 +#define X_CP_TIME 2 { "_cp_time" }, -#define X_DK_WPMS 7 - { "_dk_wpms" }, -#define X_HZ 8 +#define X_HZ 3 { "_hz" }, -#define X_STATHZ 9 +#define X_STATHZ 4 { "_stathz" }, -#define X_DK_NDRIVE 10 - { "_dk_ndrive" }, -#define X_END 10 -#if defined(hp300) || defined(luna68k) -#define X_HPDINIT (X_END+1) - { "_hp_dinit" }, -#endif -#if defined(i386) -#define X_DK_NAMES (X_END+1) - { "_dk_names" }, -#endif -#ifdef mips -#define X_SCSI_DINIT (X_END+1) - { "_scsi_dinit" }, -#endif -#ifdef tahoe -#define X_VBDINIT (X_END+1) - { "_vbdinit" }, -#endif -#ifdef vax - { "_mbdinit" }, -#define X_MBDINIT (X_END+1) - { "_ubdinit" }, -#define X_UBDINIT (X_END+2) -#endif +#define X_END 4 { NULL }, }; -struct _disk { - long cp_time[CPUSTATES]; - long *dk_time; - long *dk_wds; - long *dk_seek; - long *dk_xfer; - long tk_nin; - long tk_nout; -} cur, last; - -kvm_t *kd; -double etime; -long *dk_wpms; -int dk_ndrive, *dr_select, hz, kmemfd, ndrives; -char **dr_name; +struct statinfo cur, last; +int num_devices; +struct device_selection *dev_select; +int maxshowdevs; +int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0; #define nlread(x, v) \ kvm_read(kd, namelist[x].n_value, &(v), sizeof(v)) -#include "names.c" /* XXX */ +/* local function declarations */ +static void usage(void); +static void phdr(int signo); +static void devstats(int perf_select); +static void cpustats(void); -void cpustats __P((void)); -void dkstats __P((void)); -void phdr __P((int)); -static void usage __P((void)); +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 [-CdhIoT?] [-c count] [-M core]" + " [-n devs] [-N system]\n" + "\t [-t type,if,pass] [-w wait] [drives]\n"); +} int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char **argv) { + int c; register int i; - long tmp; - int ch, hdrcnt, reps, interval, stathz, ndrives; - char **cp, *memf, *nlistf, buf[30]; + int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0; + int count = 0, waittime = 0; + char *memf = NULL, *nlistf = NULL; + struct devstat_match *matches; + int num_matches = 0; char errbuf[_POSIX2_LINE_MAX]; + char *err_str; + kvm_t *kd; + int hz, stathz; + int headercount; + int generation; + int num_devices_specified; + int num_selected, num_selections, select_generation; + char **specified_devices; + devstat_select_mode select_mode; - interval = reps = 0; - nlistf = memf = NULL; - while ((ch = getopt(argc, argv, "c:M:N:w:")) != -1) - switch(ch) { - case 'c': - if ((reps = atoi(optarg)) <= 0) - errx(1, "repetition count <= 0"); - break; - case 'M': - memf = optarg; - break; - case 'N': - nlistf = optarg; - break; - case 'w': - if ((interval = atoi(optarg)) <= 0) - errx(1, "interval <= 0"); - break; - case '?': - default: - usage(); + matches = NULL; + maxshowdevs = 3; + + while ((c = getopt(argc, argv, "c:CdhIM:n:N:ot:Tw:?")) != -1) { + switch(c) { + case 'c': + cflag++; + count = atoi(optarg); + if (count < 1) + errx(1, "count %d is < 1", count); + break; + case 'C': + Cflag++; + break; + case 'd': + dflag++; + break; + case 'h': + hflag++; + break; + case 'I': + Iflag++; + break; + case 'M': + memf = optarg; + break; + case 'n': + nflag++; + maxshowdevs = atoi(optarg); + if (maxshowdevs < 0) + errx(1, "number of devcies %d is < 0", + maxshowdevs); + break; + case 'N': + nlistf = optarg; + break; + case 'o': + oflag++; + break; + case 't': + tflag++; + if (buildmatch(optarg, &matches, + &num_matches) != 0) + errx(1, "%s", devstat_errbuf); + break; + case 'T': + Tflag++; + break; + case 'w': + wflag++; + waittime = atoi(optarg); + if (waittime < 1) + errx(1, "wait time is < 1"); + break; + default: + usage(); + exit(1); + break; } + } + argc -= optind; argv += optind; @@ -180,133 +252,248 @@ main(argc, argv) if (nlistf != NULL || memf != NULL) setgid(getgid()); - kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); + /* + * 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 (checkversion() < 0) + errx(1, "%s", devstat_errbuf); + + /* + * Figure out how many devices we should display. + */ + if (nflag == 0) { + 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; + } + } + + /* find out how many devices we have */ + if ((num_devices = getnumdevs()) < 0) + err(1, "can't get number of devices"); + + cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + bzero(cur.dinfo, sizeof(struct devinfo)); + bzero(last.dinfo, sizeof(struct devinfo)); + + /* + * 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 (getdevs(&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 *)); + 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); + specified_devices[num_devices_specified - 1] = *argv; + + } + + 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 (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); + + free(specified_devices); + + /* + * Look for the traditional wait time and count arguments. + */ + if (*argv) { + waittime = atoi(*argv); + + /* Let the user know he goofed, but keep going anyway */ + if (wflag != 0) + warnx("discarding previous wait interval, using" + " %d instead", waittime); + 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; + + /* + * 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; + + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); + if (kd == 0) errx(1, "kvm_openfiles: %s", errbuf); + if (kvm_nlist(kd, namelist) == -1) errx(1, "kvm_nlist: %s", kvm_geterr(kd)); - if (namelist[X_DK_NDRIVE].n_type == 0) - errx(1, "dk_ndrive not found in namelist"); - (void)nlread(X_DK_NDRIVE, dk_ndrive); - if (dk_ndrive < 0) - errx(1, "invalid dk_ndrive %d", dk_ndrive); - cur.dk_time = calloc(dk_ndrive, sizeof(long)); - cur.dk_wds = calloc(dk_ndrive, sizeof(long)); - cur.dk_seek = calloc(dk_ndrive, sizeof(long)); - cur.dk_xfer = calloc(dk_ndrive, sizeof(long)); - last.dk_time = calloc(dk_ndrive, sizeof(long)); - last.dk_wds = calloc(dk_ndrive, sizeof(long)); - last.dk_seek = calloc(dk_ndrive, sizeof(long)); - last.dk_xfer = calloc(dk_ndrive, sizeof(long)); - dr_select = calloc(dk_ndrive, sizeof(int)); - dr_name = calloc(dk_ndrive, sizeof(char *)); - dk_wpms = calloc(dk_ndrive, sizeof(long)); - - for (i = 0; i < dk_ndrive; i++) { - (void)sprintf(buf, "dk%d", i); - dr_name[i] = strdup(buf); - } - if (!read_names()) - exit(1); (void)nlread(X_HZ, hz); (void)nlread(X_STATHZ, stathz); if (stathz) hz = stathz; - (void)kvm_read(kd, namelist[X_DK_WPMS].n_value, dk_wpms, - dk_ndrive * sizeof(dk_wpms)); /* - * Choose drives to be displayed. Priority goes to (in order) drives - * supplied as arguments and default drives. If everything isn't - * filled in and there are drives not taken care of, display the first - * few that fit. - * - * The backward compatibility #ifdefs permit the syntax: - * iostat [ drives ] [ interval [ count ] ] + * If the user stops the program (control-Z) and then resumes it, + * print out the header again. */ -#define BACKWARD_COMPATIBILITY - for (ndrives = 0; *argv; ++argv) { -#ifdef BACKWARD_COMPATIBILITY - if (isdigit(**argv)) - break; -#endif - for (i = 0; i < dk_ndrive; i++) { - if (strcmp(dr_name[i], *argv)) - continue; - dr_select[i] = 1; - ++ndrives; - } - } -#ifdef BACKWARD_COMPATIBILITY - if (*argv) { - interval = atoi(*argv); - if (*++argv) - reps = atoi(*argv); - } -#endif - - if (interval) { - if (!reps) - reps = -1; - } else - if (reps) - interval = 1; - - for (i = 0; i < dk_ndrive && ndrives < 4; i++) { - if (dr_select[i] || dk_wpms[i] == 0) - continue; - for (cp = defdrives; *cp; cp++) - if (strcmp(dr_name[i], *cp) == 0) { - dr_select[i] = 1; - ++ndrives; - break; - } - } - for (i = 0; i < dk_ndrive && ndrives < 4; i++) { - if (dr_select[i]) - continue; - dr_select[i] = 1; - ++ndrives; - } - (void)signal(SIGCONT, phdr); - for (hdrcnt = 1;;) { - if (!--hdrcnt) { + for (headercount = 1;;) { + struct devinfo *tmp_dinfo; + long tmp; + double etime; + + if (!--headercount) { phdr(0); - hdrcnt = 20; + headercount = 20; } - (void)kvm_read(kd, namelist[X_DK_TIME].n_value, - cur.dk_time, dk_ndrive * sizeof(long)); - (void)kvm_read(kd, namelist[X_DK_XFER].n_value, - cur.dk_xfer, dk_ndrive * sizeof(long)); - (void)kvm_read(kd, namelist[X_DK_WDS].n_value, - cur.dk_wds, dk_ndrive * sizeof(long)); - (void)kvm_read(kd, namelist[X_DK_SEEK].n_value, - cur.dk_seek, dk_ndrive * sizeof(long)); (void)kvm_read(kd, namelist[X_TK_NIN].n_value, &cur.tk_nin, sizeof(cur.tk_nin)); (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, &cur.tk_nout, sizeof(cur.tk_nout)); (void)kvm_read(kd, namelist[X_CP_TIME].n_value, cur.cp_time, sizeof(cur.cp_time)); - for (i = 0; i < dk_ndrive; i++) { - if (!dr_select[i]) - continue; -#define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp - X(dk_xfer); - X(dk_seek); - X(dk_wds); - X(dk_time); + + tmp_dinfo = last.dinfo; + last.dinfo = cur.dinfo; + cur.dinfo = tmp_dinfo; + + last.busy_time = cur.busy_time; + + /* + * Here what we want to do is refresh our device stats. + * 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 (getdevs(&cur)) { + case -1: + errx(1, "%s", devstat_errbuf); + break; + case 1: { + int retval; + + num_devices = cur.dinfo->numdevs; + generation = cur.dinfo->generation; + retval = 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(0); + headercount = 20; + 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 = 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(0); + headercount = 20; + break; + default: + break; + } + } + 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 = 0; + + etime = 0.0; + +#define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp + for (i = 0; i < CPUSTATES; i++) { X(cp_time); etime += cur.cp_time[i]; @@ -314,86 +501,164 @@ main(argc, argv) if (etime == 0.0) etime = 1.0; etime /= (float)hz; - (void)printf("%4.0f%5.0f", - cur.tk_nin / etime, cur.tk_nout / etime); - dkstats(); - cpustats(); - (void)printf("\n"); - (void)fflush(stdout); + if ((dflag == 0) || (Tflag > 0)) + printf("%4.0f%5.0f", cur.tk_nin / etime, + cur.tk_nout/etime); + devstats(hflag); + if ((dflag == 0) || (Cflag > 0)) + cpustats(); + printf("\n"); + fflush(stdout); - if (reps >= 0 && --reps <= 0) + if (count >= 0 && --count <= 0) break; - (void)sleep(interval); + + sleep(waittime); } + exit(0); } -/* ARGUSED */ -void -phdr(signo) - int signo; +static void +phdr(int signo) { register int i; + int printed; + + if ((dflag == 0) || (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; + if (oflag > 0) + (void)printf("%12.6s%d ", + cur.dinfo->devices[di].device_name, + cur.dinfo->devices[di].unit_number); + else + printf("%15.6s%d ", + cur.dinfo->devices[di].device_name, + cur.dinfo->devices[di].unit_number); + printed++; + } + } + if ((dflag == 0) || (Cflag > 0)) + (void)printf(" cpu\n"); + else + (void)printf("\n"); + + if ((dflag == 0) || (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 ((dflag == 0) || (Cflag > 0)) + (void)printf(" us ni sy in id\n"); + else + printf("\n"); - (void)printf(" tty"); - for (i = 0; i < dk_ndrive; i++) - if (dr_select[i]) - (void)printf(" %4.4s ", dr_name[i]); - (void)printf(" cpu\n tin tout"); - for (i = 0; i < dk_ndrive; i++) - if (dr_select[i]) - (void)printf(" sps tps msps "); - (void)printf(" us ni sy in id\n"); } -void -dkstats() +static void +devstats(int perf_select) { register int dn; - double atime, itime, msps, words, xtime; + long double transfers_per_second; + long double kb_per_transfer, mb_per_second; + u_int64_t total_bytes, total_transfers, total_blocks; + long double busy_seconds; + long double total_mb; + long double blocks_per_second, ms_per_transaction; + + /* + * Calculate elapsed time up front, since it's the same for all + * devices. + */ + busy_seconds = compute_etime(cur.busy_time, last.busy_time); - for (dn = 0; dn < dk_ndrive; ++dn) { - if (!dr_select[dn]) + for (dn = 0; dn < num_devices; dn++) { + int di; + + if (((perf_select == 0) && (dev_select[dn].selected == 0)) + || (dev_select[dn].selected > maxshowdevs)) continue; - words = (double)cur.dk_wds[dn] * 32; /* words xfer'd */ - (void)printf("%4.0f", /* sectors */ - words / (DEV_BSIZE / 2) / etime); - (void)printf("%4.0f", cur.dk_xfer[dn] / etime); + di = dev_select[dn].position; - if (dk_wpms[dn] && cur.dk_xfer[dn]) { - atime = cur.dk_time[dn]; /* ticks disk busy */ - atime /= (float)hz; /* ticks to seconds */ - xtime = words / dk_wpms[dn]; /* transfer time */ - itime = atime - xtime; /* time not xfer'ing */ - if (itime < 0) - msps = 0; - else - msps = itime * 1000 / cur.dk_xfer[dn]; - } else - msps = 0; - (void)printf("%5.1f ", msps); + if (compute_stats(&cur.dinfo->devices[di], + &last.dinfo->devices[di], busy_seconds, + &total_bytes, &total_transfers, + &total_blocks, &kb_per_transfer, + &transfers_per_second, &mb_per_second, + &blocks_per_second, &ms_per_transaction)!= 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 (oflag > 0) { + + if (Iflag == 0) + printf("%4.0Lf%4.0Lf%5.1Lf ", + blocks_per_second, + transfers_per_second, + ms_per_transaction); + else + printf("%4.1qu%4.1qu%5.1Lf ", + total_blocks, + total_transfers, + ms_per_transaction); + } else { + + if (Iflag == 0) + printf(" %5.2Lf %3.0Lf %5.2Lf ", + kb_per_transfer, + transfers_per_second, + mb_per_second); + else { + total_mb = total_bytes; + total_mb /= 1024 * 1024; + + printf(" %5.2Lf %3.1qu %5.2Lf ", + kb_per_transfer, + total_transfers, + total_mb); + } + } } } -void -cpustats() +static void +cpustats(void) { register int state; double time; - time = 0; + time = 0.0; + for (state = 0; state < CPUSTATES; ++state) time += cur.cp_time[state]; for (state = 0; state < CPUSTATES; ++state) - (void)printf("%3.0f", - 100. * cur.cp_time[state] / (time ? time : 1)); -} - -static void -usage() -{ - (void)fprintf(stderr, -"usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n"); - exit(1); + printf("%3.0f", + 100. * cur.cp_time[state] / (time ? time : 1)); }