]> git.saurik.com Git - apple/system_cmds.git/blobdiff - iostat.tproj/iostat.c
system_cmds-735.20.1.tar.gz
[apple/system_cmds.git] / iostat.tproj / iostat.c
index da5fe41014f589f330e8a525b29e8416c6c0d9aa..58f62a27325fd40ad7384f703c5d716047ed8168 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2016 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
@@ -10,7 +10,7 @@
  * 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,
@@ -18,7 +18,7 @@
  * 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>
@@ -169,8 +169,9 @@ static mach_port_t masterPort;
 
 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);
@@ -178,13 +179,18 @@ static void phdr(int signo);
 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
@@ -196,7 +202,7 @@ usage(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");
 }
 
@@ -210,9 +216,11 @@ main(int argc, char **argv)
        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++;
@@ -245,6 +253,9 @@ main(int argc, char **argv)
                        case 'T':
                                Tflag++;
                                break;
+                       case 'U':
+                               Uflag++;
+                               break;
                        case 'w':
                                wflag++;
                                waittime = atoi(optarg);
@@ -271,15 +282,25 @@ main(int argc, char **argv)
         */
        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.
@@ -294,7 +315,7 @@ main(int argc, char **argv)
                                maxshowdevs = 4;
                } else {
                        if ((dflag > 0) && (Cflag == 0))
-                               maxshowdevs = 4;                
+                               maxshowdevs = 4;
                        else
                                maxshowdevs = 3;
                }
@@ -308,7 +329,7 @@ main(int argc, char **argv)
                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)
@@ -325,7 +346,7 @@ main(int argc, char **argv)
                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++;
@@ -386,14 +407,10 @@ main(int argc, char **argv)
                        }
                 }
 
-               if (phdr_flag) {
+               if (!--headercount || phdr_flag) {
                        phdr_flag = 0;
-                       do_phdr();
-               }
-               
-               if (!--headercount) {
-                       do_phdr();
                        headercount = 20;
+                       do_phdr();
                }
 
                last_time = cur_time;
@@ -414,7 +431,7 @@ main(int argc, char **argv)
                        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);
@@ -422,13 +439,21 @@ main(int argc, char **argv)
                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;
        }
 
@@ -439,33 +464,36 @@ static void
 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 ");
@@ -473,14 +501,17 @@ do_phdr()
                                (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");
 }
@@ -502,7 +533,7 @@ devstats(int perf_select, long double etime, int havelast)
        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
@@ -518,7 +549,7 @@ devstats(int perf_select, long double etime, int havelast)
                        kCFAllocatorDefault,
                        kNilOptions);
                if (status != KERN_SUCCESS)
-                       err(1, "device has no properties");
+                       continue;
 
                /* get statistics from properties */
                statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
@@ -574,7 +605,7 @@ devstats(int perf_select, long double etime, int havelast)
                 * 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;
 
@@ -583,7 +614,7 @@ devstats(int perf_select, long double etime, int havelast)
                        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;
@@ -593,16 +624,16 @@ devstats(int perf_select, long double etime, int havelast)
                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) {
@@ -614,7 +645,7 @@ devstats(int perf_select, long double etime, int havelast)
                                       transfers_per_second,
                                       msdig,
                                       ms_per_transaction);
-                       else 
+                       else
                                printf("%4.1qu%4.1qu%5.*Lf ",
                                       interval_blocks,
                                       interval_transfers,
@@ -622,7 +653,7 @@ devstats(int perf_select, long double etime, int havelast)
                                       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);
@@ -630,7 +661,7 @@ devstats(int perf_select, long double etime, int havelast)
                                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);
@@ -675,19 +706,28 @@ cpustats(void)
        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
@@ -739,8 +779,8 @@ compute_etime(struct timeval cur_time, struct timeval prev_time)
 
        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;
@@ -755,9 +795,7 @@ static int
 record_all_devices(void)
 {
        io_iterator_t drivelist;
-       io_registry_entry_t drive;
        CFMutableDictionaryRef match;
-       int error, ndrives;
        kern_return_t status;
 
        /*
@@ -765,7 +803,10 @@ record_all_devices(void)
         */
        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");
 
@@ -777,19 +818,72 @@ record_all_devices(void)
         *
         * 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);
+       }
 }
 
 /*
@@ -815,7 +909,7 @@ record_one_device(char *name)
        /*
         * 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);
@@ -844,7 +938,7 @@ record_device(io_registry_entry_t drive)
        CFStringRef name;
        CFNumberRef number;
        kern_return_t status;
-       
+
        /* get drive's parent */
        status = IORegistryEntryGetParentEntry(drive,
                kIOServicePlane, &parent);
@@ -864,14 +958,27 @@ record_device(io_registry_entry_t drive)
                /* 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);
@@ -883,3 +990,11 @@ record_device(io_registry_entry_t drive)
        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);
+}