/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
- * Copyright (c) 1999-2003 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 2.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.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
+ *
+ * "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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
+ * 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@
*/
/*
#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>
static int num_devices;
static int maxshowdevs;
-static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
+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 do_phdr();
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 long double compute_etime(struct timeval cur_time,
+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
* This isn't mentioned in the man page, or the usage statement,
* but it is supported.
*/
- fprintf(stderr, "usage: iostat [-CdIKoT?] [-c count] [-n devs]\n"
+ fprintf(stderr, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
"\t [-w wait] [drives]\n");
}
int num_devices_specified;
int havelast = 0;
+ CFRunLoopSourceRef rls;
+
maxshowdevs = 3;
- while ((c = getopt(argc, argv, "c:CdIKM:n:oTw:?")) != -1) {
+ while ((c = getopt(argc, argv, "c:CdIKM:n:oTUw:?")) != -1) {
switch(c) {
case 'c':
cflag++;
case 'T':
Tflag++;
break;
+ case 'U':
+ Uflag++;
+ break;
case 'w':
wflag++;
waittime = atoi(optarg);
*/
IOMasterPort(bootstrap_port, &masterPort);
+ notifyPort = IONotificationPortCreate(masterPort);
+ rls = IONotificationPortGetRunLoopSource(notifyPort);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+
/*
- * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is
+ * 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.
maxshowdevs = 4;
} else {
if ((dflag > 0) && (Cflag == 0))
- maxshowdevs = 4;
+ maxshowdevs = 4;
else
maxshowdevs = 3;
}
if (isdigit(**argv))
break;
if (record_one_device(*argv))
- errx(1, "can't record '%s' for monitoring");
+ errx(1, "can't record '%s' for monitoring", *argv);
num_devices_specified++;
}
if (nflag == 0 && maxshowdevs < num_devices_specified)
waittime = atoi(*argv);
/* Let the user know he goofed, but keep going anyway */
- if (wflag != 0)
+ if (wflag != 0)
warnx("discarding previous wait interval, using"
" %d instead", waittime);
wflag++;
}
}
- if (phdr_flag) {
+ if (!--headercount || phdr_flag) {
phdr_flag = 0;
- do_phdr();
- }
-
- if (!--headercount) {
- do_phdr();
headercount = 20;
+ do_phdr();
}
last_time = cur_time;
etime = 1.0;
if (Tflag > 0)
- printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ 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;
- sleep(waittime);
+ /*
+ * Instead of sleep(waittime), wait in
+ * the RunLoop for IONotifications.
+ */
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)waittime, 1);
+
havelast = 1;
}
phdr(int signo)
{
- phdr_flag = 1;
+ phdr_flag = 1;
}
static void
-do_phdr()
+do_phdr(void)
{
register int i;
if (Tflag > 0)
(void)printf(" tty");
- for (i = 0; (i < num_devices); i++){
+ for (i = 0; i < num_devices && i < maxshowdevs; i++){
if (oflag > 0)
(void)printf("%12.6s ", drivestat[i].name);
else
- printf("%15.6s ", drivestat[i].name);
+ printf("%19.6s ", drivestat[i].name);
}
-
+
if (Cflag > 0)
- (void)printf(" cpu\n");
+ (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++){
+ for (i=0; i < num_devices && i < maxshowdevs; i++){
if (oflag > 0) {
if (Iflag == 0)
(void)printf(" sps tps msps ");
(void)printf(" blk xfr msps ");
} else {
if (Iflag == 0)
- printf(" KB/t tps MB/s ");
+ printf(" KB/t tps MB/s ");
else
- printf(" KB/t xfrs MB ");
+ printf(" KB/t xfrs MB ");
}
}
if (Cflag > 0)
- (void)printf(" us sy id\n");
+ (void)printf(" us sy id");
+
+ if (Uflag > 0)
+ (void)printf(" 1m 5m 15m\n");
else
printf("\n");
}
kern_return_t status;
int i;
- for (i = 0; i < num_devices; i++) {
+ for (i = 0; i < num_devices && i < maxshowdevs; i++) {
/*
* If the drive goes away, we may not get any properties
kCFAllocatorDefault,
kNilOptions);
if (status != KERN_SUCCESS)
- err(1, "device has no properties");
+ continue;
/* get statistics from properties */
statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
* Compute delta values and stats.
*/
interval_bytes = total_bytes - drivestat[i].total_bytes;
- interval_transfers = total_transfers
+ interval_transfers = total_transfers
- drivestat[i].total_transfers;
interval_time = total_time - drivestat[i].total_time;
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;
mb_per_second = (interval_bytes / etime) / (1024 * 1024);
kb_per_transfer = (interval_transfers > 0) ?
- ((long double)interval_bytes / interval_transfers)
+ ((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)
+ ((long double)interval_time / interval_transfers)
/ 1000 : 0;
if (Kflag)
- total_blocks = total_blocks * drivestat[i].blocksize
+ total_blocks = total_blocks * drivestat[i].blocksize
/ 1024;
if (oflag > 0) {
transfers_per_second,
msdig,
ms_per_transaction);
- else
+ else
printf("%4.1qu%4.1qu%5.*Lf ",
interval_blocks,
interval_transfers,
ms_per_transaction);
} else {
if (Iflag == 0)
- printf(" %5.2Lf %3.0Lf %5.2Lf ",
+ printf(" %7.2Lf %4.0Lf %5.2Lf ",
kb_per_transfer,
transfers_per_second,
mb_per_second);
interval_mb = interval_bytes;
interval_mb /= 1024 * 1024;
- printf(" %5.2Lf %3.1qu %5.2Lf ",
+ printf(" %7.2Lf %3.1qu %5.2Lf ",
kb_per_transfer,
interval_transfers,
interval_mb);
last.load.cpu_ticks[CPU_STATE_IDLE]
+= cur.load.cpu_ticks[CPU_STATE_IDLE];
time += cur.load.cpu_ticks[CPU_STATE_IDLE];
-
+
/*
* Print times.
*/
- printf("%3.0f",
- rint(100. * cur.load.cpu_ticks[CPU_STATE_USER]
- / (time ? time : 1)));
- printf("%3.0f",
- rint(100. * cur.load.cpu_ticks[CPU_STATE_SYSTEM]
- / (time ? time : 1)));
- printf("%3.0f",
- rint(100. * cur.load.cpu_ticks[CPU_STATE_IDLE]
- / (time ? time : 1)));
+#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
timersub(&cur_time, &prev_time, &busy_time);
- busy_usec = busy_time.tv_sec;
- busy_usec *= 1000000;
+ busy_usec = busy_time.tv_sec;
+ busy_usec *= 1000000;
busy_usec += busy_time.tv_usec;
etime = busy_usec;
etime /= 1000000;
record_all_devices(void)
{
io_iterator_t drivelist;
- io_registry_entry_t drive;
CFMutableDictionaryRef match;
- int error, ndrives;
kern_return_t status;
/*
*/
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
- status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
+
+ CFRetain(match);
+ status = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, match, &record_drivelist, NULL, &drivelist);
+
if (status != KERN_SUCCESS)
errx(1, "couldn't match whole IOMedia devices");
*
* XXX What about RAID devices?
*/
- error = 1;
- ndrives = 0;
- while ((drive = IOIteratorNext(drivelist))
- && (ndrives < maxshowdevs)) {
- if (!record_device(drive)) {
- error = 0;
- ndrives++;
+
+ 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);
}
- IOObjectRelease(drivelist);
+ 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;
- return(error);
+ /* 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);
+ }
}
/*
/*
* Get the first match (should only be one)
*/
- if ((drive = IOIteratorNext(drivelist)) == NULL)
+ if (!(drive = IOIteratorNext(drivelist)))
errx(1, "'%s' not found", name);
if (!IOObjectConformsTo(drive, "IOMedia"))
errx(1, "'%s' is not a storage device", name);
CFStringRef name;
CFNumberRef number;
kern_return_t status;
-
+
/* get drive's parent */
status = IORegistryEntryGetParentEntry(drive,
kIOServicePlane, &parent);
/* get name from properties */
name = (CFStringRef)CFDictionaryGetValue(properties,
CFSTR(kIOBSDNameKey));
- CFStringGetCString(name, drivestat[num_devices].name,
- MAXDRIVENAME, CFStringGetSystemEncoding());
+ 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));
- CFNumberGetValue(number, kCFNumberSInt64Type,
- &drivestat[num_devices].blocksize);
+ 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);
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);
+}