Add some features to libdevstat, and overhaul the interface a bit:
1.) prefix all functions in the library with devstat_ (compatability functions are available for all functions that were chaned in an incompatible way, but are deprecated). 2.) Add a pointer to a kvm_t as the first argument to functions that used to get their information via sysctl; they behave the same as before when NULL is passed as this argument, otherwise, the information is obtained via libkvm using the supplied handle. 3.) Add a new function, devstat_compute_statistics(), that is intended to replace the old compute_stats() function. It offers more statistics data, and has a more flexible interface. libdevstat does now require libkvm; a library depedency is added, so that libkvm only needs to be explicitely specified for statically linked programs. The library major version number is bumped. Submitted by: Sergey A. Osokin <osa@freebsd.org.ru>, ken (3) Reviewed by: ken
This commit is contained in:
parent
fb501835eb
commit
37ac1a7962
@ -735,6 +735,7 @@ includes:
|
||||
# libatm: libmd
|
||||
# libcam: libsbuf
|
||||
# libcrypt: libmd
|
||||
# libdevstat: libkvm
|
||||
# libdialog: libncurses
|
||||
# libedit: libncurses
|
||||
# libg++: libm
|
||||
@ -793,7 +794,7 @@ _libperl= gnu/usr.bin/perl/libperl
|
||||
libraries:
|
||||
.for _lib in ${_csu} gnu/lib/csu gnu/lib/libgcc lib/libmd lib/libcrypt \
|
||||
${_secure_lib} ${_kerberosIV_lib} \
|
||||
${_kerberos5_lib} lib/libcom_err ${_libm} lib/libncurses \
|
||||
${_kerberos5_lib} lib/libcom_err lib/libkvm ${_libm} lib/libncurses \
|
||||
lib/libopie lib/libradius lib/libsbuf lib/libtacplus \
|
||||
lib/libutil lib/libz lib gnu/lib ${_libperl} usr.bin/lex/lib ${_libkeycap}
|
||||
.if exists(${.CURDIR}/${_lib})
|
||||
|
@ -7,6 +7,7 @@
|
||||
# csu must be built before all shared libaries for ELF.
|
||||
# libcom_err must be built before libkrb, libpam and libss.
|
||||
# libcrypt must be built before libkrb and libpam.
|
||||
# libkvm must be built before libdevstat.
|
||||
# msun must be built before libg++ and libstdc++.
|
||||
# libmd must be built before libatm, libopie, libradius, and libtacplus.
|
||||
# libncurses must be built before libdialog, libedit and libreadline.
|
||||
@ -18,12 +19,12 @@
|
||||
#
|
||||
# Otherwise, the SUBDIR list should be in alphabetical order.
|
||||
|
||||
SUBDIR= ${_csu} libcom_err libcrypt msun libmd \
|
||||
SUBDIR= ${_csu} libcom_err libcrypt libkvm msun libmd \
|
||||
libncurses libradius libtacplus libutil libsbuf \
|
||||
${_compat} libalias libatm ${_libbind} libbz2 libc ${_libc_r} \
|
||||
libcalendar libcam libcompat libdevinfo libdevstat libdisk \
|
||||
libedit libfetch libform libftpio libgnumalloc ${_libio} libipsec \
|
||||
libipx libisc libkvm libmenu ${_libmp} ${_libncp} \
|
||||
libipx libisc libmenu ${_libmp} ${_libncp} \
|
||||
libnetgraph libopie libpam libpanel libpcap \
|
||||
libresolv librpcsvc libsmdb libsmutil libss \
|
||||
libstand ${_libtelnet} libusb ${_libvgl} libwrap libxpg4 liby libz
|
||||
|
@ -3,11 +3,25 @@
|
||||
MAINTAINER=ken@FreeBSD.ORG
|
||||
|
||||
LIB= devstat
|
||||
SHLIB_MAJOR= 3
|
||||
SHLIB_MINOR= 0
|
||||
SRCS= devstat.c
|
||||
INCS= devstat.h
|
||||
|
||||
DPADD+= ${LIBKVM}
|
||||
LDADD+= -lkvm
|
||||
|
||||
MAN= devstat.3
|
||||
|
||||
MLINKS+=devstat.3 devstat_getnumdevs.3
|
||||
MLINKS+=devstat.3 devstat_getgeneration.3
|
||||
MLINKS+=devstat.3 devstat_getversion.3
|
||||
MLINKS+=devstat.3 devstat_checkversion.3
|
||||
MLINKS+=devstat.3 devstat_getdevs.3
|
||||
MLINKS+=devstat.3 devstat_selectdevs.3
|
||||
MLINKS+=devstat.3 devstat_buildmatch.3
|
||||
MLINKS+=devstat.3 devstat_compute_statistics.3
|
||||
MLINKS+=devstat.3 devstat_compute_etime.3
|
||||
MLINKS+=devstat.3 getnumdevs.3
|
||||
MLINKS+=devstat.3 getgeneration.3
|
||||
MLINKS+=devstat.3 getversion.3
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\"
|
||||
.\" Copyright (c) 1998, 1999 Kenneth D. Merry.
|
||||
.\" Copyright (c) 1998, 1999, 2001 Kenneth D. Merry.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
@ -27,11 +27,20 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 21, 1998
|
||||
.Dd July 15, 2001
|
||||
.Dt DEVSTAT 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm devstat ,
|
||||
.Nm devstat_getnumdevs ,
|
||||
.Nm devstat_getgeneration ,
|
||||
.Nm devstat_getversion ,
|
||||
.Nm devstat_checkversion ,
|
||||
.Nm devstat_getdevs ,
|
||||
.Nm devstat_selectdevs ,
|
||||
.Nm devstat_buildmatch ,
|
||||
.Nm devstat_compute_statistics ,
|
||||
.Nm devstat_compute_etime ,
|
||||
.Nm getnumdevs ,
|
||||
.Nm getgeneration ,
|
||||
.Nm getversion ,
|
||||
@ -48,6 +57,62 @@
|
||||
.Fd #include <sys/dkstat.h>
|
||||
.Fd #include <devstat.h>
|
||||
.Ft int
|
||||
.Fo devstat_getnumdevs
|
||||
.Fa "kvm_t *kd"
|
||||
.Fc
|
||||
.Ft long
|
||||
.Fo devstat_getgeneration
|
||||
.Fa "kvm_t *kd"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo devstat_getversion
|
||||
.Fa "kvm_t *kd"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo devstat_checkversion
|
||||
.Fa "kvm_t *kd"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo devstat_getdevs
|
||||
.Fa "kvm_t *kd"
|
||||
.Fa "struct statinfo *stats"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo devstat_selectdevs
|
||||
.Fa "struct device_selection **dev_select"
|
||||
.Fa "int *num_selected"
|
||||
.Fa "int *num_selections"
|
||||
.Fa "long *select_generation"
|
||||
.Fa "long 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 devstat_buildmatch
|
||||
.Fa "char *match_str"
|
||||
.Fa "struct devstat_match **matches"
|
||||
.Fa "int *num_matches"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo devstat_compute_statistics
|
||||
.Fa "struct devstat *current"
|
||||
.Fa "struct devstat *previous"
|
||||
.Fa "long double *etime"
|
||||
.Fa "..."
|
||||
.Fc
|
||||
.Ft long double
|
||||
.Fo devstat_compute_etime
|
||||
.Fa "struct timeval cur_time"
|
||||
.Fa "struct timeval prev_time"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fn getnumdevs "void"
|
||||
.Ft long
|
||||
.Fn getgeneration "void"
|
||||
@ -105,34 +170,77 @@ The
|
||||
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
|
||||
and
|
||||
.Xr kvm 3 .
|
||||
All functions that that take a
|
||||
.Vt kvm_t *
|
||||
as first argument can be passed
|
||||
.Dv NULL
|
||||
instead of a kvm handle as this argument,
|
||||
which causes the data to be read via
|
||||
.Xr sysctl 3 .
|
||||
Otherwise, it is read via
|
||||
.Xr kvm 3
|
||||
using the supplied handle.
|
||||
.Fn devstat_checkversion
|
||||
should be called with each kvm handle that is going to be used (or with
|
||||
.Dv NULL
|
||||
if
|
||||
.Xr sysctl 3
|
||||
is going to be used).
|
||||
.Pp
|
||||
.Fn getnumdevs
|
||||
.Fn devstat_getnumdevs
|
||||
returns the number of devices registered with the
|
||||
.Nm
|
||||
subsystem in the kernel.
|
||||
.Pp
|
||||
.Fn getgeneration
|
||||
.Fn getnumdevs
|
||||
is a deprecated version of
|
||||
.Fn devstat_getnumdevs
|
||||
which always uses
|
||||
.Xr sysctl 3 .
|
||||
.Pp
|
||||
.Fn devstat_getgeneration
|
||||
returns the current generation of the
|
||||
.Nm
|
||||
list of devices in the kernel.
|
||||
.Pp
|
||||
.Fn getversion
|
||||
.Fn getgeneration
|
||||
is a deprecated version of
|
||||
.Fn devstat_getgeneration
|
||||
which always uses
|
||||
.Xr sysctl 3 .
|
||||
.Pp
|
||||
.Fn devstat_getversion
|
||||
returns the current kernel
|
||||
.Nm
|
||||
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
|
||||
.Fn getversion
|
||||
is a deprecated version of
|
||||
.Fn devstat_getversion
|
||||
which always uses
|
||||
.Xr sysctl 3 .
|
||||
.Pp
|
||||
.Fn devstat_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
|
||||
.Fn checkversion
|
||||
is a deprecated version of
|
||||
.Fn devstat_checkversion
|
||||
which always uses
|
||||
.Xr sysctl 3 .
|
||||
.Pp
|
||||
.Fn devstat_getdevs
|
||||
fetches the current list of devices and statistics into the supplied
|
||||
.Va statinfo
|
||||
structure. The
|
||||
structure.
|
||||
The
|
||||
.Va statinfo
|
||||
structure can be found in
|
||||
.Aq Pa devstat.h :
|
||||
@ -146,18 +254,18 @@ struct statinfo {
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
.Fn getdevs
|
||||
.Fn devstat_getdevs
|
||||
expects the
|
||||
.Va statinfo
|
||||
structure to be allocated, and it also expects the
|
||||
.Va dinfo
|
||||
subelement to be allocated and zeroed prior to the first invocation of
|
||||
.Fn getdevs .
|
||||
.Fn devstat_getdevs .
|
||||
The
|
||||
.Va dinfo
|
||||
subelement is used to store state between calls, and should not be modified
|
||||
after the first call to
|
||||
.Fn getdevs .
|
||||
.Fn devstat_getdevs .
|
||||
The
|
||||
.Va dinfo
|
||||
subelement contains the following elements:
|
||||
@ -177,11 +285,11 @@ variable contains an array of
|
||||
.Nm
|
||||
structures, but at the head of the array is the current
|
||||
.Nm
|
||||
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
|
||||
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
|
||||
@ -193,19 +301,18 @@ subelement of the
|
||||
.Va devinfo
|
||||
structure is a pointer to memory that is allocated, and resized if
|
||||
necessary, by
|
||||
.Fn getdevs .
|
||||
.Fn devstat_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
|
||||
variable (or the corresponding values read via
|
||||
.Xr kvm 3 ) .
|
||||
The generation subelement of the
|
||||
.Va devinfo
|
||||
structure contains the generation number from the
|
||||
.Va kern.devstat.all
|
||||
.Nm sysctl
|
||||
variable.
|
||||
structure contains the corresponding generation number.
|
||||
The
|
||||
.Va numdevs
|
||||
subelement of the
|
||||
@ -215,15 +322,21 @@ number of devices registered with the kernel
|
||||
.Nm
|
||||
subsystem.
|
||||
.Pp
|
||||
.Fn selectdevs
|
||||
.Fn getdevs
|
||||
is a deprecated version of
|
||||
.Fn devstat_getdevs
|
||||
which always uses
|
||||
.Xr sysctl 3 .
|
||||
.Pp
|
||||
.Fn devstat_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.
|
||||
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
|
||||
.Fn devstat_buildmatch
|
||||
from user input.
|
||||
.It performance
|
||||
If performance mode is enabled, devices will be sorted based on the
|
||||
@ -231,35 +344,36 @@ If performance mode is enabled, devices will be sorted based on the
|
||||
field in the
|
||||
.Va device_selection
|
||||
structure passed in to
|
||||
.Fn selectdevs .
|
||||
.Fn devstat_selectdevs .
|
||||
The
|
||||
.Va bytes
|
||||
value currently must be maintained by the user. In the future,
|
||||
this may be done for him in a
|
||||
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
|
||||
performance. 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.
|
||||
performance.
|
||||
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
|
||||
.Fn devstat_selectdevs
|
||||
will automatically select up to
|
||||
.Va maxshowdevs
|
||||
devices.
|
||||
.El
|
||||
.Pp
|
||||
.Fn selectdevs
|
||||
.Fn devstat_selectdevs
|
||||
performs selections in four different modes:
|
||||
.Bl -tag -width DS_SELECT_ADDONLY
|
||||
.It DS_SELECT_ADD
|
||||
In add mode,
|
||||
.Fn selectdevs
|
||||
.Fn devstat_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
|
||||
@ -268,43 +382,43 @@ or until all devices are
|
||||
selected.
|
||||
.It DS_SELECT_ONLY
|
||||
In only mode,
|
||||
.Fn selectdevs
|
||||
.Fn devstat_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.
|
||||
.Fn devstat_selectdevs
|
||||
will remove devices specified by name or by matching pattern.
|
||||
It will not select any additional devices.
|
||||
.It DS_SELECT_ADDONLY
|
||||
In add only mode,
|
||||
.Fn selectdevs
|
||||
.Fn devstat_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.
|
||||
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
|
||||
.Fn devstat_selectdevs
|
||||
will not select any more than
|
||||
.Va maxshowdevs
|
||||
devices. One exception to
|
||||
this is when you are in
|
||||
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.
|
||||
mode and no devices have been selected.
|
||||
In this case,
|
||||
.Fn devstat_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
|
||||
.Fn devstat_selectdevs
|
||||
handles allocation and resizing of the
|
||||
.Va dev_select
|
||||
structure passed in
|
||||
by the client.
|
||||
.Fn selectdevs
|
||||
.Fn devstat_selectdevs
|
||||
uses the
|
||||
.Va numdevs
|
||||
and
|
||||
@ -312,7 +426,8 @@ and
|
||||
fields to track the
|
||||
current
|
||||
.Nm
|
||||
generation and number of devices. If
|
||||
generation and number of devices.
|
||||
If
|
||||
.Va num_selections
|
||||
is not the same
|
||||
as
|
||||
@ -321,12 +436,17 @@ or if
|
||||
.Va select_generation
|
||||
is not the same as
|
||||
.Va current_generation ,
|
||||
.Fn selectdevs
|
||||
.Fn devstat_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
|
||||
.Fn selectdevs
|
||||
is the old name of
|
||||
.Fn devstat_selectdevs ,
|
||||
and is deprecated.
|
||||
.Pp
|
||||
.Fn devstat_buildmatch
|
||||
take a comma separated match string and compile it into a
|
||||
\fBdevstat_match\fR structure that is understood by
|
||||
.Fn selectdevs .
|
||||
Match strings have the following format:
|
||||
@ -335,7 +455,7 @@ Match strings have the following format:
|
||||
device,type,if
|
||||
.Ed
|
||||
.Pp
|
||||
.Fn buildmatch
|
||||
.Fn devstat_buildmatch
|
||||
takes care of allocating and reallocating the match list as necessary.
|
||||
Currently known match types include:
|
||||
.Pp
|
||||
@ -387,22 +507,285 @@ Passthrough devices
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
.Fn buildmatch
|
||||
is the old name of
|
||||
.Fn devstat_buildmatch ,
|
||||
and is deprecated.
|
||||
.Pp
|
||||
.Fn devstat_compute_statistics
|
||||
is an updated version of
|
||||
.Fn compute_stats
|
||||
provides an easy way to obtain various device statistics. Only two
|
||||
arguments are mandatory:
|
||||
that provides more complete statistics calculation.
|
||||
There are four arguments for which values \fBmust\fR be supplied:
|
||||
.Va current ,
|
||||
.Va previous ,
|
||||
.Va etime ,
|
||||
and the terminating argument for the varargs list,
|
||||
.Va DSM_NONE .
|
||||
For most applications, the user will want to supply valid devstat
|
||||
structures for both
|
||||
.Va current
|
||||
and
|
||||
.Va previous .
|
||||
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 devstat_compute_statistics
|
||||
will use the total stats in the
|
||||
.Va current
|
||||
structure to calculate statistics over
|
||||
.Va etime .
|
||||
For each statistic to be calculated, the user should supply the proper
|
||||
enumerated type (listed below), and a variable of the indicated type.
|
||||
All statistics are either integer values, for which a u_int64_t is used,
|
||||
or floating point, for which a long double is used.
|
||||
The statistics that may be calculated are:
|
||||
.Bl -tag -width DSM_TRANSFERS_PER_SECOND_OTHER
|
||||
.It DSM_NONE
|
||||
type: N/A
|
||||
.Pp
|
||||
This \fBmust\fR
|
||||
be the last argument passed to
|
||||
.Fn devstat_compute_statistics .
|
||||
It is an argument list terminator.
|
||||
.It DSM_TOTAL_BYTES
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of bytes transferred between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_BYTES_READ
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of bytes read between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_BYTES_WRITE
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of bytes written between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_TRANSFERS
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of transfers between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_TRANSFERS_READ
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of reads between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_TRANSFERS_WRITE
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of writes between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_TRANSFERS_OTHER
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of transactions that are not reads or writes that occurred
|
||||
between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TOTAL_BLOCKS
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of blocks transferred between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_TOTAL_BLOCKS_READ
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of blocks read between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_TOTAL_BLOCKS_WRITE
|
||||
type: u_int64_t *
|
||||
.Pp
|
||||
The total number of blocks written between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_KB_PER_TRANSFER
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of kilobytes per transfer between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_KB_PER_TRANSFER_READ
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of kilobytes per read transaction between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_KB_PER_TRANSFER_WRITE
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of kilobytes per write transaction between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TRANSFERS_PER_SECOND
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of transfers per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TRANSFERS_PER_SECOND_READ
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of reads per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TRANSFERS_PER_SECOND_WRITE
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of writes per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_TRANSFERS_PER_SECOND_OTHER
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of non-read, non-write transactions per second between
|
||||
the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_MB_PER_SECOND
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of megabytes transferred per second between the
|
||||
acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_MB_PER_SECOND_READ
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of megabytes read per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_MB_PER_SECOND_WRITE
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of megabytes written per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
.It DSM_BLOCKS_PER_SECOND
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of blocks transferred per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_BLOCKS_PER_SECOND_READ
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of blocks read per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_BLOCKS_PER_SECOND_WRITE
|
||||
type: long double *
|
||||
.Pp
|
||||
The average number of blocks written per second between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
This number is in terms of the blocksize reported by the device.
|
||||
If no blocksize has been reported (i.e. the block size is 0), a default
|
||||
blocksize of 512 bytes will be used in the calculation.
|
||||
.It DSM_MS_PER_TRANSACTION
|
||||
type: long double *
|
||||
.Pp
|
||||
The average rate of transaction completion between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
Note that this isn't a true reflection of the average number of
|
||||
milliseconds per transaction, but rather is the average rate of transaction
|
||||
completion.
|
||||
The number is derived by dividing the time elapsed by the number of
|
||||
transactions completed.
|
||||
.It DSM_MS_PER_TRANSACTION_READ
|
||||
type: long double *
|
||||
.Pp
|
||||
The average rate of read completions between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
As above, this is not the true number of milliseconds per transaction, but
|
||||
rather the average rate of read transaction completion.
|
||||
.It DSM_MS_PER_TRANSACTION_WRITE
|
||||
type: long double *
|
||||
.Pp
|
||||
The average rate of write transaction completion between the acquisition of
|
||||
.Va previous
|
||||
and
|
||||
.Va current .
|
||||
As above, this is not the true number of milliseconds per transaction, but
|
||||
rather the average rate of write transaction completion.
|
||||
.El
|
||||
.Pp
|
||||
.Fn compute_stats
|
||||
is deprecated; use
|
||||
.Fn devstat_compute_statistics
|
||||
instead.
|
||||
.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
|
||||
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
|
||||
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,
|
||||
argument.
|
||||
In that case,
|
||||
.Fn compute_stats
|
||||
will use the total stats in the
|
||||
.Va current
|
||||
@ -437,7 +820,8 @@ is NULL, the result will be the total number of transactions listed in
|
||||
.It total_blocks
|
||||
This is basically
|
||||
.Va total_bytes
|
||||
divided by the device blocksize. If the device blocksize is listed as
|
||||
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
|
||||
@ -448,56 +832,67 @@ 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
|
||||
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 milliseconds per transaction.
|
||||
.El
|
||||
.Pp
|
||||
.Fn compute_etime
|
||||
.Fn devstat_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
|
||||
structures.
|
||||
This is most commonly used in conjunction with the time recorded by the
|
||||
.Fn devstat_getdevs
|
||||
function (in struct
|
||||
.Va statinfo )
|
||||
each time it fetches the current
|
||||
.Nm
|
||||
list.
|
||||
.Pp
|
||||
.Fn compute_etime
|
||||
is the old name of
|
||||
.Fn devstat_compute_etime ,
|
||||
and is deprecated.
|
||||
.Sh RETURN VALUES
|
||||
.Fn getnumdevs ,
|
||||
.Fn getgeneration ,
|
||||
.Fn devstat_getnumdevs ,
|
||||
.Fn devstat_getgeneration ,
|
||||
and
|
||||
.Fn getversion
|
||||
.Fn devstat_getversion
|
||||
return the indicated \fBsysctl\fR variable, or -1 if there is an error
|
||||
fetching the variable.
|
||||
.Pp
|
||||
.Fn checkversion
|
||||
.Fn devstat_checkversion
|
||||
returns 0 if the kernel and userland
|
||||
.Nm
|
||||
versions match. If they do not match, it returns -1.
|
||||
versions match.
|
||||
If they do not match, it returns -1.
|
||||
.Pp
|
||||
.Fn getdevs
|
||||
.Fn devstat_getdevs
|
||||
and
|
||||
.Fn selectdevs
|
||||
.Fn devstat_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
|
||||
list or selected devices have changed.
|
||||
A return value of 1 from
|
||||
.Fn devstat_getdevs
|
||||
is usually a hint to re-run
|
||||
.Fn selectdevs
|
||||
.Fn devstat_selectdevs
|
||||
because the device list has changed.
|
||||
.Pp
|
||||
.Fn buildmatch
|
||||
.Fn devstat_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
|
||||
.Fn devstat_compute_etime
|
||||
returns the computed elapsed time.
|
||||
.Pp
|
||||
.Fn devstat_compute_statistics
|
||||
returns -1 for error, and 0 for success.
|
||||
.Pp
|
||||
If an error is returned from one of the
|
||||
.Nm
|
||||
library functions, the reason for the error is generally printed in
|
||||
@ -507,7 +902,10 @@ which is
|
||||
.Dv DEVSTAT_ERRBUF_SIZE
|
||||
characters long.
|
||||
.Sh SEE ALSO
|
||||
.Xr sysctl 1 ,
|
||||
.Xr systat 1 ,
|
||||
.Xr kvm 3 ,
|
||||
.Xr sysctl 3 ,
|
||||
.Xr iostat 8 ,
|
||||
.Xr rpc.rstatd 8 ,
|
||||
.Xr vmstat 8 ,
|
||||
@ -517,16 +915,18 @@ The
|
||||
.Nm
|
||||
statistics system first appeared in
|
||||
.Fx 3.0 .
|
||||
The new interface (the functions prefixed with devstat_) first appeared in
|
||||
.Fx 5.0 .
|
||||
.Sh AUTHORS
|
||||
.An Kenneth Merry Aq ken@FreeBSD.org
|
||||
.Sh BUGS
|
||||
There should probably be an interface to de-allocate memory allocated by
|
||||
.Fn getdevs ,
|
||||
.Fn selectdevs ,
|
||||
.Fn devstat_getdevs ,
|
||||
.Fn devstat_selectdevs ,
|
||||
and
|
||||
.Fn buildmatch .
|
||||
.Fn devstat_buildmatch .
|
||||
.Pp
|
||||
.Fn selectdevs
|
||||
.Fn devstat_selectdevs
|
||||
should probably not select more than
|
||||
.Va maxshowdevs
|
||||
devices in
|
||||
|
@ -32,15 +32,26 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/dkstat.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include "devstat.h"
|
||||
|
||||
typedef enum {
|
||||
DEVSTAT_ARG_NOTYPE,
|
||||
DEVSTAT_ARG_UINT64,
|
||||
DEVSTAT_ARG_LD
|
||||
} devstat_arg_type;
|
||||
|
||||
char devstat_errbuf[DEVSTAT_ERRBUF_SIZE];
|
||||
|
||||
/*
|
||||
@ -68,30 +79,90 @@ struct devstat_match_table match_table[] = {
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
struct devstat_args {
|
||||
devstat_metric metric;
|
||||
devstat_arg_type argtype;
|
||||
} devstat_arg_list[] = {
|
||||
{ DSM_NONE, DEVSTAT_ARG_NOTYPE },
|
||||
{ DSM_TOTAL_BYTES, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_BYTES_READ, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_BYTES_WRITE, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_TRANSFERS, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_TRANSFERS_READ, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_TRANSFERS_WRITE, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_TRANSFERS_OTHER, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_BLOCKS, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_BLOCKS_READ, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_TOTAL_BLOCKS_WRITE, DEVSTAT_ARG_UINT64 },
|
||||
{ DSM_KB_PER_TRANSFER, DEVSTAT_ARG_LD },
|
||||
{ DSM_KB_PER_TRANSFER_READ, DEVSTAT_ARG_LD },
|
||||
{ DSM_KB_PER_TRANSFER_WRITE, DEVSTAT_ARG_LD },
|
||||
{ DSM_TRANSFERS_PER_SECOND, DEVSTAT_ARG_LD },
|
||||
{ DSM_TRANSFERS_PER_SECOND_READ, DEVSTAT_ARG_LD },
|
||||
{ DSM_TRANSFERS_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
|
||||
{ DSM_TRANSFERS_PER_SECOND_OTHER, DEVSTAT_ARG_LD },
|
||||
{ DSM_MB_PER_SECOND, DEVSTAT_ARG_LD },
|
||||
{ DSM_MB_PER_SECOND_READ, DEVSTAT_ARG_LD },
|
||||
{ DSM_MB_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
|
||||
{ DSM_BLOCKS_PER_SECOND, DEVSTAT_ARG_LD },
|
||||
{ DSM_BLOCKS_PER_SECOND_READ, DEVSTAT_ARG_LD },
|
||||
{ DSM_BLOCKS_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
|
||||
{ DSM_MS_PER_TRANSACTION, DEVSTAT_ARG_LD },
|
||||
{ DSM_MS_PER_TRANSACTION_READ, DEVSTAT_ARG_LD },
|
||||
{ DSM_MS_PER_TRANSACTION_WRITE, DEVSTAT_ARG_LD }
|
||||
};
|
||||
|
||||
static char *namelist[] = {
|
||||
#define X_NUMDEVS 0
|
||||
"_devstat_num_devs",
|
||||
#define X_GENERATION 1
|
||||
"_devstat_generation",
|
||||
#define X_VERSION 2
|
||||
"_devstat_version",
|
||||
#define X_DEVICE_STATQ 3
|
||||
"_device_statq",
|
||||
#define X_END 4
|
||||
};
|
||||
|
||||
/*
|
||||
* Local function declarations.
|
||||
*/
|
||||
static int compare_select(const void *arg1, const void *arg2);
|
||||
static int readkmem(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes);
|
||||
static int readkmem_nl(kvm_t *kd, char *name, void *buf, size_t nbytes);
|
||||
static char *get_devstat_kvm(kvm_t *kd);
|
||||
|
||||
#define KREADNL(kd, var, val) \
|
||||
readkmem_nl(kd, namelist[var], &val, sizeof(val))
|
||||
|
||||
int
|
||||
getnumdevs(void)
|
||||
devstat_getnumdevs(kvm_t *kd)
|
||||
{
|
||||
size_t numdevsize;
|
||||
int numdevs;
|
||||
char *func_name = "getnumdevs";
|
||||
char *func_name = "devstat_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);
|
||||
if (kd == NULL) {
|
||||
if (sysctlbyname("kern.devstat.numdevs", &numdevs,
|
||||
&numdevsize, NULL, 0) == -1) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: error getting number of devices\n"
|
||||
"%s: %s", func_name, func_name,
|
||||
strerror(errno));
|
||||
return(-1);
|
||||
} else
|
||||
return(numdevs);
|
||||
} else {
|
||||
if (KREADNL(kd, X_NUMDEVS, numdevs) == -1)
|
||||
return(-1);
|
||||
else
|
||||
return(numdevs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -102,24 +173,32 @@ getnumdevs(void)
|
||||
* this function is called and the device list is retreived.
|
||||
*/
|
||||
long
|
||||
getgeneration(void)
|
||||
devstat_getgeneration(kvm_t *kd)
|
||||
{
|
||||
size_t gensize;
|
||||
long generation;
|
||||
char *func_name = "getgeneration";
|
||||
char *func_name = "devstat_getgeneration";
|
||||
|
||||
gensize = sizeof(long);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (kd == NULL) {
|
||||
if (sysctlbyname("kern.devstat.generation", &generation,
|
||||
&gensize, NULL, 0) == -1) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: error getting devstat generation\n%s: %s",
|
||||
func_name, func_name, strerror(errno));
|
||||
return(-1);
|
||||
} else
|
||||
return(generation);
|
||||
} else {
|
||||
if (KREADNL(kd, X_GENERATION, generation) == -1)
|
||||
return(-1);
|
||||
else
|
||||
return(generation);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -129,24 +208,32 @@ getgeneration(void)
|
||||
* whether they are out of sync with the kernel.
|
||||
*/
|
||||
int
|
||||
getversion(void)
|
||||
devstat_getversion(kvm_t *kd)
|
||||
{
|
||||
size_t versize;
|
||||
int version;
|
||||
char *func_name = "getversion";
|
||||
char *func_name = "devstat_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);
|
||||
if (kd == NULL) {
|
||||
if (sysctlbyname("kern.devstat.version", &version, &versize,
|
||||
NULL, 0) == -1) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: error getting devstat version\n%s: %s",
|
||||
func_name, func_name, strerror(errno));
|
||||
return(-1);
|
||||
} else
|
||||
return(version);
|
||||
} else {
|
||||
if (KREADNL(kd, X_VERSION, version) == -1)
|
||||
return(-1);
|
||||
else
|
||||
return(version);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -155,14 +242,14 @@ getversion(void)
|
||||
* devstat error buffer, and return -1. If they match, return 0.
|
||||
*/
|
||||
int
|
||||
checkversion(void)
|
||||
devstat_checkversion(kvm_t *kd)
|
||||
{
|
||||
int retval = 0;
|
||||
int errlen = 0;
|
||||
char *func_name = "checkversion";
|
||||
char *func_name = "devstat_checkversion";
|
||||
int version;
|
||||
|
||||
version = getversion();
|
||||
version = devstat_getversion(kd);
|
||||
|
||||
if (version != DEVSTAT_VERSION) {
|
||||
int buflen = 0;
|
||||
@ -226,7 +313,7 @@ checkversion(void)
|
||||
* 1 -- device list has changed
|
||||
*/
|
||||
int
|
||||
getdevs(struct statinfo *stats)
|
||||
devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
{
|
||||
int error;
|
||||
size_t dssize;
|
||||
@ -234,87 +321,100 @@ getdevs(struct statinfo *stats)
|
||||
long oldgeneration;
|
||||
int retval = 0;
|
||||
struct devinfo *dinfo;
|
||||
char *func_name = "getdevs";
|
||||
char *func_name = "devstat_getdevs";
|
||||
|
||||
dinfo = stats->dinfo;
|
||||
|
||||
if (dinfo == NULL) {
|
||||
sprintf(devstat_errbuf, "%s: stats->dinfo was NULL", func_name);
|
||||
snprintf(devstat_errbuf, sizeof(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(long);
|
||||
dinfo->mem_ptr = (u_int8_t *)malloc(dssize);
|
||||
} else
|
||||
dssize =(dinfo->numdevs * sizeof(struct devstat)) +sizeof(long);
|
||||
|
||||
/* 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) {
|
||||
if (kd == NULL) {
|
||||
/* If this is our first time through, mem_ptr will be null. */
|
||||
if (dinfo->mem_ptr == NULL) {
|
||||
/*
|
||||
* No need to set the error string here, getnumdevs()
|
||||
* will do that if it fails.
|
||||
* 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(long);
|
||||
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,
|
||||
sizeof(long);
|
||||
dinfo->mem_ptr = (u_int8_t *)malloc(dssize);
|
||||
} else
|
||||
dssize = (dinfo->numdevs * sizeof(struct devstat)) +
|
||||
sizeof(long);
|
||||
|
||||
/*
|
||||
* 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(long);
|
||||
dinfo->mem_ptr = (u_int8_t *)
|
||||
realloc(dinfo->mem_ptr, dssize);
|
||||
if ((error = sysctlbyname("kern.devstat.all",
|
||||
dinfo->mem_ptr, &dssize, NULL, 0)) == -1) {
|
||||
snprintf(devstat_errbuf,
|
||||
sizeof(devstat_errbuf),
|
||||
"%s: error getting device "
|
||||
"stats\n%s: %s", func_name,
|
||||
func_name, strerror(errno));
|
||||
return(-1);
|
||||
}
|
||||
} else {
|
||||
snprintf(devstat_errbuf, sizeof(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This is of course non-atomic, but since we are working
|
||||
* on a core dump, the generation is unlikely to change
|
||||
*/
|
||||
if ((dinfo->numdevs = getnumdevs()) == -1)
|
||||
return(-1);
|
||||
if ((dinfo->mem_ptr = get_devstat_kvm(kd)) == NULL)
|
||||
return(-1);
|
||||
}
|
||||
/*
|
||||
* The sysctl spits out the generation as the first four bytes,
|
||||
* then all of the device statistics structures.
|
||||
@ -415,12 +515,13 @@ getdevs(struct statinfo *stats)
|
||||
* 1 -- selected devices changed
|
||||
*/
|
||||
int
|
||||
selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long 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)
|
||||
devstat_selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long 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;
|
||||
@ -879,17 +980,19 @@ compare_select(const void *arg1, const void *arg2)
|
||||
* device matching expression from it.
|
||||
*/
|
||||
int
|
||||
buildmatch(char *match_str, struct devstat_match **matches, int *num_matches)
|
||||
devstat_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";
|
||||
char *func_name = "devstat_buildmatch";
|
||||
|
||||
/* We can't do much without a string to parse */
|
||||
if (match_str == NULL) {
|
||||
sprintf(devstat_errbuf, "%s: no match expression", func_name);
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: no match expression", func_name);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
@ -905,8 +1008,8 @@ buildmatch(char *match_str, struct devstat_match **matches, int *num_matches)
|
||||
|
||||
/* The user gave us too many type arguments */
|
||||
if (num_args > 3) {
|
||||
sprintf(devstat_errbuf, "%s: too many type arguments",
|
||||
func_name);
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: too many type arguments", func_name);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
@ -971,7 +1074,8 @@ buildmatch(char *match_str, struct devstat_match **matches, int *num_matches)
|
||||
*/
|
||||
if (((*matches)[*num_matches].match_fields &
|
||||
match_table[j].match_field) != 0) {
|
||||
sprintf(devstat_errbuf,
|
||||
snprintf(devstat_errbuf,
|
||||
sizeof(devstat_errbuf),
|
||||
"%s: cannot have more than "
|
||||
"one match item in a single "
|
||||
"category", func_name);
|
||||
@ -1032,7 +1136,8 @@ compute_stats(struct devstat *current, struct devstat *previous,
|
||||
* current is the only mandatory field.
|
||||
*/
|
||||
if (current == NULL) {
|
||||
sprintf(devstat_errbuf, "%s: current stats structure was NULL",
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: current stats structure was NULL",
|
||||
func_name);
|
||||
return(-1);
|
||||
}
|
||||
@ -1110,7 +1215,7 @@ compute_stats(struct devstat *current, struct devstat *previous,
|
||||
}
|
||||
|
||||
long double
|
||||
compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
devstat_compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
{
|
||||
struct timeval busy_time;
|
||||
u_int64_t busy_usec;
|
||||
@ -1126,3 +1231,453 @@ compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
|
||||
return(etime);
|
||||
}
|
||||
|
||||
int
|
||||
devstat_compute_statistics(struct devstat *current, struct devstat *previous,
|
||||
long double etime, ...)
|
||||
{
|
||||
char *func_name = "devstat_compute_statistics";
|
||||
u_int64_t totalbytes, totalbytesread, totalbyteswrite;
|
||||
u_int64_t totaltransfers, totaltransfersread, totaltransferswrite;
|
||||
u_int64_t totaltransfersother, totalblocks, totalblocksread;
|
||||
u_int64_t totalblockswrite;
|
||||
va_list ap;
|
||||
devstat_metric metric;
|
||||
u_int64_t *destu64;
|
||||
long double *destld;
|
||||
int retval;
|
||||
|
||||
retval = 0;
|
||||
|
||||
/*
|
||||
* current is the only mandatory field.
|
||||
*/
|
||||
if (current == NULL) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: current stats structure was NULL", func_name);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
totalbytesread = current->bytes_read -
|
||||
((previous) ? previous->bytes_read : 0);
|
||||
totalbyteswrite = current->bytes_written -
|
||||
((previous) ? previous->bytes_written : 0);
|
||||
|
||||
totalbytes = totalbytesread + totalbyteswrite;
|
||||
|
||||
totaltransfersread = current->num_reads -
|
||||
((previous) ? previous->num_reads : 0);
|
||||
|
||||
totaltransferswrite = current->num_writes -
|
||||
((previous) ? previous->num_writes : 0);
|
||||
|
||||
totaltransfersother = current->num_other -
|
||||
((previous) ? previous->num_other : 0);
|
||||
|
||||
totaltransfers = totaltransfersread + totaltransferswrite +
|
||||
totaltransfersother;
|
||||
|
||||
totalblocks = totalbytes;
|
||||
totalblocksread = totalbytesread;
|
||||
totalblockswrite = totalbyteswrite;
|
||||
|
||||
if (current->block_size > 0) {
|
||||
totalblocks /= current->block_size;
|
||||
totalblocksread /= current->block_size;
|
||||
totalblockswrite /= current->block_size;
|
||||
} else {
|
||||
totalblocks /= 512;
|
||||
totalblocksread /= 512;
|
||||
totalblockswrite /= 512;
|
||||
}
|
||||
|
||||
va_start(ap, etime);
|
||||
|
||||
while ((metric = (devstat_metric)va_arg(ap, devstat_metric)) != 0) {
|
||||
|
||||
if (metric == DSM_NONE)
|
||||
break;
|
||||
|
||||
if (metric >= DSM_MAX) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: metric %d is out of range", func_name,
|
||||
metric);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
switch (devstat_arg_list[metric].argtype) {
|
||||
case DEVSTAT_ARG_UINT64:
|
||||
destu64 = (u_int64_t *)va_arg(ap, u_int64_t *);
|
||||
if (destu64 == NULL) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: argument type not u_int64_t * or "
|
||||
"argument type missing", func_name);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
break; /* NOTREACHED */
|
||||
}
|
||||
break;
|
||||
case DEVSTAT_ARG_LD:
|
||||
destld = (long double *)va_arg(ap, long double *);
|
||||
if (destld == NULL) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: argument type not long double * "
|
||||
"or argument type missing", func_name);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
break; /* NOTREACHED */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: unknown argument type %d", func_name,
|
||||
devstat_arg_list[metric].argtype);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
break; /* NOTREACHED */
|
||||
}
|
||||
|
||||
switch (metric) {
|
||||
case DSM_TOTAL_BYTES:
|
||||
*destu64 = totalbytes;
|
||||
break;
|
||||
case DSM_TOTAL_BYTES_READ:
|
||||
*destu64 = totalbytesread;
|
||||
break;
|
||||
case DSM_TOTAL_BYTES_WRITE:
|
||||
*destu64 = totalbyteswrite;
|
||||
break;
|
||||
case DSM_TOTAL_TRANSFERS:
|
||||
*destu64 = totaltransfers;
|
||||
break;
|
||||
case DSM_TOTAL_TRANSFERS_READ:
|
||||
*destu64 = totaltransfersread;
|
||||
break;
|
||||
case DSM_TOTAL_TRANSFERS_WRITE:
|
||||
*destu64 = totaltransferswrite;
|
||||
break;
|
||||
case DSM_TOTAL_TRANSFERS_OTHER:
|
||||
*destu64 = totaltransfersother;
|
||||
break;
|
||||
case DSM_TOTAL_BLOCKS:
|
||||
*destu64 = totalblocks;
|
||||
break;
|
||||
case DSM_TOTAL_BLOCKS_READ:
|
||||
*destu64 = totalblocksread;
|
||||
break;
|
||||
case DSM_TOTAL_BLOCKS_WRITE:
|
||||
*destu64 = totalblockswrite;
|
||||
break;
|
||||
case DSM_KB_PER_TRANSFER:
|
||||
*destld = totalbytes;
|
||||
*destld /= 1024;
|
||||
if (totaltransfers > 0)
|
||||
*destld /= totaltransfers;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_KB_PER_TRANSFER_READ:
|
||||
*destld = totalbytesread;
|
||||
*destld /= 1024;
|
||||
if (totaltransfersread > 0)
|
||||
*destld /= totaltransfersread;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_KB_PER_TRANSFER_WRITE:
|
||||
*destld = totalbyteswrite;
|
||||
*destld /= 1024;
|
||||
if (totaltransferswrite > 0)
|
||||
*destld /= totaltransferswrite;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_TRANSFERS_PER_SECOND:
|
||||
if (etime > 0.0) {
|
||||
*destld = totaltransfers;
|
||||
*destld /= etime;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_TRANSFERS_PER_SECOND_READ:
|
||||
if (etime > 0.0) {
|
||||
*destld = totaltransfersread;
|
||||
*destld /= etime;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_TRANSFERS_PER_SECOND_WRITE:
|
||||
if (etime > 0.0) {
|
||||
*destld = totaltransferswrite;
|
||||
*destld /= etime;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_TRANSFERS_PER_SECOND_OTHER:
|
||||
if (etime > 0.0) {
|
||||
*destld = totaltransfersother;
|
||||
*destld /= etime;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_MB_PER_SECOND:
|
||||
*destld = totalbytes;
|
||||
*destld /= 1024 * 1024;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_MB_PER_SECOND_READ:
|
||||
*destld = totalbytesread;
|
||||
*destld /= 1024 * 1024;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_MB_PER_SECOND_WRITE:
|
||||
*destld = totalbyteswrite;
|
||||
*destld /= 1024 * 1024;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_BLOCKS_PER_SECOND:
|
||||
*destld = totalblocks;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_BLOCKS_PER_SECOND_READ:
|
||||
*destld = totalblocksread;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_BLOCKS_PER_SECOND_WRITE:
|
||||
*destld = totalblockswrite;
|
||||
if (etime > 0.0)
|
||||
*destld /= etime;
|
||||
else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
/*
|
||||
* This calculation is somewhat bogus. It simply divides
|
||||
* the elapsed time by the total number of transactions
|
||||
* completed. While that does give the caller a good
|
||||
* picture of the average rate of transaction completion,
|
||||
* it doesn't necessarily give the caller a good view of
|
||||
* how long transactions took to complete on average.
|
||||
* Those two numbers will be different for a device that
|
||||
* can handle more than one transaction at a time. e.g.
|
||||
* SCSI disks doing tagged queueing.
|
||||
*
|
||||
* The only way to accurately determine the real average
|
||||
* time per transaction would be to compute and store the
|
||||
* time on a per-transaction basis. That currently isn't
|
||||
* done in the kernel, and would only be desireable if it
|
||||
* could be implemented in a somewhat non-intrusive and high
|
||||
* performance way.
|
||||
*/
|
||||
case DSM_MS_PER_TRANSACTION:
|
||||
if (totaltransfers > 0) {
|
||||
*destld = etime;
|
||||
*destld /= totaltransfers;
|
||||
*destld *= 1000;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
/*
|
||||
* As above, these next two really only give the average
|
||||
* rate of completion for read and write transactions, not
|
||||
* the average time the transaction took to complete.
|
||||
*/
|
||||
case DSM_MS_PER_TRANSACTION_READ:
|
||||
if (totaltransfersread > 0) {
|
||||
*destld = etime;
|
||||
*destld /= totaltransfersread;
|
||||
*destld *= 1000;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
case DSM_MS_PER_TRANSACTION_WRITE:
|
||||
if (totaltransferswrite > 0) {
|
||||
*destld = etime;
|
||||
*destld /= totaltransferswrite;
|
||||
*destld *= 1000;
|
||||
} else
|
||||
*destld = 0.0;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* This shouldn't happen, since we should have
|
||||
* caught any out of range metrics at the top of
|
||||
* the loop.
|
||||
*/
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: unknown metric %d", func_name, metric);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
break; /* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
bailout:
|
||||
|
||||
va_end(ap);
|
||||
return(retval);
|
||||
}
|
||||
|
||||
static int
|
||||
readkmem(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes)
|
||||
{
|
||||
char *func_name = "readkmem";
|
||||
|
||||
if (kvm_read(kd, addr, buf, nbytes) == -1) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: error reading value (kvm_read): %s", func_name,
|
||||
kvm_geterr(kd));
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
readkmem_nl(kvm_t *kd, char *name, void *buf, size_t nbytes)
|
||||
{
|
||||
char *func_name = "readkmem_nl";
|
||||
struct nlist nl[2] = { { name }, { NULL } };
|
||||
|
||||
if (kvm_nlist(kd, nl) == -1) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: error getting name list (kvm_nlist): %s",
|
||||
func_name, kvm_geterr(kd));
|
||||
return(-1);
|
||||
}
|
||||
return(readkmem(kd, nl[0].n_value, buf, nbytes));
|
||||
}
|
||||
|
||||
/*
|
||||
* This duplicates the functionality of the kernel sysctl handler for poking
|
||||
* through crash dumps.
|
||||
*/
|
||||
static char *
|
||||
get_devstat_kvm(kvm_t *kd)
|
||||
{
|
||||
int error, i, wp;
|
||||
long gen;
|
||||
struct devstat *nds;
|
||||
struct devstat ds;
|
||||
struct devstatlist dhead;
|
||||
int num_devs;
|
||||
char *rv = NULL;
|
||||
char *func_name = "get_devstat_kvm";
|
||||
|
||||
if ((num_devs = getnumdevs()) <= 0)
|
||||
return(NULL);
|
||||
error = 0;
|
||||
if (KREADNL(kd, X_DEVICE_STATQ, dhead) == -1)
|
||||
return(NULL);
|
||||
|
||||
nds = STAILQ_FIRST(&dhead);
|
||||
|
||||
if ((rv = malloc(sizeof(gen))) == NULL) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: out of memory (initial malloc failed)",
|
||||
func_name);
|
||||
return(NULL);
|
||||
}
|
||||
gen = getgeneration();
|
||||
memcpy(rv, &gen, sizeof(gen));
|
||||
wp = sizeof(gen);
|
||||
/*
|
||||
* Now push out all the devices.
|
||||
*/
|
||||
for (i = 0; (nds != NULL) && (i < num_devs);
|
||||
nds = STAILQ_NEXT(nds, dev_links), i++) {
|
||||
if (readkmem(kd, (long)nds, &ds, sizeof(ds)) == -1) {
|
||||
free(rv);
|
||||
return(NULL);
|
||||
}
|
||||
nds = &ds;
|
||||
rv = (char *)reallocf(rv, sizeof(gen) +
|
||||
sizeof(ds) * (i + 1));
|
||||
if (rv == NULL) {
|
||||
snprintf(devstat_errbuf, sizeof(devstat_errbuf),
|
||||
"%s: out of memory (malloc failed)",
|
||||
func_name);
|
||||
return(NULL);
|
||||
}
|
||||
memcpy(rv + wp, &ds, sizeof(ds));
|
||||
wp += sizeof(ds);
|
||||
}
|
||||
return(rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compatability functions for libdevstat 2. These are deprecated and may
|
||||
* eventually be removed.
|
||||
*/
|
||||
int
|
||||
getnumdevs(void)
|
||||
{
|
||||
return(devstat_getnumdevs(NULL));
|
||||
}
|
||||
|
||||
long
|
||||
getgeneration(void)
|
||||
{
|
||||
return(devstat_getgeneration(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
getversion(void)
|
||||
{
|
||||
return(devstat_getversion(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
checkversion(void)
|
||||
{
|
||||
return(devstat_checkversion(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
getdevs(struct statinfo *stats)
|
||||
{
|
||||
return(devstat_getdevs(NULL, stats));
|
||||
}
|
||||
|
||||
int
|
||||
selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long 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)
|
||||
{
|
||||
|
||||
return(devstat_selectdevs(dev_select, num_selected, num_selections,
|
||||
select_generation, current_generation, devices, numdevs,
|
||||
matches, num_matches, dev_selections, num_dev_selections,
|
||||
select_mode, maxshowdevs, perf_select));
|
||||
}
|
||||
|
||||
int
|
||||
buildmatch(char *match_str, struct devstat_match **matches,
|
||||
int *num_matches)
|
||||
{
|
||||
return(devstat_buildmatch(match_str, matches, num_matches));
|
||||
}
|
||||
|
||||
long double
|
||||
compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
{
|
||||
return(devstat_compute_etime(cur_time, prev_time));
|
||||
}
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/devicestat.h>
|
||||
|
||||
#include <kvm.h>
|
||||
|
||||
#define DEVSTAT_ERRBUF_SIZE 2048 /* size of the devstat library error string */
|
||||
|
||||
extern char devstat_errbuf[];
|
||||
@ -44,6 +46,37 @@ typedef enum {
|
||||
DEVSTAT_MATCH_PASS = 0x04
|
||||
} devstat_match_flags;
|
||||
|
||||
typedef enum {
|
||||
DSM_NONE,
|
||||
DSM_TOTAL_BYTES,
|
||||
DSM_TOTAL_BYTES_READ,
|
||||
DSM_TOTAL_BYTES_WRITE,
|
||||
DSM_TOTAL_TRANSFERS,
|
||||
DSM_TOTAL_TRANSFERS_READ,
|
||||
DSM_TOTAL_TRANSFERS_WRITE,
|
||||
DSM_TOTAL_TRANSFERS_OTHER,
|
||||
DSM_TOTAL_BLOCKS,
|
||||
DSM_TOTAL_BLOCKS_READ,
|
||||
DSM_TOTAL_BLOCKS_WRITE,
|
||||
DSM_KB_PER_TRANSFER,
|
||||
DSM_KB_PER_TRANSFER_READ,
|
||||
DSM_KB_PER_TRANSFER_WRITE,
|
||||
DSM_TRANSFERS_PER_SECOND,
|
||||
DSM_TRANSFERS_PER_SECOND_READ,
|
||||
DSM_TRANSFERS_PER_SECOND_WRITE,
|
||||
DSM_TRANSFERS_PER_SECOND_OTHER,
|
||||
DSM_MB_PER_SECOND,
|
||||
DSM_MB_PER_SECOND_READ,
|
||||
DSM_MB_PER_SECOND_WRITE,
|
||||
DSM_BLOCKS_PER_SECOND,
|
||||
DSM_BLOCKS_PER_SECOND_READ,
|
||||
DSM_BLOCKS_PER_SECOND_WRITE,
|
||||
DSM_MS_PER_TRANSACTION,
|
||||
DSM_MS_PER_TRANSACTION_READ,
|
||||
DSM_MS_PER_TRANSACTION_WRITE,
|
||||
DSM_MAX
|
||||
} devstat_metric;
|
||||
|
||||
struct devstat_match {
|
||||
devstat_match_flags match_fields;
|
||||
devstat_type_flags device_type;
|
||||
@ -88,6 +121,7 @@ typedef enum {
|
||||
} devstat_select_mode;
|
||||
|
||||
__BEGIN_DECLS
|
||||
/* Legacy interfaces. */
|
||||
int getnumdevs(void);
|
||||
long getgeneration(void);
|
||||
int getversion(void);
|
||||
@ -110,6 +144,27 @@ int compute_stats(struct devstat *current, struct devstat *previous,
|
||||
long double *blocks_per_second,
|
||||
long double *ms_per_transaction);
|
||||
long double compute_etime(struct timeval cur_time, struct timeval prev_time);
|
||||
|
||||
/* New interfaces. */
|
||||
int devstat_getnumdevs(kvm_t *kd);
|
||||
long devstat_getgeneration(kvm_t *kd);
|
||||
int devstat_getversion(kvm_t *kd);
|
||||
int devstat_checkversion(kvm_t *kd);
|
||||
int devstat_getdevs(kvm_t *kd, struct statinfo *stats);
|
||||
int devstat_selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long 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 devstat_buildmatch(char *match_str, struct devstat_match **matches,
|
||||
int *num_matches);
|
||||
int devstat_compute_statistics(struct devstat *current,
|
||||
struct devstat *previous,
|
||||
long double etime, ...);
|
||||
long double devstat_compute_etime(struct timeval cur_time,
|
||||
struct timeval prev_time);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _DEVSTAT_H */
|
||||
|
@ -6,8 +6,8 @@ MAN= vinum.8
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../../sys -Wall
|
||||
|
||||
DPADD= ${LIBREADLINE} ${LIBTERMCAP} ${LIBDEVSTAT}
|
||||
LDADD= -lreadline -ltermcap -ldevstat
|
||||
DPADD= ${LIBREADLINE} ${LIBTERMCAP} ${LIBDEVSTAT} ${LIBKVM}
|
||||
LDADD= -lreadline -ltermcap -ldevstat -lkvm
|
||||
|
||||
.PATH: ${.CURDIR}/../../sys/dev/vinum
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user