X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/d904471cf60febf9484bc16c29e58e92baf2b9cc..b58caf92d598c70ddd398b3909b0a2b8b5a110e1:/iostat.tproj/iostat.c?ds=sidebyside diff --git a/iostat.tproj/iostat.c b/iostat.tproj/iostat.c index da5fe41..58f62a2 100644 --- a/iostat.tproj/iostat.c +++ b/iostat.tproj/iostat.c @@ -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@ */ /* @@ -132,7 +132,7 @@ #include #include #include - +#include /* host_statistics */ #include #include #include @@ -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); +}