summaryrefslogtreecommitdiffstats
path: root/system_cmds/iostat.tproj
diff options
context:
space:
mode:
Diffstat (limited to 'system_cmds/iostat.tproj')
-rw-r--r--system_cmds/iostat.tproj/iostat.8302
-rw-r--r--system_cmds/iostat.tproj/iostat.c998
2 files changed, 1300 insertions, 0 deletions
diff --git a/system_cmds/iostat.tproj/iostat.8 b/system_cmds/iostat.tproj/iostat.8
new file mode 100644
index 0000000..f79c834
--- /dev/null
+++ b/system_cmds/iostat.tproj/iostat.8
@@ -0,0 +1,302 @@
+.\"
+.\" Copyright (c) 1997 Kenneth D. Merry.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)iostat.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd May 22, 2015
+.Dt IOSTAT 8
+.Os
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl CUdKIoT?\&
+.Op Fl c Ar count
+.Op Fl n Ar devs
+.Op Fl w Ar wait
+.Op Ar drives
+.Sh DESCRIPTION
+The
+.Nm
+utility displays kernel
+.Tn I/O
+statistics on terminal, device and cpu operations.
+The first statistics that are printed are averaged over the system uptime.
+To get information about the current activity, a suitable wait time should
+be specified, so that the subsequent sets of printed statistics will be
+averaged over that time.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl ?\&
+Display a usage statement and exit.
+.It Fl c
+Repeat the display
+.Ar count
+times.
+If no
+.Ar wait
+interval is specified, the default is 1 second.
+.It Fl C
+Display CPU statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl d
+Display only device statistics.
+If this flag is turned on, only device statistics will be displayed, unless
+.Fl C
+or
+.Fl U
+or
+.Fl T
+is also specified to enable the display of CPU, load average or TTY statistics.
+.It Fl I
+Display total statistics for a given time period, rather than average
+statistics for each second during that time period.
+.It Fl K
+In the blocks transferred display (-o), display block count in kilobytes rather
+then the device native block size.
+.It Fl n
+Display up to
+.Ar devs
+number of devices.
+The
+.Nm
+utility will display fewer devices if there are not
+.Ar devs
+devices present.
+.It Fl o
+Display old-style
+.Nm
+device statistics.
+Sectors per second, transfers per second, and milliseconds per seek are
+displayed.
+If
+.Fl I
+is specified, total blocks/sectors, total transfers, and
+milliseconds per seek are displayed.
+.It Fl T
+Display TTY statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl U
+Display system load averages.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl w
+Pause
+.Ar wait
+seconds between each display.
+If no repeat
+.Ar count
+is specified, the default is infinity.
+.El
+.Pp
+The
+.Nm
+utility displays its information in the following format:
+.Bl -tag -width flag
+.It tty
+.Bl -tag -width indent -compact
+.It tin
+characters read from terminals
+.It tout
+characters written to terminals
+.El
+.It devices
+Device operations.
+The header of the field is the device name and unit number.
+The
+.Nm
+utility
+will display as many devices as will fit in a standard 80 column screen, or
+the maximum number of devices in the system, whichever is smaller.
+If
+.Fl n
+is specified on the command line,
+.Nm
+will display the smaller of the
+requested number of devices, and the maximum number of devices in the system.
+To force
+.Nm
+to display specific drives, their names may be supplied on the command
+line.
+The
+.Nm
+utility
+will not display more devices than will fit in an 80 column screen, unless
+the
+.Fl n
+argument is given on the command line to specify a maximum number of
+devices to display, or the list of specified devices exceeds 80 columns.
+If fewer devices are specified on the command line than will fit in an 80
+column screen,
+.Nm
+will show only the specified devices.
+.Pp
+The standard
+.Nm
+device display shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It tps
+transfers per second
+.It MB/s
+megabytes per second
+.El
+.Pp
+The standard
+.Nm
+device display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It xfrs
+total number of transfers
+.It MB
+total number of megabytes transferred
+.El
+.Pp
+The old-style
+.Nm
+display (using
+.Fl o )
+shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It sps
+sectors transferred per second
+.It tps
+transfers per second
+.It msps
+average milliseconds per transaction
+.El
+.Pp
+The old-style
+.Nm
+display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It blk
+total blocks/sectors transferred
+.It xfr
+total transfers
+.It msps
+average milliseconds per transaction
+.El
+.It cpu
+.Bl -tag -width indent -compact
+.It \&us
+% of cpu time in user mode
+.It \&sy
+% of cpu time in system mode
+.It \&id
+% of cpu time in idle mode
+.El
+.El
+.Sh EXAMPLES
+.Dl iostat -w 1 disk0 disk2
+.Pp
+Display statistics for the first and third disk devices device every
+second ad infinitum.
+.Pp
+.Dl iostat -c 2
+.Pp
+Display the statistics for the first four devices in the system twice, with
+a one second display interval.
+.Pp
+.Dl iostat -Iw 3
+.Pp
+Display total statistics every three seconds ad infinitum.
+.Pp
+.Dl iostat -odICTw 2 -c 9
+.Pp
+Display total statistics using the old-style output format 9 times, with
+a two second interval between each measurement/display.
+The
+.Fl d
+flag generally disables the TTY and CPU displays, but since the
+.Fl T
+and
+.Fl C
+flags are given, the TTY and CPU displays will be displayed.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr top 1 ,
+.Xr vm_stat 1
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 3.0 .
diff --git a/system_cmds/iostat.tproj/iostat.c b/system_cmds/iostat.tproj/iostat.c
new file mode 100644
index 0000000..a9c73cb
--- /dev/null
+++ b/system_cmds/iostat.tproj/iostat.c
@@ -0,0 +1,998 @@
+/*
+ * Copyright (c) 1999-2019 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
+ * Reserved. This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License'). You may not use this file
+ * except in compliance with the License. Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1997, 1998, 2000, 2001 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Parts of this program are derived from the original FreeBSD iostat
+ * program:
+ */
+/*-
+ * Copyright (c) 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Ideas for the new iostat statistics output modes taken from the NetBSD
+ * version of iostat:
+ */
+/*
+ * Copyright (c) 1996 John M. Vinopal
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by John M. Vinopal.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define IOKIT 1 /* to get io_name_t in device_types.h */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/IOBSD.h>
+#include <mach/mach_host.h> /* host_statistics */
+#include <err.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXDRIVES 16 /* most drives we will record */
+#define MAXDRIVENAME 31 /* largest drive name we allow */
+
+struct drivestats {
+ io_registry_entry_t driver;
+ char name[MAXDRIVENAME + 1];
+ u_int64_t blocksize;
+ u_int64_t total_bytes;
+ u_int64_t total_transfers;
+ u_int64_t total_time;
+};
+
+static struct drivestats drivestat[MAXDRIVES];
+
+static struct timeval cur_time, last_time;
+
+struct statinfo {
+ long tk_nin;
+ long tk_nout;
+ host_cpu_load_info_data_t load;
+};
+
+static struct statinfo cur, last;
+
+static mach_port_t host_priv_port;
+static mach_port_t masterPort;
+
+static int num_devices;
+static int maxshowdevs;
+static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Uflag = 0, Kflag = 0;
+static volatile sig_atomic_t phdr_flag = 0;
+static IONotificationPortRef notifyPort;
+
+/* local function declarations */
+static void usage(void);
+static void phdr(int signo);
+static void do_phdr(void);
+static void devstats(int perf_select, long double etime, int havelast);
+static void cpustats(void);
+static void loadstats(void);
+static int readvar(const char *name, void *ptr, size_t len);
+
+static int record_all_devices(void);
+static void record_drivelist(void* context, io_iterator_t drivelist);
+static void remove_drivelist(void* context, io_iterator_t drivelist);
+static int record_one_device(char *name);
+static int record_device(io_registry_entry_t drive);
+
+static int compare_drivestats(const void* pa, const void* pb);
+
+static long double compute_etime(struct timeval cur_time,
+ struct timeval prev_time);
+
+static void
+usage(void)
+{
+ /*
+ * We also support the following 'traditional' syntax:
+ * iostat [drives] [wait [count]]
+ * This isn't mentioned in the man page, or the usage statement,
+ * but it is supported.
+ */
+ fprintf(stderr, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
+ "\t [-w wait] [drives]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int hflag = 0, cflag = 0, wflag = 0, nflag = 0;
+ int count = 0, waittime = 0;
+ int headercount;
+ int num_devices_specified;
+ int havelast = 0;
+
+ CFRunLoopSourceRef rls;
+
+ maxshowdevs = 3;
+
+ while ((c = getopt(argc, argv, "c:CdIKM:n:oTUw:?")) != -1) {
+ switch(c) {
+ case 'c':
+ cflag++;
+ count = atoi(optarg);
+ if (count < 1)
+ errx(1, "count %d is < 1", count);
+ break;
+ case 'C':
+ Cflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'I':
+ Iflag++;
+ break;
+ case 'K':
+ Kflag++;
+ break;
+ case 'n':
+ nflag++;
+ maxshowdevs = atoi(optarg);
+ if (maxshowdevs < 0)
+ errx(1, "number of devices %d is < 0",
+ maxshowdevs);
+ break;
+ case 'o':
+ oflag++;
+ break;
+ case 'T':
+ Tflag++;
+ break;
+ case 'U':
+ Uflag++;
+ break;
+ case 'w':
+ wflag++;
+ waittime = atoi(optarg);
+ if (waittime < 1)
+ errx(1, "wait time is < 1");
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Get the Mach private port.
+ */
+ host_priv_port = mach_host_self();
+
+ /*
+ * Get the I/O Kit communication handle.
+ */
+ IOMasterPort(bootstrap_port, &masterPort);
+
+ notifyPort = IONotificationPortCreate(masterPort);
+ rls = IONotificationPortGetRunLoopSource(notifyPort);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+
+ /*
+ * Make sure Tflag, Cflag and Uflag are set if dflag == 0. If dflag is
+ * greater than 0, they may be 0 or non-zero.
+ */
+ if (dflag == 0) {
+ Cflag = 1;
+ Tflag = 1;
+ Uflag = 1;
+ }
+
+ /*
+ * TTY statistics are broken, disabling them.
+ */
+ Tflag = 0;
+
+ /*
+ * Figure out how many devices we should display if not given
+ * an explicit value.
+ */
+ if (nflag == 0) {
+ if (oflag > 0) {
+ if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
+ maxshowdevs = 5;
+ else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
+ maxshowdevs = 5;
+ else
+ maxshowdevs = 4;
+ } else {
+ if ((dflag > 0) && (Cflag == 0))
+ maxshowdevs = 4;
+ else
+ maxshowdevs = 3;
+ }
+ }
+
+ /*
+ * If the user specified any devices on the command line, record
+ * them for monitoring.
+ */
+ for (num_devices_specified = 0; *argv; ++argv) {
+ if (isdigit(**argv))
+ break;
+ if (record_one_device(*argv))
+ errx(1, "can't record '%s' for monitoring", *argv);
+ num_devices_specified++;
+ }
+ if (nflag == 0 && maxshowdevs < num_devices_specified)
+ maxshowdevs = num_devices_specified;
+
+ /* if no devices were specified, pick them ourselves */
+ if ((num_devices_specified == 0) && record_all_devices())
+ err(1, "can't find any devices to display");
+
+ /*
+ * Look for the traditional wait time and count arguments.
+ */
+ if (*argv) {
+ waittime = atoi(*argv);
+
+ /* Let the user know he goofed, but keep going anyway */
+ if (wflag != 0)
+ warnx("discarding previous wait interval, using"
+ " %d instead", waittime);
+ wflag++;
+
+ if (*++argv) {
+ count = atoi(*argv);
+ if (cflag != 0)
+ warnx("discarding previous count, using %d"
+ " instead", count);
+ cflag++;
+ } else
+ count = -1;
+ }
+
+ /*
+ * If the user specified a count, but not an interval, we default
+ * to an interval of 1 second.
+ */
+ if ((wflag == 0) && (cflag > 0))
+ waittime = 1;
+
+ /*
+ * If the user specified a wait time, but not a count, we want to
+ * go on ad infinitum. This can be redundant if the user uses the
+ * traditional method of specifying the wait, since in that case we
+ * already set count = -1 above. Oh well.
+ */
+ if ((wflag > 0) && (cflag == 0))
+ count = -1;
+
+ cur.tk_nout = 0;
+ cur.tk_nin = 0;
+
+ /*
+ * Set the busy time to the system boot time, so the stats are
+ * calculated since system boot.
+ */
+ if (readvar("kern.boottime", &cur_time, sizeof(cur_time)) != 0)
+ exit(1);
+
+ /*
+ * If the user stops the program (control-Z) and then resumes it,
+ * print out the header again.
+ */
+ (void)signal(SIGCONT, phdr);
+
+ for (headercount = 1;;) {
+ long tmp;
+ long double etime;
+
+ if (Tflag > 0) {
+ if ((readvar("kern.tty_nin", &cur.tk_nin,
+ sizeof(cur.tk_nin)) != 0)
+ || (readvar("kern.tty_nout",
+ &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
+ Tflag = 0;
+ warnx("disabling TTY statistics");
+ }
+ }
+
+ if (!--headercount || phdr_flag) {
+ phdr_flag = 0;
+ headercount = 20;
+ do_phdr();
+ }
+
+ last_time = cur_time;
+ gettimeofday(&cur_time, NULL);
+
+ if (Tflag > 0) {
+ tmp = cur.tk_nin;
+ cur.tk_nin -= last.tk_nin;
+ last.tk_nin = tmp;
+ tmp = cur.tk_nout;
+ cur.tk_nout -= last.tk_nout;
+ last.tk_nout = tmp;
+ }
+
+ etime = compute_etime(cur_time, last_time);
+
+ if (etime == 0.0)
+ etime = 1.0;
+
+ if (Tflag > 0)
+ printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ cur.tk_nout / etime);
+
+ devstats(hflag, etime, havelast);
+
+ if (Cflag > 0)
+ cpustats();
+
+ if (Uflag > 0)
+ loadstats();
+
+ printf("\n");
+ fflush(stdout);
+
+ if (count >= 0 && --count <= 0)
+ break;
+
+ /*
+ * Instead of sleep(waittime), wait in
+ * the RunLoop for IONotifications.
+ */
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)waittime, 1);
+
+ havelast = 1;
+ }
+
+ exit(0);
+}
+
+static void
+phdr(int signo)
+{
+
+ phdr_flag = 1;
+}
+
+static void
+do_phdr(void)
+{
+ register int i;
+
+ if (Tflag > 0)
+ (void)printf(" tty");
+
+ for (i = 0; i < num_devices && i < maxshowdevs; i++){
+ if (oflag > 0)
+ (void)printf("%12.6s ", drivestat[i].name);
+ else
+ printf("%19.6s ", drivestat[i].name);
+ }
+
+ if (Cflag > 0)
+ (void)printf(" cpu");
+
+ if (Uflag > 0)
+ (void)printf(" load average\n");
+ else
+ (void)printf("\n");
+
+ if (Tflag > 0)
+ (void)printf(" tin tout");
+
+ for (i=0; i < num_devices && i < maxshowdevs; i++){
+ if (oflag > 0) {
+ if (Iflag == 0)
+ (void)printf(" sps tps msps ");
+ else
+ (void)printf(" blk xfr msps ");
+ } else {
+ if (Iflag == 0)
+ printf(" KB/t tps MB/s ");
+ else
+ printf(" KB/t xfrs MB ");
+ }
+ }
+
+ if (Cflag > 0)
+ (void)printf(" us sy id");
+
+ if (Uflag > 0)
+ (void)printf(" 1m 5m 15m\n");
+ else
+ printf("\n");
+}
+
+static void
+devstats(int perf_select, long double etime, int havelast)
+{
+ CFNumberRef number;
+ CFDictionaryRef properties;
+ CFDictionaryRef statistics;
+ long double transfers_per_second;
+ long double kb_per_transfer, mb_per_second;
+ u_int64_t value;
+ u_int64_t total_bytes, total_transfers, total_blocks, total_time;
+ u_int64_t interval_bytes, interval_transfers, interval_blocks;
+ u_int64_t interval_time;
+ long double interval_mb;
+ long double blocks_per_second, ms_per_transaction;
+ kern_return_t status;
+ int i;
+
+ for (i = 0; i < num_devices && i < maxshowdevs; i++) {
+
+ /*
+ * If the drive goes away, we may not get any properties
+ * for it. So take some defaults.
+ */
+ total_bytes = 0;
+ total_transfers = 0;
+ total_time = 0;
+
+ /* get drive properties */
+ status = IORegistryEntryCreateCFProperties(drivestat[i].driver,
+ (CFMutableDictionaryRef *)&properties,
+ kCFAllocatorDefault,
+ kNilOptions);
+ if (status != KERN_SUCCESS)
+ continue;
+
+ /* get statistics from properties */
+ statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBlockStorageDriverStatisticsKey));
+ if (statistics) {
+
+ /*
+ * Get I/O volume.
+ */
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_bytes += value;
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_bytes += value;
+ }
+
+ /*
+ * Get I/O counts.
+ */
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_transfers += value;
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_transfers += value;
+ }
+
+ /*
+ * Get I/O time.
+ */
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_time += value;
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
+ CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &value);
+ total_time += value;
+ }
+
+ }
+ CFRelease(properties);
+
+ /*
+ * Compute delta values and stats.
+ */
+ interval_bytes = total_bytes - drivestat[i].total_bytes;
+ interval_transfers = total_transfers
+ - drivestat[i].total_transfers;
+ interval_time = total_time - drivestat[i].total_time;
+
+ /* update running totals, only once for -I */
+ if ((Iflag == 0) || (drivestat[i].total_bytes == 0)) {
+ drivestat[i].total_bytes = total_bytes;
+ drivestat[i].total_transfers = total_transfers;
+ drivestat[i].total_time = total_time;
+ }
+
+ interval_blocks = interval_bytes / drivestat[i].blocksize;
+ total_blocks = total_bytes / drivestat[i].blocksize;
+
+ blocks_per_second = interval_blocks / etime;
+ transfers_per_second = interval_transfers / etime;
+ mb_per_second = (interval_bytes / etime) / (1024 * 1024);
+
+ kb_per_transfer = (interval_transfers > 0) ?
+ ((long double)interval_bytes / interval_transfers)
+ / 1024 : 0;
+
+ /* times are in nanoseconds, convert to milliseconds */
+ ms_per_transaction = (interval_transfers > 0) ?
+ ((long double)interval_time / interval_transfers)
+ / 1000 : 0;
+
+ if (Kflag)
+ total_blocks = total_blocks * drivestat[i].blocksize
+ / 1024;
+
+ if (oflag > 0) {
+ int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
+
+ if (Iflag == 0)
+ printf("%4.0Lf%4.0Lf%5.*Lf ",
+ blocks_per_second,
+ transfers_per_second,
+ msdig,
+ ms_per_transaction);
+ else
+ printf("%4.1qu%4.1qu%5.*Lf ",
+ interval_blocks,
+ interval_transfers,
+ msdig,
+ ms_per_transaction);
+ } else {
+ if (Iflag == 0)
+ printf(" %7.2Lf %4.0Lf %5.2Lf ",
+ kb_per_transfer,
+ transfers_per_second,
+ mb_per_second);
+ else {
+ interval_mb = interval_bytes;
+ interval_mb /= 1024 * 1024;
+
+ printf(" %7.2Lf %3.1qu %5.2Lf ",
+ kb_per_transfer,
+ interval_transfers,
+ interval_mb);
+ }
+ }
+ }
+}
+
+static void
+cpustats(void)
+{
+ mach_msg_type_number_t count;
+ kern_return_t status;
+ double time;
+
+ /*
+ * Get CPU usage counters.
+ */
+ count = HOST_CPU_LOAD_INFO_COUNT;
+ status = host_statistics(host_priv_port, HOST_CPU_LOAD_INFO,
+ (host_info_t)&cur.load, &count);
+ if (status != KERN_SUCCESS)
+ errx(1, "couldn't fetch CPU stats");
+
+ /*
+ * Make 'cur' fields relative, update 'last' fields to current values,
+ * calculate total elapsed time.
+ */
+ time = 0.0;
+ cur.load.cpu_ticks[CPU_STATE_USER]
+ -= last.load.cpu_ticks[CPU_STATE_USER];
+ last.load.cpu_ticks[CPU_STATE_USER]
+ += cur.load.cpu_ticks[CPU_STATE_USER];
+ time += cur.load.cpu_ticks[CPU_STATE_USER];
+ cur.load.cpu_ticks[CPU_STATE_SYSTEM]
+ -= last.load.cpu_ticks[CPU_STATE_SYSTEM];
+ last.load.cpu_ticks[CPU_STATE_SYSTEM]
+ += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
+ time += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
+ cur.load.cpu_ticks[CPU_STATE_IDLE]
+ -= last.load.cpu_ticks[CPU_STATE_IDLE];
+ last.load.cpu_ticks[CPU_STATE_IDLE]
+ += cur.load.cpu_ticks[CPU_STATE_IDLE];
+ time += cur.load.cpu_ticks[CPU_STATE_IDLE];
+
+ /*
+ * Print times.
+ */
+#define PTIME(kind) { \
+ double cpu = rint(100. * cur.load.cpu_ticks[kind] / (time ? time : 1));\
+ printf("%*.0f", (100 == cpu) ? 4 : 3, cpu); \
+}
+ PTIME(CPU_STATE_USER);
+ PTIME(CPU_STATE_SYSTEM);
+ PTIME(CPU_STATE_IDLE);
+}
+
+static void
+loadstats(void)
+{
+ double loadavg[3];
+
+ if(getloadavg(loadavg,3)!=3)
+ errx(1, "couldn't fetch load average");
+
+ printf(" %4.2f %4.2f %4.2f",loadavg[0],loadavg[1],loadavg[2]);
+}
+
+static int
+readvar(const char *name, void *ptr, size_t len)
+{
+ int oid[4];
+ int oidlen;
+
+ size_t nlen = len;
+
+ if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
+ if (errno != ENOENT) {
+ warn("sysctl(%s) failed", name);
+ return (1);
+ }
+ /*
+ * XXX fallback code to deal with systems where
+ * sysctlbyname can't find "old" OIDs, should be removed.
+ */
+ if (!strcmp(name, "kern.boottime")) {
+ oid[0] = CTL_KERN;
+ oid[1] = KERN_BOOTTIME;
+ oidlen = 2;
+ } else {
+ warn("sysctl(%s) failed", name);
+ return (1);
+ }
+
+ nlen = len;
+ if (sysctl(oid, oidlen, ptr, &nlen, NULL, 0) == -1) {
+ warn("sysctl(%s) failed", name);
+ return (1);
+ }
+ }
+ if (nlen != len) {
+ warnx("sysctl(%s): expected %lu, got %lu", name,
+ (unsigned long)len, (unsigned long)nlen);
+ return (1);
+ }
+ return (0);
+}
+
+static 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);
+}
+
+/*
+ * Record all "whole" IOMedia objects as being interesting.
+ */
+static int
+record_all_devices(void)
+{
+ io_iterator_t drivelist;
+ CFMutableDictionaryRef match;
+ kern_return_t status;
+
+ /*
+ * Get an iterator for IOMedia objects.
+ */
+ match = IOServiceMatching("IOMedia");
+ CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
+
+ CFRetain(match);
+ status = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, match, &record_drivelist, NULL, &drivelist);
+
+ if (status != KERN_SUCCESS)
+ errx(1, "couldn't match whole IOMedia devices");
+
+ /*
+ * Scan all of the IOMedia objects, and for each
+ * object that has a parent IOBlockStorageDriver, save
+ * the object's name and the parent (from which we can
+ * fetch statistics).
+ *
+ * XXX What about RAID devices?
+ */
+
+ record_drivelist(NULL, drivelist);
+
+
+ status = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, match, &remove_drivelist, NULL, &drivelist);
+
+ if (status != KERN_SUCCESS)
+ errx(1, "couldn't match whole IOMedia device removal");
+
+ remove_drivelist(NULL, drivelist);
+
+ return(0);
+}
+
+static void
+record_drivelist(void* context, io_iterator_t drivelist)
+{
+ io_registry_entry_t drive;
+ while ((drive = IOIteratorNext(drivelist))) {
+ if (num_devices < MAXDRIVES) {
+ record_device(drive);
+ phdr_flag = 1;
+ }
+ IOObjectRelease(drive);
+ }
+ qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
+}
+
+static void
+remove_drivelist(void* context, io_iterator_t drivelist)
+{
+ io_registry_entry_t drive;
+ while ((drive = IOIteratorNext(drivelist))) {
+ kern_return_t status;
+ char bsdname[MAXDRIVENAME];
+ CFDictionaryRef properties;
+ CFStringRef name;
+
+ /* get drive properties */
+ status = IORegistryEntryCreateCFProperties(drive,
+ (CFMutableDictionaryRef *)&properties,
+ kCFAllocatorDefault,
+ kNilOptions);
+ if (status != KERN_SUCCESS) continue;
+
+ /* get name from properties */
+ name = (CFStringRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBSDNameKey));
+
+ if (name && CFStringGetCString(name, bsdname, MAXDRIVENAME, kCFStringEncodingUTF8)) {
+ int i;
+ for (i = 0; i < num_devices; ++i) {
+ if (strcmp(bsdname,drivestat[i].name) == 0) {
+ if (i < MAXDRIVES-1) {
+ memmove(&drivestat[i], &drivestat[i+1], sizeof(struct drivestats)*(MAXDRIVES-i));
+ }
+ --num_devices;
+ phdr_flag = 1;
+ qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
+ break;
+ }
+ }
+ }
+ CFRelease(properties);
+ IOObjectRelease(drive);
+ }
+}
+
+/*
+ * Try to record the named device as interesting. It
+ * must be an IOMedia device.
+ */
+static int
+record_one_device(char *name)
+{
+ io_iterator_t drivelist;
+ io_registry_entry_t drive;
+ kern_return_t status;
+
+ /*
+ * Find the device.
+ */
+ status = IOServiceGetMatchingServices(masterPort,
+ IOBSDNameMatching(masterPort, kNilOptions, name),
+ &drivelist);
+ if (status != KERN_SUCCESS)
+ errx(1, "couldn't match '%s'", name);
+
+ /*
+ * Get the first match (should only be one)
+ */
+ if (!(drive = IOIteratorNext(drivelist)))
+ errx(1, "'%s' not found", name);
+ if (!IOObjectConformsTo(drive, "IOMedia"))
+ errx(1, "'%s' is not a storage device", name);
+
+ /*
+ * Record the device.
+ */
+ if (record_device(drive))
+ errx(1, "could not record '%s' for monitoring", name);
+
+ IOObjectRelease(drive);
+ IOObjectRelease(drivelist);
+
+ return(0);
+}
+
+/*
+ * Determine whether an IORegistryEntry refers to a valid
+ * I/O device, and if so, record it.
+ */
+static int
+record_device(io_registry_entry_t drive)
+{
+ io_registry_entry_t parent;
+ CFDictionaryRef properties;
+ CFStringRef name;
+ CFNumberRef number;
+ kern_return_t status;
+
+ /* get drive's parent */
+ status = IORegistryEntryGetParentEntry(drive,
+ kIOServicePlane, &parent);
+ if (status != KERN_SUCCESS)
+ errx(1, "device has no parent");
+ if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
+ drivestat[num_devices].driver = parent;
+
+ /* get drive properties */
+ status = IORegistryEntryCreateCFProperties(drive,
+ (CFMutableDictionaryRef *)&properties,
+ kCFAllocatorDefault,
+ kNilOptions);
+ if (status != KERN_SUCCESS)
+ errx(1, "device has no properties");
+
+ /* get name from properties */
+ name = (CFStringRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOBSDNameKey));
+ if (name)
+ CFStringGetCString(name, drivestat[num_devices].name,
+ MAXDRIVENAME, kCFStringEncodingUTF8);
+ else {
+ errx(1, "device does not have a BSD name");
+ }
+
+ /* get blocksize from properties */
+ number = (CFNumberRef)CFDictionaryGetValue(properties,
+ CFSTR(kIOMediaPreferredBlockSizeKey));
+ if (number)
+ CFNumberGetValue(number, kCFNumberSInt64Type,
+ &drivestat[num_devices].blocksize);
+ else
+ errx(1, "device does not have a preferred block size");
+
+ // radar:18700383
+ if (drivestat[num_devices].blocksize == 0) {
+ warnx("%s claims a blocksize of 0; defaulting to 512. Its statistics may be inaccurate.", drivestat[num_devices].name);
+ drivestat[num_devices].blocksize = 512;
+ }
+
+ /* clean up, return success */
+ CFRelease(properties);
+ num_devices++;
+ return(0);
+ }
+
+ /* failed, don't keep parent */
+ IOObjectRelease(parent);
+ return(1);
+}
+
+static int
+compare_drivestats(const void* pa, const void* pb)
+{
+ struct drivestats* a = (struct drivestats*)pa;
+ struct drivestats* b = (struct drivestats*)pb;
+ return strcmp(a->name, b->name);
+}