summaryrefslogtreecommitdiffstats
path: root/lib/libdevstat
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1998-09-15 06:23:21 +0000
committergibbs <gibbs@FreeBSD.org>1998-09-15 06:23:21 +0000
commitbbc0682d67a7e0c50ade831509abd8c3e119db5c (patch)
treee1df1a384f62c98a156b19960095d69f22769b78 /lib/libdevstat
parentade38c7f132fc2fb24216396d5a415f8bdadc5ff (diff)
downloadFreeBSD-src-bbc0682d67a7e0c50ade831509abd8c3e119db5c.zip
FreeBSD-src-bbc0682d67a7e0c50ade831509abd8c3e119db5c.tar.gz
Device statistics library. Used by iostat, vmstat, and systat to
format and analyze the output of the new device statistics sysctls. Submitted by: "Kenneth D. Merry" <ken@FreeBSD.org>
Diffstat (limited to 'lib/libdevstat')
-rw-r--r--lib/libdevstat/Makefile13
-rw-r--r--lib/libdevstat/devstat.3525
-rw-r--r--lib/libdevstat/devstat.c1102
-rw-r--r--lib/libdevstat/devstat.h115
4 files changed, 1755 insertions, 0 deletions
diff --git a/lib/libdevstat/Makefile b/lib/libdevstat/Makefile
new file mode 100644
index 0000000..ce3f9c2
--- /dev/null
+++ b/lib/libdevstat/Makefile
@@ -0,0 +1,13 @@
+
+LIB= devstat
+SRCS= devstat.c
+
+MAN3= devstat.3
+
+beforeinstall:
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/devstat.h \
+ ${DESTDIR}/usr/include
+
+CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../sys
+
+.include <bsd.lib.mk>
diff --git a/lib/libdevstat/devstat.3 b/lib/libdevstat/devstat.3
new file mode 100644
index 0000000..189f703
--- /dev/null
+++ b/lib/libdevstat/devstat.3
@@ -0,0 +1,525 @@
+.\"
+.\" 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$
+.\"
+.Dd May 21, 1998
+.Dt DEVSTAT 3
+.Os FreeBSD 3.0
+.Sh NAME
+.Nm devstat
+.Nd device statistics utility library
+.Sh SYNOPSIS
+.Fd #include <devstat.h>
+.Ft int
+.Fn getnumdevs "void"
+.Ft int
+.Fn getgeneration "void"
+.Ft int
+.Fn getversion "void"
+.Ft int
+.Fn checkversion "void"
+.Ft int
+.Fn getdevs "struct statinfo *stats"
+.Ft int
+.Fo selectdevs
+.Fa "struct device_selection **dev_select"
+.Fa "int *num_selected"
+.Fa "int *num_selections"
+.Fa "int *select_generation"
+.Fa "int current_generation"
+.Fa "struct devstat *devices"
+.Fa "int numdevs"
+.Fa "struct devstat_match *matches"
+.Fa "int num_matches"
+.Fa "char **dev_selections"
+.Fa "int num_dev_selections"
+.Fa "devstat_select_mode select_mode"
+.Fa "int maxshowdevs"
+.Fa "int perf_select"
+.Fc
+.Ft int
+.Fo buildmatch
+.Fa "char *match_str"
+.Fa "struct devstat_match **matches"
+.Fa "int *num_matches"
+.Fc
+.Ft int
+.Fo compute_stats
+.Fa "struct devstat *current"
+.Fa "struct devstat *previous"
+.Fa "long double etime"
+.Fa "u_int64_t *total_bytes"
+.Fa "u_int64_t *total_transfers"
+.Fa "u_int64_t *total_blocks"
+.Fa "long double *kb_per_transfer"
+.Fa "long double *transfers_per_second"
+.Fa "long double *mb_per_second"
+.Fa "long double *blocks_per_second"
+.Fa "long double *ms_per_transaction"
+.Fc
+.Ft long double
+.Fo compute_etime
+.Fa "struct timeval cur_time"
+.Fa "struct timeval prev_time"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm
+library is a library of helper functions for dealing with the kernel
+.Xr devstat 9
+interface, which is accessible to users via
+.Xr sysctl 3 .
+.Pp
+.Fn getnumdevs
+returns the number of devices registered with the
+.Nm devstat
+subsystem in the kernel.
+.Pp
+.Fn getgeneration
+returns the current generation of the
+.Nm devstat
+list of devices in the kernel.
+.Pp
+.Fn getversion
+returns the current kernel
+.Nm devstat
+version.
+.Pp
+.Fn checkversion
+checks the userland devstat version against the kernel devstat version. If
+the two are identical, it returns zero. Otherwise, it prints an
+appropriate error in
+.Va devstat_errbuf
+and returns -1.
+.Pp
+.Fn getdevs
+fetches the current list of devices and statistics into the supplied
+.Va statinfo
+structure. The
+.Va statinfo
+structure can be found in
+.Aq Pa devstat.h :
+.Bd -literal -offset indent
+struct statinfo {
+ long cp_time[CPUSTATES];
+ long tk_nin;
+ long tk_nout;
+ struct devinfo *dinfo;
+ struct timeval busy_time;
+};
+.Ed
+.Pp
+.Fn getdevs
+expects the
+.Va statinfo
+structure to be allocated, and it also expects the
+.Va dinfo
+subelement to be allocated ahead of time. The
+.Va dinfo
+subelement contains the following elements:
+.Bd -literal -offset indent
+struct devinfo {
+ struct devstat *devices;
+ u_int8_t *mem_ptr;
+ int generation;
+ int numdevs;
+};
+.Ed
+.Pp
+The
+.Va kern.devstat.all
+.Nm sysctl
+variable contains an array of
+.Nm devstat
+structures, but at the head of the array is the current
+.Nm devstat
+generation. The reason the generation is at the head of the buffer is so
+that userland software accessing the devstat statistics information can
+atomically get both the statistics information and the corresponding
+generation number. If client software were forced to get the generation
+number via a separate
+.Nm sysctl
+variable (which is available for convenience), the list of devices could
+change between the time the client gets the generation and the time the
+client gets the device list.
+.Pp
+The
+.Va mem_ptr
+subelement of the
+.Va devinfo
+structure is a pointer to memory that is allocated, and resized if
+necessary, by
+.Fn getdevs .
+The devices subelement of the
+.Va devinfo
+structure is basically a pointer to the beginning of the array of devstat
+structures from the
+.Va kern.devstat.all
+.Nm sysctl
+variable. The generation subelement of the
+.Nm devinfo
+structure contains the generation number from the
+.Va kern.devstat.all
+.Nm sysctl
+variable.
+The
+.Va numdevs
+subelement of the
+.Va devinfo
+structure contains the current
+number of devices registered with the kernel
+.Nm devstat
+subsystem.
+.Pp
+.Fn selectdevs
+selects devices to display based upon a number of criteria:
+.Bl -tag -width flag
+.It specified devices
+Specified devices are the first selection priority. These are generally
+devices specified by name by the user. e.g. da0, da1, cd0.
+.It match patterns
+These are pattern matching expressions generated by
+.Fn buildmatch
+from user input.
+.It performance
+If performance mode is enabled, devices will be sorted based on the
+.Va bytes
+field in the
+.Va device_selection
+structure passed in to
+.Fn selectdevs .
+The
+.Va bytes
+value currently must be maintained by the user. In the future,
+this may be done for him in a
+.Nm
+library routine.
+If no devices have been selected by name or by pattern, the performance
+tracking code will select every device in the system, and sort them by
+perforcmance. If devices have been selected by name or pattern, the
+performance tracking code will honor those selections and will only sort
+among the selected devices.
+.It order in the devstat list
+If the selection mode is set to DS_SELECT_ADD, and if there are still less
+than
+.Va maxshowdevs
+devices selected,
+.Fn selectdevs
+will automatically select up to
+.Va maxshowdevs
+devices.
+.El
+.Pp
+.Fn selectdevs
+performs selections in four different modes:
+.Bl -tag -width DS_SELECT_ADDONLY
+.It DS_SELECT_ADD
+In add mode,
+.Fn selectdevs
+will select any unselected devices specified by name or matching pattern.
+It will also select more devices, in devstat list order, until the number
+of selected devices is equal to
+.Va maxshowdevs
+or until all devices are
+selected.
+.It DS_SELECT_ONLY
+In only mode,
+.Fn selectdevs
+will clear all current selections, and will only select devices specified
+by name or by matching pattern.
+.It DS_SELECT_REMOVE
+In remove mode,
+.Fn selectdevs
+will remove devices specified by name or by matching pattern. It will not
+select any additional devices.
+.It DS_SELECT_ADDONLY
+In addonly mode,
+.Fn selectdevs
+will select any unselected devices specified by name or matching pattern.
+In this respect it is identical to add mode. It will not, however, select
+any devices other than those specified.
+.El
+.Pp
+In all selection modes,
+.Fn selectdevs
+will not select any more than
+.Va maxshowdevs
+devices. One exception to
+this is when you are in
+.Dq top
+mode and no devices have been selected. In
+this case,
+.Fn selectdevs
+will select every device in the system. Client programs must pay attention
+to selection order when deciding whether to pay attention to a particular
+device. This may be the wrong behavior, and probably requires additional
+thought.
+.Pp
+.Fn selectdevs
+handles allocation and resizing of the
+.Va dev_select
+structure passed in
+by the client.
+.Fn selectdevs
+uses the
+.Va numdevs
+and
+.Va current_generation
+fields to track the
+current
+.Nm
+generation and number of devices. If
+.Va num_selections
+is not the same
+as
+.Va numdevs
+or if
+.Va select_generation
+is not the same as
+.Va current_generation ,
+.Fn selectdevs
+will resize the selection list as necessary, and re-initialize the
+selection array.
+.Pp
+.Fn buildmatch
+takes a comma separated match string and compiles it into a
+\fBdevstat_match\fR structure that is understood by
+.Fn selectdevs .
+Match strings have the following format:
+.Pp
+.Bd -literal -offset indent
+device,type,if
+.Ed
+.Pp
+.Fn buildmatch
+takes care of allocating and reallocating the match list as necessary.
+Currently known match types include:
+.Pp
+.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
+.Fn compute_stats
+provides an easy way to obtain various device statistics. Only two
+arguments are mandatory:
+.Va current
+and
+.Va etime .
+Every other argument is optional. For most applications, the user will
+want to supply both
+.Va current
+and
+.Va previous
+devstat structures so that statistics may be calculated over a given period
+of time. In some instances, for instance when calculating statistics since
+system boot, the user may pass in a NULL pointer for the
+.Va previous
+argument. In that case,
+.Fn compute_stats
+will use the total stats in the
+.Va current
+structure to calculate statistics over
+.Va etime .
+The various statistics that may be calculated by
+.Fn compute_stats
+should be mostly explained by the function declaration itself, but for
+completeness here is a list of variable names and the statistics that will
+be put in them:
+.Bl -tag -width transfers_per_second
+.It total_bytes
+This is the total number of bytes transferred on the given device, both
+reads and writes, between the acquisition of
+.Va previous
+and the acquisition of
+.Va current .
+If
+.Va previous
+is NULL, the result will be the total reads and writes given in
+.Va current .
+.It total_transfers
+This is the total number of transfers completed between the
+acquisition of
+.Va previous
+and the acquisition of
+.Va current .
+If
+.Va previous
+is NULL, the result will be the total number of transactions listed in
+.Va current .
+.It total_blocks
+This is basically
+.Va total_bytes
+divided by the device blocksize. If the device blocksize is listed as
+.Sq 0 ,
+the device blocksize will default to 512 bytes.
+.It kb_per_transfer
+This is the average number of kilobytes per transfer during the measurement
+period.
+.It transfers_per_second
+This is the average number of transfers per second.
+.It mb_per_second
+This is average megabytes per second.
+.It blocks_per_second
+This is average blocks per second. If the device blocksize is
+.Sq 0 ,
+a default blocksize of 512 bytes will be used instead.
+.It ms_per_transaction
+The average number of miliseconds per transaction.
+.El
+.Pp
+.Fn compute_etime
+provides an easy way to find the difference in seconds between two
+.Va timeval
+structures. This is most commonly used in conjunction with the time
+recorded by the
+.Fn getdevs
+function (in struct
+.Va statinfo )
+each time it fetches the current
+.Nm
+list.
+.Sh RETURN VALUES
+.Fn getnumdevs ,
+.Fn getgeneration ,
+and
+.Fn getversion
+return the indicated \fBsysctl\fR variable, or -1 if there is an error
+fetching the variable.
+.Pp
+.Fn checkversion
+returns 0 if the kernel and userland
+.Nm devstat
+versions match. If they do not match, it returns -1.
+.Pp
+.Fn getdevs
+and
+.Fn selectdevs
+return -1 in case of an error, 0 if there is no error and 1 if the device
+list or selected devices have changed. A return value of 1 from
+.Fn getdevs
+is usually a hint to re-run
+.Fn selectdevs
+because the device list has changed.
+.Pp
+.Fn buildmatch
+returns -1 for error, and 0 if there is no error.
+.Pp
+.Fn compute_stats
+returns -1 for error, and 0 for success.
+.Pp
+.Fn compute_etime
+returns the computed elapsed time.
+.Pp
+If an error is returned from one of the
+.Nm
+library functions, the reason for the error is generally printed in
+the global string
+.Va devstat_errbuf
+which is
+.Dv DEVSTAT_ERRBUF_SIZE
+characters long.
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr iostat 8 ,
+.Xr rpc.rstatd 8 ,
+.Xr vmstat 8 ,
+.Xr devstat 9
+.Sh HISTORY
+The
+.Nm
+statistics system first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+Kenneth Merry
+.Aq ken@FreeBSD.ORG
+.Sh BUGS
+There should probably be an interface to de-allocate memory allocated by
+.Fn getdevs ,
+.Fn selectdevs ,
+and
+.Fn buildmatch .
+.Pp
+.Fn selectdevs
+should probably not select more than
+.Va maxshowdevs
+devices in
+.Dq top
+mode when no devices have been selected previously.
+.Pp
+There should probably be functions to perform the statistics buffer
+swapping that goes on in most of the clients of this library.
+.Pp
+The
+.Va statinfo
+and
+.Va devinfo
+structures should probably be cleaned up and thought out a little more.
diff --git a/lib/libdevstat/devstat.c b/lib/libdevstat/devstat.c
new file mode 100644
index 0000000..9c7828f
--- /dev/null
+++ b/lib/libdevstat/devstat.c
@@ -0,0 +1,1102 @@
+/*
+ * 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$
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <sys/dkstat.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "devstat.h"
+
+char devstat_errbuf[DEVSTAT_ERRBUF_SIZE];
+
+/*
+ * Table to match descriptive strings with device types. These are in
+ * order from most common to least common to speed search time.
+ */
+struct devstat_match_table match_table[] = {
+ {"da", DEVSTAT_TYPE_DIRECT, DEVSTAT_MATCH_TYPE},
+ {"cd", DEVSTAT_TYPE_CDROM, DEVSTAT_MATCH_TYPE},
+ {"scsi", DEVSTAT_TYPE_IF_SCSI, DEVSTAT_MATCH_IF},
+ {"ide", DEVSTAT_TYPE_IF_IDE, DEVSTAT_MATCH_IF},
+ {"other", DEVSTAT_TYPE_IF_OTHER, DEVSTAT_MATCH_IF},
+ {"worm", DEVSTAT_TYPE_WORM, DEVSTAT_MATCH_TYPE},
+ {"sa", DEVSTAT_TYPE_SEQUENTIAL,DEVSTAT_MATCH_TYPE},
+ {"pass", DEVSTAT_TYPE_PASS, DEVSTAT_MATCH_PASS},
+ {"optical", DEVSTAT_TYPE_OPTICAL, DEVSTAT_MATCH_TYPE},
+ {"array", DEVSTAT_TYPE_STORARRAY, DEVSTAT_MATCH_TYPE},
+ {"changer", DEVSTAT_TYPE_CHANGER, DEVSTAT_MATCH_TYPE},
+ {"scanner", DEVSTAT_TYPE_SCANNER, DEVSTAT_MATCH_TYPE},
+ {"printer", DEVSTAT_TYPE_PRINTER, DEVSTAT_MATCH_TYPE},
+ {"floppy", DEVSTAT_TYPE_FLOPPY, DEVSTAT_MATCH_TYPE},
+ {"proc", DEVSTAT_TYPE_PROCESSOR, DEVSTAT_MATCH_TYPE},
+ {"comm", DEVSTAT_TYPE_COMM, DEVSTAT_MATCH_TYPE},
+ {"enclosure", DEVSTAT_TYPE_ENCLOSURE, DEVSTAT_MATCH_TYPE},
+ {NULL, 0, 0}
+};
+
+/*
+ * Local function declarations.
+ */
+static int compare_select(const void *arg1, const void *arg2);
+
+int
+getnumdevs(void)
+{
+ size_t numdevsize;
+ int numdevs;
+ char *func_name = "getnumdevs";
+
+ numdevsize = sizeof(int);
+
+ /*
+ * Find out how many devices we have in the system.
+ */
+ if (sysctlbyname("kern.devstat.numdevs", &numdevs,
+ &numdevsize, NULL, 0) == -1) {
+ sprintf(devstat_errbuf, "%s: error getting number of devices\n"
+ "%s: %s", func_name, func_name, strerror(errno));
+ return(-1);
+ } else
+ return(numdevs);
+}
+
+/*
+ * This is an easy way to get the generation number, but the generation is
+ * supplied in a more atmoic manner by the kern.devstat.all sysctl.
+ * Because this generation sysctl is separate from the statistics sysctl,
+ * the device list and the generation could change between the time that
+ * this function is called and the device list is retreived.
+ */
+int
+getgeneration(void)
+{
+ size_t gensize;
+ int generation;
+ char *func_name = "getgeneration";
+
+ gensize = sizeof(int);
+
+ /*
+ * Get the current generation number.
+ */
+ if (sysctlbyname("kern.devstat.generation", &generation,
+ &gensize, NULL, 0) == -1) {
+ sprintf(devstat_errbuf,"%s: error getting devstat generation\n"
+ "%s: %s", func_name, func_name, strerror(errno));
+ return(-1);
+ } else
+ return(generation);
+}
+
+/*
+ * Get the current devstat version. The return value of this function
+ * should be compared with DEVSTAT_VERSION, which is defined in
+ * sys/devicestat.h. This will enable userland programs to determine
+ * whether they are out of sync with the kernel.
+ */
+int
+getversion(void)
+{
+ size_t versize;
+ int version;
+ char *func_name = "getversion";
+
+ versize = sizeof(int);
+
+ /*
+ * Get the current devstat version.
+ */
+ if (sysctlbyname("kern.devstat.version", &version, &versize,
+ NULL, 0) == -1) {
+ sprintf(devstat_errbuf, "%s: error getting devstat version\n"
+ "%s: %s", func_name, func_name, strerror(errno));
+ return(-1);
+ } else
+ return(version);
+}
+
+/*
+ * Check the devstat version we know about against the devstat version the
+ * kernel knows about. If they don't match, print an error into the
+ * devstat error buffer, and return -1. If they match, return 0.
+ */
+int
+checkversion(void)
+{
+ int retval = 0;
+ int errlen = 0;
+ char *func_name = "checkversion";
+
+ if (getversion() != DEVSTAT_VERSION) {
+ char tmpstr[256];
+
+ errlen = snprintf(devstat_errbuf, DEVSTAT_ERRBUF_SIZE,
+ "%s: userland devstat version %d is not "
+ "the same as the kernel\n%s: devstat "
+ "version %d\n", func_name, DEVSTAT_VERSION,
+ func_name, getversion());
+
+ if (getversion() < DEVSTAT_VERSION)
+ snprintf(tmpstr, sizeof(tmpstr),
+ "%s: you really should know better"
+ " than to upgrade your\n%s: "
+ "userland binaries without "
+ "upgrading your kernel\n",
+ func_name, func_name);
+ else
+ snprintf(tmpstr, sizeof(tmpstr),
+ "%s: you really should know better"
+ " than to upgrade your kernel "
+ "without\n%s: upgrading your "
+ "userland binaries\n",
+ func_name, func_name);
+
+ strncat(devstat_errbuf, tmpstr,
+ DEVSTAT_ERRBUF_SIZE - errlen - 1);
+
+ retval = -1;
+ }
+
+ return(retval);
+}
+
+/*
+ * Get the current list of devices and statistics, and the current
+ * generation number.
+ *
+ * Return values:
+ * -1 -- error
+ * 0 -- device list is unchanged
+ * 1 -- device list has changed
+ */
+int
+getdevs(struct statinfo *stats)
+{
+ int error;
+ size_t dssize;
+ int oldnumdevs, oldgeneration;
+ int retval = 0;
+ struct devinfo *dinfo;
+ char *func_name = "getdevs";
+
+ dinfo = stats->dinfo;
+
+ if (dinfo == NULL) {
+ sprintf(devstat_errbuf, "%s: stats->dinfo was NULL", func_name);
+ return(-1);
+ }
+
+ oldnumdevs = dinfo->numdevs;
+ oldgeneration = dinfo->generation;
+
+ /*
+ * If this is our first time through, mem_ptr will be null.
+ */
+ if (dinfo->mem_ptr == NULL) {
+ /*
+ * Get the number of devices. If it's negative, it's an
+ * error. Don't bother setting the error string, since
+ * getnumdevs() has already done that for us.
+ */
+ if ((dinfo->numdevs = getnumdevs()) < 0)
+ return(-1);
+
+ /*
+ * The kern.devstat.all sysctl returns the current generation
+ * number, as well as all the devices. So we need four
+ * bytes more.
+ */
+ dssize =(dinfo->numdevs * sizeof(struct devstat)) + sizeof(int);
+ dinfo->mem_ptr = (u_int8_t *)malloc(dssize);
+ } else
+ dssize =(dinfo->numdevs * sizeof(struct devstat)) + sizeof(int);
+
+ /* Get the current time when we get the stats */
+ gettimeofday(&stats->busy_time, NULL);
+
+ /*
+ * Request all of the devices. We only really allow for one
+ * ENOMEM failure. It would, of course, be possible to just go in
+ * a loop and keep reallocing the device structure until we don't
+ * get ENOMEM back. I'm not sure it's worth it, though. If
+ * devices are being added to the system that quickly, maybe the
+ * user can just wait until all devices are added.
+ */
+ if ((error = sysctlbyname("kern.devstat.all", dinfo->mem_ptr,
+ &dssize, NULL, 0)) == -1) {
+ /*
+ * If we get ENOMEM back, that means that there are
+ * more devices now, so we need to allocate more
+ * space for the device array.
+ */
+ if (errno == ENOMEM) {
+ /*
+ * No need to set the error string here, getnumdevs()
+ * will do that if it fails.
+ */
+ if ((dinfo->numdevs = getnumdevs()) < 0)
+ return(-1);
+
+ dssize = (dinfo->numdevs * sizeof(struct devstat)) +
+ sizeof(int);
+ dinfo->mem_ptr = (u_int8_t *)realloc(dinfo->mem_ptr,
+ dssize);
+ if ((error = sysctlbyname("kern.devstat.all",
+ dinfo->mem_ptr, &dssize, NULL, 0)) == -1) {
+ sprintf(devstat_errbuf,
+ "%s: error getting device stats\n"
+ "%s: %s", func_name, func_name,
+ strerror(errno));
+ return(-1);
+ }
+ } else {
+ sprintf(devstat_errbuf,
+ "%s: error getting device stats\n"
+ "%s: %s", func_name, func_name,
+ strerror(errno));
+ return(-1);
+ }
+ }
+
+ /*
+ * The sysctl spits out the generation as the first four bytes,
+ * then all of the device statistics structures.
+ */
+ dinfo->generation = *(int *)dinfo->mem_ptr;
+
+ /*
+ * If the generation has changed, and if the current number of
+ * devices is not the same as the number of devices recorded in the
+ * devinfo structure, it is likely that the device list has shrunk.
+ * The reason that it is likely that the device list has shrunk in
+ * this case is that if the device list has grown, the sysctl above
+ * will return an ENOMEM error, and we will reset the number of
+ * devices and reallocate the device array. If the second sysctl
+ * fails, we will return an error and therefore never get to this
+ * point. If the device list has shrunk, the sysctl will not
+ * return an error since we have more space allocated than is
+ * necessary. So, in the shrinkage case, we catch it here and
+ * reallocate the array so that we don't use any more space than is
+ * necessary.
+ */
+ if (oldgeneration != dinfo->generation) {
+ if (getnumdevs() != dinfo->numdevs) {
+ if ((dinfo->numdevs = getnumdevs()) < 0)
+ return(-1);
+ dssize = (dinfo->numdevs * sizeof(struct devstat)) +
+ sizeof(int);
+ dinfo->mem_ptr = (u_int8_t *)realloc(dinfo->mem_ptr,
+ dssize);
+ }
+ retval = 1;
+ }
+
+ dinfo->devices = (struct devstat *)(dinfo->mem_ptr + sizeof(int));
+
+ return(retval);
+}
+
+/*
+ * selectdevs():
+ *
+ * Devices are selected/deselected based upon the following criteria:
+ * - devices specified by the user on the command line
+ * - devices matching any device type expressions given on the command line
+ * - devices with the highest I/O, if 'top' mode is enabled
+ * - the first n unselected devices in the device list, if maxshowdevs
+ * devices haven't already been selected and if the user has not
+ * specified any devices on the command line and if we're in "add" mode.
+ *
+ * Input parameters:
+ * - device selection list (dev_select)
+ * - current number of devices selected (num_selected)
+ * - total number of devices in the selection list (num_selections)
+ * - devstat generation as of the last time selectdevs() was called
+ * (select_generation)
+ * - current devstat generation (current_generation)
+ * - current list of devices and statistics (devices)
+ * - number of devices in the current device list (numdevs)
+ * - compiled version of the command line device type arguments (matches)
+ * - This is optional. If the number of devices is 0, this will be ignored.
+ * - The matching code pays attention to the current selection mode. So
+ * if you pass in a matching expression, it will be evaluated based
+ * upon the selection mode that is passed in. See below for details.
+ * - number of device type matching expressions (num_matches)
+ * - Set to 0 to disable the matching code.
+ * - list of devices specified on the command line by the user (dev_selections)
+ * - number of devices selected on the command line by the user
+ * (num_dev_selections)
+ * - Our selection mode. There are four different selection modes:
+ * - add mode. (DS_SELECT_ADD) Any devices matching devices explicitly
+ * selected by the user or devices matching a pattern given by the
+ * user will be selected in addition to devices that are already
+ * selected. Additional devices will be selected, up to maxshowdevs
+ * number of devices.
+ * - only mode. (DS_SELECT_ONLY) Only devices matching devices
+ * explicitly given by the user or devices matching a pattern
+ * given by the user will be selected. No other devices will be
+ * selected.
+ * - addonly mode. (DS_SELECT_ADDONLY) This is similar to add and
+ * only. Basically, this will not de-select any devices that are
+ * current selected, as only mode would, but it will also not
+ * gratuitously select up to maxshowdevs devices as add mode would.
+ * - remove mode. (DS_SELECT_REMOVE) Any devices matching devices
+ * explicitly selected by the user or devices matching a pattern
+ * given by the user will be de-selected.
+ * - maximum number of devices we can select (maxshowdevs)
+ * - flag indicating whether or not we're in 'top' mode (perf_select)
+ *
+ * Output data:
+ * - the device selection list may be modified and passed back out
+ * - the number of devices selected and the total number of items in the
+ * device selection list may be changed
+ * - the selection generation may be changed to match the current generation
+ *
+ * Return values:
+ * -1 -- error
+ * 0 -- selected devices are unchanged
+ * 1 -- selected devices changed
+ */
+int
+selectdevs(struct device_selection **dev_select, int *num_selected,
+ int *num_selections, int *select_generation,
+ int current_generation, struct devstat *devices, int numdevs,
+ struct devstat_match *matches, int num_matches,
+ char **dev_selections, int num_dev_selections,
+ devstat_select_mode select_mode, int maxshowdevs, int perf_select)
+{
+ register int i, j, k;
+ int init_selections = 0, init_selected_var = 0;
+ struct device_selection *old_dev_select = NULL;
+ int old_num_selections = 0, old_num_selected;
+ int selection_number = 0;
+ int changed = 0, found = 0;
+
+ if ((dev_select == NULL) || (devices == NULL) || (numdevs <= 0))
+ return(-1);
+
+ /*
+ * We always want to make sure that we have as many dev_select
+ * entries as there are devices.
+ */
+ /*
+ * In this case, we haven't selected devices before.
+ */
+ if (*dev_select == NULL) {
+ *dev_select = (struct device_selection *)malloc(numdevs *
+ sizeof(struct device_selection));
+ *select_generation = current_generation;
+ init_selections = 1;
+ changed = 1;
+ /*
+ * In this case, we have selected devices before, but the device
+ * list has changed since we last selected devices, so we need to
+ * either enlarge or reduce the size of the device selection list.
+ */
+ } else if (*num_selections != numdevs) {
+ *dev_select = (struct device_selection *)realloc(*dev_select,
+ numdevs * sizeof(struct device_selection));
+ *select_generation = current_generation;
+ init_selections = 1;
+ /*
+ * In this case, we've selected devices before, and the selection
+ * list is the same size as it was the last time, but the device
+ * list has changed.
+ */
+ } else if (*select_generation < current_generation) {
+ *select_generation = current_generation;
+ init_selections = 1;
+ }
+
+ /*
+ * If we're in "only" mode, we want to clear out the selected
+ * variable since we're going to select exactly what the user wants
+ * this time through.
+ */
+ if (select_mode == DS_SELECT_ONLY)
+ init_selected_var = 1;
+
+ /*
+ * In all cases, we want to back up the number of selected devices.
+ * It is a quick and accurate way to determine whether the selected
+ * devices have changed.
+ */
+ old_num_selected = *num_selected;
+
+ /*
+ * We want to make a backup of the current selection list if
+ * the list of devices has changed, or if we're in performance
+ * selection mode. In both cases, we don't want to make a backup
+ * if we already know for sure that the list will be different.
+ * This is certainly the case if this is our first time through the
+ * selection code.
+ */
+ if (((init_selected_var != 0) || (init_selections != 0)
+ || (perf_select != 0)) && (changed == 0)){
+ old_dev_select = (struct device_selection *)malloc(
+ *num_selections * sizeof(struct device_selection));
+ old_num_selections = *num_selections;
+ bcopy(*dev_select, old_dev_select,
+ sizeof(struct device_selection) * *num_selections);
+ }
+
+ if (init_selections != 0) {
+ bzero(*dev_select, sizeof(struct device_selection) * numdevs);
+
+ for (i = 0; i < numdevs; i++) {
+ (*dev_select)[i].device_number =
+ devices[i].device_number;
+ strncpy((*dev_select)[i].device_name,
+ devices[i].device_name,
+ DEVSTAT_NAME_LEN);
+ (*dev_select)[i].unit_number = devices[i].unit_number;
+ (*dev_select)[i].position = i;
+ }
+ *num_selections = numdevs;
+ } else if (init_selected_var != 0) {
+ for (i = 0; i < numdevs; i++)
+ (*dev_select)[i].selected = 0;
+ }
+
+ /* we haven't gotten around to selecting anything yet.. */
+ if ((select_mode == DS_SELECT_ONLY) || (init_selections != 0)
+ || (init_selected_var != 0))
+ *num_selected = 0;
+
+ /*
+ * Look through any devices the user specified on the command line
+ * and see if they match known devices. If so, select them.
+ */
+ for (i = 0; (i < *num_selections) && (num_dev_selections > 0); i++) {
+ char tmpstr[80];
+
+ sprintf(tmpstr, "%s%d", (*dev_select)[i].device_name,
+ (*dev_select)[i].unit_number);
+ for (j = 0; j < num_dev_selections; j++) {
+ if (strcmp(tmpstr, dev_selections[j]) == 0) {
+ /*
+ * Here we do different things based on the
+ * mode we're in. If we're in add or
+ * addonly mode, we only select this device
+ * if it hasn't already been selected.
+ * Otherwise, we would be unnecessarily
+ * changing the selection order and
+ * incrementing the selection count. If
+ * we're in only mode, we unconditionally
+ * select this device, since in only mode
+ * any previous selections are erased and
+ * manually specified devices are the first
+ * ones to be selected. If we're in remove
+ * mode, we de-select the specified device and
+ * decrement the selection count.
+ */
+ switch(select_mode) {
+ case DS_SELECT_ADD:
+ case DS_SELECT_ADDONLY:
+ if ((*dev_select)[i].selected)
+ break;
+ /* FALLTHROUGH */
+ case DS_SELECT_ONLY:
+ (*dev_select)[i].selected =
+ ++selection_number;
+ (*num_selected)++;
+ break;
+ case DS_SELECT_REMOVE:
+ (*dev_select)[i].selected = 0;
+ (*num_selected)--;
+ /*
+ * This isn't passed back out, we
+ * just use it to keep track of
+ * how many devices we've removed.
+ */
+ num_dev_selections--;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Go through the user's device type expressions and select devices
+ * accordingly. We only do this if the number of devices already
+ * selected is less than the maximum number we can show.
+ */
+ for (i = 0; (i < num_matches) && (*num_selected < maxshowdevs); i++) {
+ /* We should probably indicate some error here */
+ if ((matches[i].match_fields == DEVSTAT_MATCH_NONE)
+ || (matches[i].num_match_categories <= 0))
+ continue;
+
+ for (j = 0; j < numdevs; j++) {
+ int num_match_categories;
+
+ num_match_categories = matches[i].num_match_categories;
+
+ /*
+ * Determine whether or not the current device
+ * matches the given matching expression. This if
+ * statement consists of three components:
+ * - the device type check
+ * - the device interface check
+ * - the passthrough check
+ * If a the matching test is successful, it
+ * decrements the number of matching categories,
+ * and if we've reached the last element that
+ * needed to be matched, the if statement succeeds.
+ *
+ */
+ if ((((matches[i].match_fields & DEVSTAT_MATCH_TYPE)!=0)
+ && ((devices[j].device_type & DEVSTAT_TYPE_MASK) ==
+ (matches[i].device_type & DEVSTAT_TYPE_MASK))
+ &&(((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0)
+ || (((matches[i].match_fields &
+ DEVSTAT_MATCH_PASS) == 0)
+ && ((devices[j].device_type &
+ DEVSTAT_TYPE_PASS) == 0)))
+ && (--num_match_categories == 0))
+ || (((matches[i].match_fields & DEVSTAT_MATCH_IF) != 0)
+ && ((devices[j].device_type & DEVSTAT_TYPE_IF_MASK) ==
+ (matches[i].device_type & DEVSTAT_TYPE_IF_MASK))
+ &&(((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0)
+ || (((matches[i].match_fields &
+ DEVSTAT_MATCH_PASS) == 0)
+ && ((devices[j].device_type &
+ DEVSTAT_TYPE_PASS) == 0)))
+ && (--num_match_categories == 0))
+ || (((matches[i].match_fields & DEVSTAT_MATCH_PASS)!=0)
+ && ((devices[j].device_type & DEVSTAT_TYPE_PASS) != 0)
+ && (--num_match_categories == 0))) {
+
+ /*
+ * This is probably a non-optimal solution
+ * to the problem that the devices in the
+ * device list will not be in the same
+ * order as the devices in the selection
+ * array.
+ */
+ for (k = 0; k < numdevs; k++) {
+ if ((*dev_select)[k].position == j) {
+ found = 1;
+ break;
+ }
+ }
+
+ /*
+ * There shouldn't be a case where a device
+ * in the device list is not in the
+ * selection list...but it could happen.
+ */
+ if (found != 1) {
+ fprintf(stderr, "selectdevs: couldn't"
+ " find %s%d in selection "
+ "list\n",
+ devices[j].device_name,
+ devices[j].unit_number);
+ break;
+ }
+
+ /*
+ * We do different things based upon the
+ * mode we're in. If we're in add or only
+ * mode, we go ahead and select this device
+ * if it hasn't already been selected. If
+ * it has already been selected, we leave
+ * it alone so we don't mess up the
+ * selection ordering. Manually specified
+ * devices have already been selected, and
+ * they have higher priority than pattern
+ * matched devices. If we're in remove
+ * mode, we de-select the given device and
+ * decrement the selected count.
+ */
+ switch(select_mode) {
+ case DS_SELECT_ADD:
+ case DS_SELECT_ADDONLY:
+ case DS_SELECT_ONLY:
+ if ((*dev_select)[k].selected != 0)
+ break;
+ (*dev_select)[k].selected =
+ ++selection_number;
+ (*num_selected)++;
+ break;
+ case DS_SELECT_REMOVE:
+ (*dev_select)[k].selected = 0;
+ (*num_selected)--;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Here we implement "top" mode. Devices are sorted in the
+ * selection array based on two criteria: whether or not they are
+ * selected (not selection number, just the fact that they are
+ * selected!) and the number of bytes in the "bytes" field of the
+ * selection structure. The bytes field generally must be kept up
+ * by the user. In the future, it may be maintained by library
+ * functions, but for now the user has to do the work.
+ *
+ * At first glance, it may seem wrong that we don't go through and
+ * select every device in the case where the user hasn't specified
+ * any devices or patterns. In fact, though, it won't make any
+ * difference in the device sorting. In that particular case (i.e.
+ * when we're in "add" or "only" mode, and the user hasn't
+ * specified anything) the first time through no devices will be
+ * selected, so the only criterion used to sort them will be their
+ * performance. The second time through, and every time thereafter,
+ * all devices will be selected, so again selection won't matter.
+ */
+ if (perf_select != 0) {
+
+ /* Sort the device array by throughput */
+ qsort(*dev_select, *num_selections,
+ sizeof(struct device_selection),
+ compare_select);
+
+ if (*num_selected == 0) {
+ /*
+ * Here we select every device in the array, if it
+ * isn't already selected. Because the 'selected'
+ * variable in the selection array entries contains
+ * the selection order, the devstats routine can show
+ * the devices that were selected first.
+ */
+ for (i = 0; i < *num_selections; i++) {
+ if ((*dev_select)[i].selected == 0) {
+ (*dev_select)[i].selected =
+ ++selection_number;
+ (*num_selected)++;
+ }
+ }
+ } else {
+ selection_number = 0;
+ for (i = 0; i < *num_selections; i++) {
+ if ((*dev_select)[i].selected != 0) {
+ (*dev_select)[i].selected =
+ ++selection_number;
+ }
+ }
+ }
+ }
+
+ /*
+ * If we're in the "add" selection mode and if we haven't already
+ * selected maxshowdevs number of devices, go through the array and
+ * select any unselected devices. If we're in "only" mode, we
+ * obviously don't want to select anything other than what the user
+ * specifies. If we're in "remove" mode, it probably isn't a good
+ * idea to go through and select any more devices, since we might
+ * end up selecting something that the user wants removed. Through
+ * more complicated logic, we could actually figure this out, but
+ * that would probably require combining this loop with the various
+ * selections loops above.
+ */
+ if ((select_mode == DS_SELECT_ADD) && (*num_selected < maxshowdevs)) {
+ for (i = 0; i < *num_selections; i++)
+ if ((*dev_select)[i].selected == 0) {
+ (*dev_select)[i].selected = ++selection_number;
+ (*num_selected)++;
+ }
+ }
+
+ /*
+ * Look at the number of devices that have been selected. If it
+ * has changed, set the changed variable. Otherwise, if we've
+ * made a backup of the selection list, compare it to the current
+ * selection list to see if the selected devices have changed.
+ */
+ if ((changed == 0) && (old_num_selected != *num_selected))
+ changed = 1;
+ else if ((changed == 0) && (old_dev_select != NULL)) {
+ /*
+ * Now we go through the selection list and we look at
+ * it three different ways.
+ */
+ for (i = 0; (i < *num_selections) && (changed == 0) &&
+ (i < old_num_selections); i++) {
+ /*
+ * If the device at index i in both the new and old
+ * selection arrays has the same device number and
+ * selection status, it hasn't changed. We
+ * continue on to the next index.
+ */
+ if (((*dev_select)[i].device_number ==
+ old_dev_select[i].device_number)
+ && ((*dev_select)[i].selected ==
+ old_dev_select[i].selected))
+ continue;
+
+ /*
+ * Now, if we're still going through the if
+ * statement, the above test wasn't true. So we
+ * check here to see if the device at index i in
+ * the current array is the same as the device at
+ * index i in the old array. If it is, that means
+ * that its selection number has changed. Set
+ * changed to 1 and exit the loop.
+ */
+ else if ((*dev_select)[i].device_number ==
+ old_dev_select[i].device_number) {
+ changed = 1;
+ break;
+ }
+ /*
+ * If we get here, then the device at index i in
+ * the current array isn't the same device as the
+ * device at index i in the old array.
+ */
+ else {
+ int found = 0;
+
+ /*
+ * Search through the old selection array
+ * looking for a device with the same
+ * device number as the device at index i
+ * in the current array. If the selection
+ * status is the same, then we mark it as
+ * found. If the selection status isn't
+ * the same, we break out of the loop.
+ * Since found isn't set, changed will be
+ * set to 1 below.
+ */
+ for (j = 0; j < old_num_selections; j++) {
+ if (((*dev_select)[i].device_number ==
+ old_dev_select[j].device_number)
+ && ((*dev_select)[i].selected ==
+ old_dev_select[j].selected)){
+ found = 1;
+ break;
+ }
+ else if ((*dev_select)[i].device_number
+ == old_dev_select[j].device_number)
+ break;
+ }
+ if (found == 0)
+ changed = 1;
+ }
+ }
+ }
+ if (old_dev_select != NULL)
+ free(old_dev_select);
+
+ return(changed);
+}
+
+/*
+ * Comparison routine for qsort() above. Note that the comparison here is
+ * backwards -- generally, it should return a value to indicate whether
+ * arg1 is <, =, or > arg2. Instead, it returns the opposite. The reason
+ * it returns the opposite is so that the selection array will be sorted in
+ * order of decreasing performance. We sort on two parameters. The first
+ * sort key is whether or not one or the other of the devices in question
+ * has been selected. If one of them has, and the other one has not, the
+ * selected device is automatically more important than the unselected
+ * device. If neither device is selected, we judge the devices based upon
+ * performance.
+ */
+static int
+compare_select(const void *arg1, const void *arg2)
+{
+ if ((((struct device_selection *)arg1)->selected)
+ && (((struct device_selection *)arg2)->selected == 0))
+ return(-1);
+ else if ((((struct device_selection *)arg1)->selected == 0)
+ && (((struct device_selection *)arg2)->selected))
+ return(1);
+ else if (((struct device_selection *)arg2)->bytes <
+ ((struct device_selection *)arg1)->bytes)
+ return(-1);
+ else if (((struct device_selection *)arg2)->bytes >
+ ((struct device_selection *)arg1)->bytes)
+ return(1);
+ else
+ return(0);
+}
+
+/*
+ * Take a string with the general format "arg1,arg2,arg3", and build a
+ * device matching expression from it.
+ */
+int
+buildmatch(char *match_str, struct devstat_match **matches, int *num_matches)
+{
+ char *tstr[5];
+ char **tempstr;
+ int num_args;
+ register int i, j;
+ char *func_name = "buildmatch";
+
+ /* We can't do much without a string to parse */
+ if (match_str == NULL) {
+ sprintf(devstat_errbuf, "%s: no match expression", func_name);
+ return(-1);
+ }
+
+ /*
+ * Break the (comma delimited) input string out into separate strings.
+ */
+ for (tempstr = tstr, num_args = 0;
+ (*tempstr = strsep(&match_str, ",")) != NULL && (num_args < 5);
+ num_args++)
+ if (**tempstr != '\0')
+ if (++tempstr >= &tstr[5])
+ break;
+
+ /* The user gave us too many type arguments */
+ if (num_args > 3) {
+ sprintf(devstat_errbuf, "%s: too many type arguments",
+ func_name);
+ return(-1);
+ }
+
+ /*
+ * Since you can't realloc a pointer that hasn't been malloced
+ * first, we malloc first and then realloc.
+ */
+ if (*num_matches == 0)
+ *matches = (struct devstat_match *)malloc(
+ sizeof(struct devstat_match));
+ else
+ *matches = (struct devstat_match *)realloc(*matches,
+ sizeof(struct devstat_match) * (*num_matches + 1));
+
+ /* Make sure the current entry is clear */
+ bzero(&matches[0][*num_matches], sizeof(struct devstat_match));
+
+ /*
+ * Step through the arguments the user gave us and build a device
+ * matching expression from them.
+ */
+ for (i = 0; i < num_args; i++) {
+ char *tempstr2, *tempstr3;
+
+ /*
+ * Get rid of leading white space.
+ */
+ tempstr2 = tstr[i];
+ while (isspace(*tempstr2) && (*tempstr2 != '\0'))
+ tempstr2++;
+
+ /*
+ * Get rid of trailing white space.
+ */
+ tempstr3 = &tempstr2[strlen(tempstr2) - 1];
+
+ while ((*tempstr3 != '\0') && (tempstr3 > tempstr2)
+ && (isspace(*tempstr3))) {
+ *tempstr3 = '\0';
+ tempstr3--;
+ }
+
+ /*
+ * Go through the match table comparing the user's
+ * arguments to known device types, interfaces, etc.
+ */
+ for (j = 0; match_table[j].match_str != NULL; j++) {
+ /*
+ * We do case-insensitive matching, in case someone
+ * wants to enter "SCSI" instead of "scsi" or
+ * something like that. Only compare as many
+ * characters as are in the string in the match
+ * table. This should help if someone tries to use
+ * a super-long match expression.
+ */
+ if (strncasecmp(tempstr2, match_table[j].match_str,
+ strlen(match_table[j].match_str)) == 0) {
+ /*
+ * Make sure the user hasn't specified two
+ * items of the same type, like "da" and
+ * "cd". One device cannot be both.
+ */
+ if (((*matches)[*num_matches].match_fields &
+ match_table[j].match_field) != 0) {
+ sprintf(devstat_errbuf,
+ "%s: cannot have more than "
+ "one match item in a single "
+ "category", func_name);
+ return(-1);
+ }
+ /*
+ * If we've gotten this far, we have a
+ * winner. Set the appropriate fields in
+ * the match entry.
+ */
+ (*matches)[*num_matches].match_fields |=
+ match_table[j].match_field;
+ (*matches)[*num_matches].device_type |=
+ match_table[j].type;
+ (*matches)[*num_matches].num_match_categories++;
+ break;
+ }
+ }
+ /*
+ * We should have found a match in the above for loop. If
+ * not, that means the user entered an invalid device type
+ * or interface.
+ */
+ if ((*matches)[*num_matches].num_match_categories != (i + 1)) {
+ sprintf(devstat_errbuf,
+ "%s: unknown match item \"%s\"", func_name,
+ tstr[i]);
+ return(-1);
+ }
+ }
+
+ (*num_matches)++;
+
+ return(0);
+}
+
+/*
+ * Compute a number of device statistics. Only one field is mandatory, and
+ * that is "current". Everything else is optional. The caller passes in
+ * pointers to variables to hold the various statistics he desires. If he
+ * doesn't want a particular staistic, he should pass in a NULL pointer.
+ * Return values:
+ * 0 -- success
+ * -1 -- failure
+ */
+int
+compute_stats(struct devstat *current, struct devstat *previous,
+ long double etime, u_int64_t *total_bytes,
+ u_int64_t *total_transfers, u_int64_t *total_blocks,
+ long double *kb_per_transfer, long double *transfers_per_second,
+ long double *mb_per_second, long double *blocks_per_second,
+ long double *ms_per_transaction)
+{
+ u_int64_t totalbytes, totaltransfers, totalblocks;
+ char *func_name = "compute_stats";
+
+ /*
+ * current is the only mandatory field.
+ */
+ if (current == NULL) {
+ sprintf(devstat_errbuf, "%s: current stats structure was NULL",
+ func_name);
+ return(-1);
+ }
+
+ totalbytes = (current->bytes_written + current->bytes_read) -
+ ((previous) ? (previous->bytes_written +
+ previous->bytes_read) : 0);
+
+ if (total_bytes)
+ *total_bytes = totalbytes;
+
+ totaltransfers = (current->num_reads +
+ current->num_writes +
+ current->num_other) -
+ ((previous) ?
+ (previous->num_reads +
+ previous->num_writes +
+ previous->num_other) : 0);
+ if (total_transfers)
+ *total_transfers = totaltransfers;
+
+ if (transfers_per_second) {
+ if (etime > 0.0) {
+ *transfers_per_second = totaltransfers;
+ *transfers_per_second /= etime;
+ } else
+ *transfers_per_second = 0.0;
+ }
+
+ if (kb_per_transfer) {
+ *kb_per_transfer = totalbytes;
+ *kb_per_transfer /= 1024;
+ if (totaltransfers > 0)
+ *kb_per_transfer /= totaltransfers;
+ else
+ *kb_per_transfer = 0.0;
+ }
+
+ if (mb_per_second) {
+ *mb_per_second = totalbytes;
+ *mb_per_second /= 1024 * 1024;
+ if (etime > 0.0)
+ *mb_per_second /= etime;
+ else
+ *mb_per_second = 0.0;
+ }
+
+ totalblocks = totalbytes;
+ if (current->block_size > 0)
+ totalblocks /= current->block_size;
+ else
+ totalblocks /= 512;
+
+ if (total_blocks)
+ *total_blocks = totalblocks;
+
+ if (blocks_per_second) {
+ *blocks_per_second = totalblocks;
+ if (etime > 0.0)
+ *blocks_per_second /= etime;
+ else
+ *blocks_per_second = 0.0;
+ }
+
+ if (ms_per_transaction) {
+ if (totaltransfers > 0) {
+ *ms_per_transaction = etime;
+ *ms_per_transaction /= totaltransfers;
+ *ms_per_transaction *= 1000;
+ } else
+ *ms_per_transaction = 0.0;
+ }
+
+ return(0);
+}
+
+long double
+compute_etime(struct timeval cur_time, struct timeval prev_time)
+{
+ struct timeval busy_time;
+ u_int64_t busy_usec;
+ long double etime;
+
+ timersub(&cur_time, &prev_time, &busy_time);
+
+ busy_usec = busy_time.tv_sec;
+ busy_usec *= 1000000;
+ busy_usec += busy_time.tv_usec;
+ etime = busy_usec;
+ etime /= 1000000;
+
+ return(etime);
+}
diff --git a/lib/libdevstat/devstat.h b/lib/libdevstat/devstat.h
new file mode 100644
index 0000000..b2f1543
--- /dev/null
+++ b/lib/libdevstat/devstat.h
@@ -0,0 +1,115 @@
+/*
+ * 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$
+ */
+
+#ifndef _DEVSTAT_H
+#define _DEVSTAT_H
+#include <sys/cdefs.h>
+#include <sys/devicestat.h>
+
+#define DEVSTAT_ERRBUF_SIZE 2048 /* size of the devstat library error string */
+
+extern char devstat_errbuf[];
+
+typedef enum {
+ DEVSTAT_MATCH_NONE = 0x00,
+ DEVSTAT_MATCH_TYPE = 0x01,
+ DEVSTAT_MATCH_IF = 0x02,
+ DEVSTAT_MATCH_PASS = 0x04
+} devstat_match_flags;
+
+struct devstat_match {
+ devstat_match_flags match_fields;
+ devstat_type_flags device_type;
+ int num_match_categories;
+};
+
+struct devstat_match_table {
+ char *match_str;
+ devstat_type_flags type;
+ devstat_match_flags match_field;
+};
+
+struct device_selection {
+ u_int32_t device_number;
+ char device_name[DEVSTAT_NAME_LEN];
+ int unit_number;
+ int selected;
+ u_int64_t bytes;
+ int position;
+};
+
+struct devinfo {
+ struct devstat *devices;
+ u_int8_t *mem_ptr;
+ int generation;
+ int numdevs;
+};
+
+struct statinfo {
+ long cp_time[CPUSTATES];
+ long tk_nin;
+ long tk_nout;
+ struct devinfo *dinfo;
+ struct timeval busy_time;
+};
+
+typedef enum {
+ DS_SELECT_ADD,
+ DS_SELECT_ONLY,
+ DS_SELECT_REMOVE,
+ DS_SELECT_ADDONLY
+} devstat_select_mode;
+
+__BEGIN_DECLS
+int getnumdevs(void);
+int getgeneration(void);
+int getversion(void);
+int checkversion(void);
+int getdevs(struct statinfo *stats);
+int selectdevs(struct device_selection **dev_select, int *num_selected,
+ int *num_selections, int *select_generation,
+ int current_generation, struct devstat *devices, int numdevs,
+ struct devstat_match *matches, int num_matches,
+ char **dev_selections, int num_dev_selections,
+ devstat_select_mode select_mode, int maxshowdevs,
+ int perf_select);
+int buildmatch(char *match_str, struct devstat_match **matches,
+ int *num_matches);
+int compute_stats(struct devstat *current, struct devstat *previous,
+ long double etime, u_int64_t *total_bytes,
+ u_int64_t *total_transfers, u_int64_t *total_blocks,
+ long double *kb_per_transfer,
+ long double *transfers_per_second, long double *mb_per_second,
+ long double *blocks_per_second,
+ long double *ms_per_transaction);
+long double compute_etime(struct timeval cur_time, struct timeval prev_time);
+__END_DECLS
+
+#endif /* _DEVSTAT_H */
OpenPOWER on IntegriCloud