+/* $OpenBSD: iostat.c,v 1.2 1996/03/03 02:52:34 tholo Exp $ */
+/* $NetBSD: iostat.c,v 1.8 1995/11/28 20:16:31 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1996 John M. Vinopal (banshee@resort.com)
+ * 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.
+ */
+
/*-
* Copyright (c) 1986, 1991, 1993
- * The Regents of the University of California. All rights reserved.
+ * 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
* 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.
+ * 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.
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1986, 1991, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
+ The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
-/* from: static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94"; */
-static char *rcsid = "$Id: iostat.c,v 1.1.1.1 1995/10/18 08:47:37 deraadt Exp $";
+#if 0
+static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94";
+#else
+static char *rcsid = "$NetBSD: iostat.c,v 1.8 1995/11/28 20:16:31 thorpej Exp $"
+;
+#endif
#endif /* not lint */
-#include <sys/param.h>
-#include <sys/buf.h>
#include <sys/dkstat.h>
+#include <sys/time.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>
-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
- { "_tk_nin" },
-#define X_TK_NOUT 4
- { "_tk_nout" },
-#define X_DK_SEEK 5
- { "_dk_seek" },
-#define X_CP_TIME 6
- { "_cp_time" },
-#define X_DK_WPMS 7
- { "_dk_wpms" },
-#define X_HZ 8
- { "_hz" },
-#define X_STATHZ 9
- { "_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
-#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
- { 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;
-
-#define nlread(x, v) \
- kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
-
-#include "names.c" /* XXX */
-
-void cpustats __P((void));
-void dkstats __P((void));
-void phdr __P((int));
-void usage __P((void));
+#include "dkstats.h"
+
+/* Defined in dkstats.c */
+extern struct _disk cur;
+extern int dk_ndrive;
+
+char *nlistf = NULL;
+char *memf = NULL;
+
+int hz, reps, interval;
+static int todo = 0;
+
+#define ISSET(x, a) ((x) & (a))
+#define SHOW_CPU 0x0001
+#define SHOW_TTY 0x0002
+#define SHOW_STATS_1 0x0004
+#define SHOW_STATS_2 0x0008
+#define SHOW_TOTALS 0x0080
+
+static void cpustats __P((void));
+static void disk_stats __P((double));
+static void disk_stats2 __P((double));
+static void header __P((int));
+static void usage __P((void));
+static void display __P((void));
+static void selectdrives __P((int, char **));
+
+void dkswap __P((void));
+void dkreadstats __P((void));
+int dkinit __P((int));
int
main(argc, argv)
int argc;
char *argv[];
{
- register int i;
- long tmp;
- int ch, hdrcnt, reps, interval, stathz, ndrives;
- char **cp, *memf, *nlistf, buf[30];
- char errbuf[_POSIX2_LINE_MAX];
-
- interval = reps = 0;
- nlistf = memf = NULL;
- while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF)
+ int ch, hdrcnt;
+ struct timeval tv;
+
+ while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:")) != EOF)
switch(ch) {
case 'c':
if ((reps = atoi(optarg)) <= 0)
errx(1, "repetition count <= 0.");
break;
+ case 'C':
+ todo |= SHOW_CPU;
+ break;
+ case 'd':
+ todo |= SHOW_STATS_1;
+ break;
+ case 'D':
+ todo |= SHOW_STATS_2;
+ break;
+ case 'I':
+ todo |= SHOW_TOTALS;
+ break;
case 'M':
memf = optarg;
break;
case 'N':
nlistf = optarg;
break;
+ case 'T':
+ todo |= SHOW_TTY;
+ break;
case 'w':
if ((interval = atoi(optarg)) <= 0)
errx(1, "interval <= 0.");
argc -= optind;
argv += optind;
+ if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_1 | SHOW_STATS_2))
+ todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
+
/*
* Discard setgid privileges if not the running kernel so that bad
* guys can't print interesting stuff from kernel memory.
if (nlistf != NULL || memf != NULL)
setgid(getgid());
- 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\n", 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 ] ]
- */
-#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;
+ dkinit(0);
+ dkreadstats();
+ selectdrives(argc, argv);
- 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;
- }
+ tv.tv_sec = interval;
+ tv.tv_usec = 0;
- (void)signal(SIGCONT, phdr);
+ /* print a new header on sigcont */
+ (void)signal(SIGCONT, header);
for (hdrcnt = 1;;) {
if (!--hdrcnt) {
- phdr(0);
+ header(0);
hdrcnt = 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 = 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;
- for (i = 0; i < CPUSTATES; i++) {
- X(cp_time);
- etime += cur.cp_time[i];
- }
- 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 (!ISSET(todo, SHOW_TOTALS))
+ dkswap();
+ display();
if (reps >= 0 && --reps <= 0)
break;
- (void)sleep(interval);
+ select(0, NULL, NULL, NULL, &tv);
+ dkreadstats();
}
exit(0);
}
-/* ARGUSED */
-void
-phdr(signo)
+static void
+header(signo)
int signo;
{
register int i;
- (void)printf(" tty");
+ /* Main Headers. */
+ if (ISSET(todo, SHOW_TTY))
+ (void)printf(" tty");
+
+ if (ISSET(todo, SHOW_STATS_1))
+ for (i = 0; i < dk_ndrive; i++)
+ if (cur.dk_select[i])
+ (void)printf(" %3.3s ", cur.dk_name[i]);
+
+ if (ISSET(todo, SHOW_STATS_2))
for (i = 0; i < dk_ndrive; i++)
- if (dr_select[i])
- (void)printf(" %3.3s ", dr_name[i]);
- (void)printf(" cpu\n tin tout");
+ if (cur.dk_select[i])
+ (void)printf(" %3.3s ", cur.dk_name[i]);
+
+ if (ISSET(todo, SHOW_CPU))
+ (void)printf(" cpu");
+ printf("\n");
+
+ /* Sub-Headers. */
+ if (ISSET(todo, SHOW_TTY))
+ printf(" tin tout");
+
+ if (ISSET(todo, SHOW_STATS_1))
+ for (i = 0; i < dk_ndrive; i++)
+ if (cur.dk_select[i])
+ if (ISSET(todo, SHOW_TOTALS))
+ (void)printf(" K/t xfr Mb ");
+ else
+ (void)printf(" K/t t/s Mb/s ");
+
+ if (ISSET(todo, SHOW_STATS_2))
for (i = 0; i < dk_ndrive; i++)
- if (dr_select[i])
- (void)printf(" sps tps msps ");
- (void)printf(" us ni sy in id\n");
+ if (cur.dk_select[i])
+ (void)printf(" Kb xfr time ");
+
+ if (ISSET(todo, SHOW_CPU))
+ (void)printf(" us ni sy in id");
+ printf("\n");
+}
+
+static void
+disk_stats(etime)
+double etime;
+{
+ register int dn;
+ double atime, mbps;
+
+ for (dn = 0; dn < dk_ndrive; ++dn) {
+ if (!cur.dk_select[dn])
+ continue;
+
+ /* average Kbytes per transfer. */
+ if (cur.dk_xfer[dn])
+ mbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn];
+ else
+ mbps = 0.0;
+ (void)printf(" %5.2f", mbps);
+
+ /* average transfers per second. */
+ (void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
+
+ /* time busy in disk activity */
+ atime = (double)cur.dk_time[dn].tv_sec +
+ ((double)cur.dk_time[dn].tv_usec / (double)1000000);
+
+ /* Megabytes per second. */
+ if (atime != 0.0)
+ mbps = cur.dk_bytes[dn] / (double)(1024 * 1024);
+ else
+ mbps = 0;
+ (void)printf(" %4.2f ", mbps / etime);
+ }
}
-void
-dkstats()
+static void
+disk_stats2(etime)
+double etime;
{
register int dn;
- double atime, itime, msps, words, xtime;
+ double atime;
for (dn = 0; dn < dk_ndrive; ++dn) {
- if (!dr_select[dn])
+ if (!cur.dk_select[dn])
continue;
- words = 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);
-
- 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);
+
+ /* average kbytes per second. */
+ (void)printf(" %4.0f", cur.dk_bytes[dn] / (1024.0) / etime);
+
+ /* average transfers per second. */
+ (void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
+
+ /* average time busy in disk activity. */
+ atime = (double)cur.dk_time[dn].tv_sec +
+ ((double)cur.dk_time[dn].tv_usec / (double)1000000);
+ (void)printf(" %4.2f ", atime / etime);
}
}
-void
+static void
cpustats()
{
register int state;
time = 0;
for (state = 0; state < CPUSTATES; ++state)
time += cur.cp_time[state];
+ if (!time)
+ time = 1.0;
+ /* States are generally never 100% and can use %3.0f. */
for (state = 0; state < CPUSTATES; ++state)
- (void)printf("%3.0f",
- 100. * cur.cp_time[state] / (time ? time : 1));
+ printf("%3.0f", 100. * cur.cp_time[state] / time);
}
-void
+static void
usage()
{
(void)fprintf(stderr,
-"usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n");
+"usage: iostat [-CdDIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n");
exit(1);
}
+
+static void
+display()
+{
+ int i;
+ double etime;
+
+ /* Sum up the elapsed ticks. */
+ etime = 0.0;
+ for (i = 0; i < CPUSTATES; i++) {
+ etime += cur.cp_time[i];
+ }
+ if (etime == 0.0)
+ etime = 1.0;
+ /* Convert to seconds. */
+ etime /= (float)hz;
+
+ /* If we're showing totals only, then don't divide by the
+ * system time.
+ */
+ if (ISSET(todo, SHOW_TOTALS))
+ etime = 1.0;
+
+ if (ISSET(todo, SHOW_TTY))
+ printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
+
+ if (ISSET(todo, SHOW_STATS_1))
+ disk_stats(etime);
+
+ if (ISSET(todo, SHOW_STATS_2))
+ disk_stats2(etime);
+
+ if (ISSET(todo, SHOW_CPU))
+ cpustats();
+
+ (void)printf("\n");
+ (void)fflush(stdout);
+}
+
+static void
+selectdrives(argc, argv)
+int argc;
+char *argv[];
+{
+ int i, ndrives;
+
+ /*
+ * 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 ] ]
+ */
+#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(cur.dk_name[i], *argv))
+ continue;
+ cur.dk_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;
+
+ /* Pick up to 4 drives if none specified. */
+ if (ndrives == 0)
+ for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
+ if (cur.dk_select[i])
+ continue;
+ cur.dk_select[i] = 1;
+ ++ndrives;
+ }
+}