Update system to new device statistics code.

Submitted by:	"Kenneth D. Merry" <ken@plutotech.com>
This commit is contained in:
Justin T. Gibbs 1998-09-15 08:16:45 +00:00
parent 7a59208d92
commit 8d2fbde504
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=39230
11 changed files with 1767 additions and 613 deletions

320
usr.bin/systat/devs.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/devicestat.h>
#include <sys/dkstat.h>
#include <string.h>
#include <devstat.h>
#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);
}

View File

@ -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 *));

View File

@ -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 <sys/param.h>
#include <sys/dkstat.h>
#include <sys/buf.h>
#include <sys/dkstat.h>
#include <string.h>
#include <stdlib.h>
#include <nlist.h>
#include <paths.h>
#include <devstat.h>
#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();

View File

@ -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 ,

View File

@ -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 <sys/param.h>
#include <sys/dkstat.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/time.h>
@ -52,6 +51,7 @@ static const char rcsid[] =
#include <sys/uio.h>
#include <sys/namei.h>
#include <sys/sysctl.h>
#include <sys/dkstat.h>
#include <sys/vmmeter.h>
#include <vm/vm_param.h>
@ -66,6 +66,7 @@ static const char rcsid[] =
#include <time.h>
#include <unistd.h>
#include <utmp.h>
#include <devstat.h>
#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);
}

View File

@ -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 <bsd.prog.mk>

View File

@ -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

View File

@ -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 <sys/param.h>
@ -74,48 +74,45 @@ static const char rcsid[] =
#include <sysexits.h>
#include <time.h>
#include <unistd.h>
#include <devstat.h>
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

View File

@ -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

View File

@ -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

View File

@ -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 <sys/param.h>
#include <sys/buf.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/dkstat.h>
#include <err.h>
#include <ctype.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <nlist.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <devstat.h>
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));
}