]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/iostat.tproj/iostat.c
Merge branch 'apple'
[apple_cmds.git] / system_cmds / iostat.tproj / iostat.c
1 /*
2 * Copyright (c) 1999-2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*-
25 * SPDX-License-Identifier: BSD-3-Clause
26 *
27 * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. The name of the author may not be used to endorse or promote products
39 * derived from this software without specific prior written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 * $FreeBSD$
54 */
55 /*
56 * Parts of this program are derived from the original FreeBSD iostat
57 * program:
58 */
59 /*-
60 * Copyright (c) 1986, 1991, 1993
61 * The Regents of the University of California. All rights reserved.
62 *
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
65 * are met:
66 * 1. Redistributions of source code must retain the above copyright
67 * notice, this list of conditions and the following disclaimer.
68 * 2. Redistributions in binary form must reproduce the above copyright
69 * notice, this list of conditions and the following disclaimer in the
70 * documentation and/or other materials provided with the distribution.
71 * 4. Neither the name of the University nor the names of its contributors
72 * may be used to endorse or promote products derived from this software
73 * without specific prior written permission.
74 *
75 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
76 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
77 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
78 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
79 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
80 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
81 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
82 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
83 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
84 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
85 * SUCH DAMAGE.
86 */
87 /*
88 * Ideas for the new iostat statistics output modes taken from the NetBSD
89 * version of iostat:
90 */
91 /*
92 * Copyright (c) 1996 John M. Vinopal
93 * All rights reserved.
94 *
95 * Redistribution and use in source and binary forms, with or without
96 * modification, are permitted provided that the following conditions
97 * are met:
98 * 1. Redistributions of source code must retain the above copyright
99 * notice, this list of conditions and the following disclaimer.
100 * 2. Redistributions in binary form must reproduce the above copyright
101 * notice, this list of conditions and the following disclaimer in the
102 * documentation and/or other materials provided with the distribution.
103 * 3. All advertising materials mentioning features or use of this software
104 * must display the following acknowledgement:
105 * This product includes software developed for the NetBSD Project
106 * by John M. Vinopal.
107 * 4. The name of the author may not be used to endorse or promote products
108 * derived from this software without specific prior written permission.
109 *
110 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
111 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
112 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
113 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
114 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
115 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
116 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
117 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
118 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
119 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
120 * SUCH DAMAGE.
121 */
122
123 #define IOKIT 1 /* to get io_name_t in device_types.h */
124
125 #include <sys/param.h>
126 #include <sys/sysctl.h>
127
128 #include <CoreFoundation/CoreFoundation.h>
129 #include <IOKit/IOKitLib.h>
130 #include <IOKit/storage/IOBlockStorageDriver.h>
131 #include <IOKit/storage/IOMedia.h>
132 #include <IOKit/IOBSD.h>
133 #include <mach/mach_host.h> /* host_statistics */
134 #include <err.h>
135 #include <signal.h>
136 #include <stdio.h>
137 #include <stdlib.h>
138 #include <string.h>
139 #include <unistd.h>
140
141 #define MAXDRIVES 16 /* most drives we will record */
142 #define MAXDRIVENAME 31 /* largest drive name we allow */
143
144 struct drivestats {
145 io_registry_entry_t driver;
146 char name[MAXDRIVENAME + 1];
147 u_int64_t blocksize;
148 u_int64_t total_bytes;
149 u_int64_t total_transfers;
150 u_int64_t total_time;
151 };
152
153 static struct drivestats drivestat[MAXDRIVES];
154
155 static struct timeval cur_time, last_time;
156
157 struct statinfo {
158 long tk_nin;
159 long tk_nout;
160 host_cpu_load_info_data_t load;
161 };
162
163 static struct statinfo cur, last;
164
165 static mach_port_t host_priv_port;
166 static mach_port_t masterPort;
167
168 static int num_devices;
169 static int maxshowdevs;
170 static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Uflag = 0, Kflag = 0;
171 static volatile sig_atomic_t phdr_flag = 0;
172 static IONotificationPortRef notifyPort;
173
174 /* local function declarations */
175 static void usage(void);
176 static void phdr(int signo);
177 static void do_phdr(void);
178 static void devstats(int perf_select, long double etime, int havelast);
179 static void cpustats(void);
180 static void loadstats(void);
181 static int readvar(const char *name, void *ptr, size_t len);
182
183 static int record_all_devices(void);
184 static void record_drivelist(void* context, io_iterator_t drivelist);
185 static void remove_drivelist(void* context, io_iterator_t drivelist);
186 static int record_one_device(char *name);
187 static int record_device(io_registry_entry_t drive);
188
189 static int compare_drivestats(const void* pa, const void* pb);
190
191 static long double compute_etime(struct timeval cur_time,
192 struct timeval prev_time);
193
194 static void
195 usage(void)
196 {
197 /*
198 * We also support the following 'traditional' syntax:
199 * iostat [drives] [wait [count]]
200 * This isn't mentioned in the man page, or the usage statement,
201 * but it is supported.
202 */
203 fprintf(stderr, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
204 "\t [-w wait] [drives]\n");
205 }
206
207 int
208 main(int argc, char **argv)
209 {
210 int c;
211 int hflag = 0, cflag = 0, wflag = 0, nflag = 0;
212 int count = 0, waittime = 0;
213 int headercount;
214 int num_devices_specified;
215 int havelast = 0;
216
217 CFRunLoopSourceRef rls;
218
219 maxshowdevs = 3;
220
221 while ((c = getopt(argc, argv, "c:CdIKM:n:oTUw:?")) != -1) {
222 switch(c) {
223 case 'c':
224 cflag++;
225 count = atoi(optarg);
226 if (count < 1)
227 errx(1, "count %d is < 1", count);
228 break;
229 case 'C':
230 Cflag++;
231 break;
232 case 'd':
233 dflag++;
234 break;
235 case 'I':
236 Iflag++;
237 break;
238 case 'K':
239 Kflag++;
240 break;
241 case 'n':
242 nflag++;
243 maxshowdevs = atoi(optarg);
244 if (maxshowdevs < 0)
245 errx(1, "number of devices %d is < 0",
246 maxshowdevs);
247 break;
248 case 'o':
249 oflag++;
250 break;
251 case 'T':
252 Tflag++;
253 break;
254 case 'U':
255 Uflag++;
256 break;
257 case 'w':
258 wflag++;
259 waittime = atoi(optarg);
260 if (waittime < 1)
261 errx(1, "wait time is < 1");
262 break;
263 default:
264 usage();
265 exit(1);
266 break;
267 }
268 }
269
270 argc -= optind;
271 argv += optind;
272
273 /*
274 * Get the Mach private port.
275 */
276 host_priv_port = mach_host_self();
277
278 /*
279 * Get the I/O Kit communication handle.
280 */
281 IOMasterPort(bootstrap_port, &masterPort);
282
283 notifyPort = IONotificationPortCreate(masterPort);
284 rls = IONotificationPortGetRunLoopSource(notifyPort);
285 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
286
287 /*
288 * Make sure Tflag, Cflag and Uflag are set if dflag == 0. If dflag is
289 * greater than 0, they may be 0 or non-zero.
290 */
291 if (dflag == 0) {
292 Cflag = 1;
293 Tflag = 1;
294 Uflag = 1;
295 }
296
297 /*
298 * TTY statistics are broken, disabling them.
299 */
300 Tflag = 0;
301
302 /*
303 * Figure out how many devices we should display if not given
304 * an explicit value.
305 */
306 if (nflag == 0) {
307 if (oflag > 0) {
308 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
309 maxshowdevs = 5;
310 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
311 maxshowdevs = 5;
312 else
313 maxshowdevs = 4;
314 } else {
315 if ((dflag > 0) && (Cflag == 0))
316 maxshowdevs = 4;
317 else
318 maxshowdevs = 3;
319 }
320 }
321
322 /*
323 * If the user specified any devices on the command line, record
324 * them for monitoring.
325 */
326 for (num_devices_specified = 0; *argv; ++argv) {
327 if (isdigit(**argv))
328 break;
329 if (record_one_device(*argv))
330 errx(1, "can't record '%s' for monitoring", *argv);
331 num_devices_specified++;
332 }
333 if (nflag == 0 && maxshowdevs < num_devices_specified)
334 maxshowdevs = num_devices_specified;
335
336 /* if no devices were specified, pick them ourselves */
337 if ((num_devices_specified == 0) && record_all_devices())
338 err(1, "can't find any devices to display");
339
340 /*
341 * Look for the traditional wait time and count arguments.
342 */
343 if (*argv) {
344 waittime = atoi(*argv);
345
346 /* Let the user know he goofed, but keep going anyway */
347 if (wflag != 0)
348 warnx("discarding previous wait interval, using"
349 " %d instead", waittime);
350 wflag++;
351
352 if (*++argv) {
353 count = atoi(*argv);
354 if (cflag != 0)
355 warnx("discarding previous count, using %d"
356 " instead", count);
357 cflag++;
358 } else
359 count = -1;
360 }
361
362 /*
363 * If the user specified a count, but not an interval, we default
364 * to an interval of 1 second.
365 */
366 if ((wflag == 0) && (cflag > 0))
367 waittime = 1;
368
369 /*
370 * If the user specified a wait time, but not a count, we want to
371 * go on ad infinitum. This can be redundant if the user uses the
372 * traditional method of specifying the wait, since in that case we
373 * already set count = -1 above. Oh well.
374 */
375 if ((wflag > 0) && (cflag == 0))
376 count = -1;
377
378 cur.tk_nout = 0;
379 cur.tk_nin = 0;
380
381 /*
382 * Set the busy time to the system boot time, so the stats are
383 * calculated since system boot.
384 */
385 if (readvar("kern.boottime", &cur_time, sizeof(cur_time)) != 0)
386 exit(1);
387
388 /*
389 * If the user stops the program (control-Z) and then resumes it,
390 * print out the header again.
391 */
392 (void)signal(SIGCONT, phdr);
393
394 for (headercount = 1;;) {
395 long tmp;
396 long double etime;
397
398 if (Tflag > 0) {
399 if ((readvar("kern.tty_nin", &cur.tk_nin,
400 sizeof(cur.tk_nin)) != 0)
401 || (readvar("kern.tty_nout",
402 &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
403 Tflag = 0;
404 warnx("disabling TTY statistics");
405 }
406 }
407
408 if (!--headercount || phdr_flag) {
409 phdr_flag = 0;
410 headercount = 20;
411 do_phdr();
412 }
413
414 last_time = cur_time;
415 gettimeofday(&cur_time, NULL);
416
417 if (Tflag > 0) {
418 tmp = cur.tk_nin;
419 cur.tk_nin -= last.tk_nin;
420 last.tk_nin = tmp;
421 tmp = cur.tk_nout;
422 cur.tk_nout -= last.tk_nout;
423 last.tk_nout = tmp;
424 }
425
426 etime = compute_etime(cur_time, last_time);
427
428 if (etime == 0.0)
429 etime = 1.0;
430
431 if (Tflag > 0)
432 printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
433 cur.tk_nout / etime);
434
435 devstats(hflag, etime, havelast);
436
437 if (Cflag > 0)
438 cpustats();
439
440 if (Uflag > 0)
441 loadstats();
442
443 printf("\n");
444 fflush(stdout);
445
446 if (count >= 0 && --count <= 0)
447 break;
448
449 /*
450 * Instead of sleep(waittime), wait in
451 * the RunLoop for IONotifications.
452 */
453 CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)waittime, 1);
454
455 havelast = 1;
456 }
457
458 exit(0);
459 }
460
461 static void
462 phdr(int signo)
463 {
464
465 phdr_flag = 1;
466 }
467
468 static void
469 do_phdr(void)
470 {
471 register int i;
472
473 if (Tflag > 0)
474 (void)printf(" tty");
475
476 for (i = 0; i < num_devices && i < maxshowdevs; i++){
477 if (oflag > 0)
478 (void)printf("%12.6s ", drivestat[i].name);
479 else
480 printf("%19.6s ", drivestat[i].name);
481 }
482
483 if (Cflag > 0)
484 (void)printf(" cpu");
485
486 if (Uflag > 0)
487 (void)printf(" load average\n");
488 else
489 (void)printf("\n");
490
491 if (Tflag > 0)
492 (void)printf(" tin tout");
493
494 for (i=0; i < num_devices && i < maxshowdevs; i++){
495 if (oflag > 0) {
496 if (Iflag == 0)
497 (void)printf(" sps tps msps ");
498 else
499 (void)printf(" blk xfr msps ");
500 } else {
501 if (Iflag == 0)
502 printf(" KB/t tps MB/s ");
503 else
504 printf(" KB/t xfrs MB ");
505 }
506 }
507
508 if (Cflag > 0)
509 (void)printf(" us sy id");
510
511 if (Uflag > 0)
512 (void)printf(" 1m 5m 15m\n");
513 else
514 printf("\n");
515 }
516
517 static void
518 devstats(int perf_select, long double etime, int havelast)
519 {
520 CFNumberRef number;
521 CFDictionaryRef properties;
522 CFDictionaryRef statistics;
523 long double transfers_per_second;
524 long double kb_per_transfer, mb_per_second;
525 u_int64_t value;
526 u_int64_t total_bytes, total_transfers, total_blocks, total_time;
527 u_int64_t interval_bytes, interval_transfers, interval_blocks;
528 u_int64_t interval_time;
529 long double interval_mb;
530 long double blocks_per_second, ms_per_transaction;
531 kern_return_t status;
532 int i;
533
534 for (i = 0; i < num_devices && i < maxshowdevs; i++) {
535
536 /*
537 * If the drive goes away, we may not get any properties
538 * for it. So take some defaults.
539 */
540 total_bytes = 0;
541 total_transfers = 0;
542 total_time = 0;
543
544 /* get drive properties */
545 status = IORegistryEntryCreateCFProperties(drivestat[i].driver,
546 (CFMutableDictionaryRef *)&properties,
547 kCFAllocatorDefault,
548 kNilOptions);
549 if (status != KERN_SUCCESS)
550 continue;
551
552 /* get statistics from properties */
553 statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
554 CFSTR(kIOBlockStorageDriverStatisticsKey));
555 if (statistics) {
556
557 /*
558 * Get I/O volume.
559 */
560 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
561 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
562 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
563 total_bytes += value;
564 }
565 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
566 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
567 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
568 total_bytes += value;
569 }
570
571 /*
572 * Get I/O counts.
573 */
574 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
575 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
576 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
577 total_transfers += value;
578 }
579 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
580 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
581 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
582 total_transfers += value;
583 }
584
585 /*
586 * Get I/O time.
587 */
588 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
589 CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
590 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
591 total_time += value;
592 }
593 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
594 CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
595 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
596 total_time += value;
597 }
598
599 }
600 CFRelease(properties);
601
602 /*
603 * Compute delta values and stats.
604 */
605 interval_bytes = total_bytes - drivestat[i].total_bytes;
606 interval_transfers = total_transfers
607 - drivestat[i].total_transfers;
608 interval_time = total_time - drivestat[i].total_time;
609
610 /* update running totals, only once for -I */
611 if ((Iflag == 0) || (drivestat[i].total_bytes == 0)) {
612 drivestat[i].total_bytes = total_bytes;
613 drivestat[i].total_transfers = total_transfers;
614 drivestat[i].total_time = total_time;
615 }
616
617 interval_blocks = interval_bytes / drivestat[i].blocksize;
618 total_blocks = total_bytes / drivestat[i].blocksize;
619
620 blocks_per_second = interval_blocks / etime;
621 transfers_per_second = interval_transfers / etime;
622 mb_per_second = (interval_bytes / etime) / (1024 * 1024);
623
624 kb_per_transfer = (interval_transfers > 0) ?
625 ((long double)interval_bytes / interval_transfers)
626 / 1024 : 0;
627
628 /* times are in nanoseconds, convert to milliseconds */
629 ms_per_transaction = (interval_transfers > 0) ?
630 ((long double)interval_time / interval_transfers)
631 / 1000 : 0;
632
633 if (Kflag)
634 total_blocks = total_blocks * drivestat[i].blocksize
635 / 1024;
636
637 if (oflag > 0) {
638 int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
639
640 if (Iflag == 0)
641 printf("%4.0Lf%4.0Lf%5.*Lf ",
642 blocks_per_second,
643 transfers_per_second,
644 msdig,
645 ms_per_transaction);
646 else
647 printf("%4.1qu%4.1qu%5.*Lf ",
648 interval_blocks,
649 interval_transfers,
650 msdig,
651 ms_per_transaction);
652 } else {
653 if (Iflag == 0)
654 printf(" %7.2Lf %4.0Lf %5.2Lf ",
655 kb_per_transfer,
656 transfers_per_second,
657 mb_per_second);
658 else {
659 interval_mb = interval_bytes;
660 interval_mb /= 1024 * 1024;
661
662 printf(" %7.2Lf %3.1qu %5.2Lf ",
663 kb_per_transfer,
664 interval_transfers,
665 interval_mb);
666 }
667 }
668 }
669 }
670
671 static void
672 cpustats(void)
673 {
674 mach_msg_type_number_t count;
675 kern_return_t status;
676 double time;
677
678 /*
679 * Get CPU usage counters.
680 */
681 count = HOST_CPU_LOAD_INFO_COUNT;
682 status = host_statistics(host_priv_port, HOST_CPU_LOAD_INFO,
683 (host_info_t)&cur.load, &count);
684 if (status != KERN_SUCCESS)
685 errx(1, "couldn't fetch CPU stats");
686
687 /*
688 * Make 'cur' fields relative, update 'last' fields to current values,
689 * calculate total elapsed time.
690 */
691 time = 0.0;
692 cur.load.cpu_ticks[CPU_STATE_USER]
693 -= last.load.cpu_ticks[CPU_STATE_USER];
694 last.load.cpu_ticks[CPU_STATE_USER]
695 += cur.load.cpu_ticks[CPU_STATE_USER];
696 time += cur.load.cpu_ticks[CPU_STATE_USER];
697 cur.load.cpu_ticks[CPU_STATE_SYSTEM]
698 -= last.load.cpu_ticks[CPU_STATE_SYSTEM];
699 last.load.cpu_ticks[CPU_STATE_SYSTEM]
700 += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
701 time += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
702 cur.load.cpu_ticks[CPU_STATE_IDLE]
703 -= last.load.cpu_ticks[CPU_STATE_IDLE];
704 last.load.cpu_ticks[CPU_STATE_IDLE]
705 += cur.load.cpu_ticks[CPU_STATE_IDLE];
706 time += cur.load.cpu_ticks[CPU_STATE_IDLE];
707
708 /*
709 * Print times.
710 */
711 #define PTIME(kind) { \
712 double cpu = rint(100. * cur.load.cpu_ticks[kind] / (time ? time : 1));\
713 printf("%*.0f", (100 == cpu) ? 4 : 3, cpu); \
714 }
715 PTIME(CPU_STATE_USER);
716 PTIME(CPU_STATE_SYSTEM);
717 PTIME(CPU_STATE_IDLE);
718 }
719
720 static void
721 loadstats(void)
722 {
723 double loadavg[3];
724
725 if(getloadavg(loadavg,3)!=3)
726 errx(1, "couldn't fetch load average");
727
728 printf(" %4.2f %4.2f %4.2f",loadavg[0],loadavg[1],loadavg[2]);
729 }
730
731 static int
732 readvar(const char *name, void *ptr, size_t len)
733 {
734 int oid[4];
735 int oidlen;
736
737 size_t nlen = len;
738
739 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
740 if (errno != ENOENT) {
741 warn("sysctl(%s) failed", name);
742 return (1);
743 }
744 /*
745 * XXX fallback code to deal with systems where
746 * sysctlbyname can't find "old" OIDs, should be removed.
747 */
748 if (!strcmp(name, "kern.boottime")) {
749 oid[0] = CTL_KERN;
750 oid[1] = KERN_BOOTTIME;
751 oidlen = 2;
752 } else {
753 warn("sysctl(%s) failed", name);
754 return (1);
755 }
756
757 nlen = len;
758 if (sysctl(oid, oidlen, ptr, &nlen, NULL, 0) == -1) {
759 warn("sysctl(%s) failed", name);
760 return (1);
761 }
762 }
763 if (nlen != len) {
764 warnx("sysctl(%s): expected %lu, got %lu", name,
765 (unsigned long)len, (unsigned long)nlen);
766 return (1);
767 }
768 return (0);
769 }
770
771 static long double
772 compute_etime(struct timeval cur_time, struct timeval prev_time)
773 {
774 struct timeval busy_time;
775 u_int64_t busy_usec;
776 long double etime;
777
778 timersub(&cur_time, &prev_time, &busy_time);
779
780 busy_usec = busy_time.tv_sec;
781 busy_usec *= 1000000;
782 busy_usec += busy_time.tv_usec;
783 etime = busy_usec;
784 etime /= 1000000;
785
786 return(etime);
787 }
788
789 /*
790 * Record all "whole" IOMedia objects as being interesting.
791 */
792 static int
793 record_all_devices(void)
794 {
795 io_iterator_t drivelist;
796 CFMutableDictionaryRef match;
797 kern_return_t status;
798
799 /*
800 * Get an iterator for IOMedia objects.
801 */
802 match = IOServiceMatching("IOMedia");
803 CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
804
805 CFRetain(match);
806 status = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, match, &record_drivelist, NULL, &drivelist);
807
808 if (status != KERN_SUCCESS)
809 errx(1, "couldn't match whole IOMedia devices");
810
811 /*
812 * Scan all of the IOMedia objects, and for each
813 * object that has a parent IOBlockStorageDriver, save
814 * the object's name and the parent (from which we can
815 * fetch statistics).
816 *
817 * XXX What about RAID devices?
818 */
819
820 record_drivelist(NULL, drivelist);
821
822
823 status = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, match, &remove_drivelist, NULL, &drivelist);
824
825 if (status != KERN_SUCCESS)
826 errx(1, "couldn't match whole IOMedia device removal");
827
828 remove_drivelist(NULL, drivelist);
829
830 return(0);
831 }
832
833 static void
834 record_drivelist(void* context, io_iterator_t drivelist)
835 {
836 io_registry_entry_t drive;
837 while ((drive = IOIteratorNext(drivelist))) {
838 if (num_devices < MAXDRIVES) {
839 record_device(drive);
840 phdr_flag = 1;
841 }
842 IOObjectRelease(drive);
843 }
844 qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
845 }
846
847 static void
848 remove_drivelist(void* context, io_iterator_t drivelist)
849 {
850 io_registry_entry_t drive;
851 while ((drive = IOIteratorNext(drivelist))) {
852 kern_return_t status;
853 char bsdname[MAXDRIVENAME];
854 CFDictionaryRef properties;
855 CFStringRef name;
856
857 /* get drive properties */
858 status = IORegistryEntryCreateCFProperties(drive,
859 (CFMutableDictionaryRef *)&properties,
860 kCFAllocatorDefault,
861 kNilOptions);
862 if (status != KERN_SUCCESS) continue;
863
864 /* get name from properties */
865 name = (CFStringRef)CFDictionaryGetValue(properties,
866 CFSTR(kIOBSDNameKey));
867
868 if (name && CFStringGetCString(name, bsdname, MAXDRIVENAME, kCFStringEncodingUTF8)) {
869 int i;
870 for (i = 0; i < num_devices; ++i) {
871 if (strcmp(bsdname,drivestat[i].name) == 0) {
872 if (i < MAXDRIVES-1) {
873 memmove(&drivestat[i], &drivestat[i+1], sizeof(struct drivestats)*(MAXDRIVES-i));
874 }
875 --num_devices;
876 phdr_flag = 1;
877 qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
878 break;
879 }
880 }
881 }
882 CFRelease(properties);
883 IOObjectRelease(drive);
884 }
885 }
886
887 /*
888 * Try to record the named device as interesting. It
889 * must be an IOMedia device.
890 */
891 static int
892 record_one_device(char *name)
893 {
894 io_iterator_t drivelist;
895 io_registry_entry_t drive;
896 kern_return_t status;
897
898 /*
899 * Find the device.
900 */
901 status = IOServiceGetMatchingServices(masterPort,
902 IOBSDNameMatching(masterPort, kNilOptions, name),
903 &drivelist);
904 if (status != KERN_SUCCESS)
905 errx(1, "couldn't match '%s'", name);
906
907 /*
908 * Get the first match (should only be one)
909 */
910 if (!(drive = IOIteratorNext(drivelist)))
911 errx(1, "'%s' not found", name);
912 if (!IOObjectConformsTo(drive, "IOMedia"))
913 errx(1, "'%s' is not a storage device", name);
914
915 /*
916 * Record the device.
917 */
918 if (record_device(drive))
919 errx(1, "could not record '%s' for monitoring", name);
920
921 IOObjectRelease(drive);
922 IOObjectRelease(drivelist);
923
924 return(0);
925 }
926
927 /*
928 * Determine whether an IORegistryEntry refers to a valid
929 * I/O device, and if so, record it.
930 */
931 static int
932 record_device(io_registry_entry_t drive)
933 {
934 io_registry_entry_t parent;
935 CFDictionaryRef properties;
936 CFStringRef name;
937 CFNumberRef number;
938 kern_return_t status;
939
940 /* get drive's parent */
941 status = IORegistryEntryGetParentEntry(drive,
942 kIOServicePlane, &parent);
943 if (status != KERN_SUCCESS)
944 errx(1, "device has no parent");
945 if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
946 drivestat[num_devices].driver = parent;
947
948 /* get drive properties */
949 status = IORegistryEntryCreateCFProperties(drive,
950 (CFMutableDictionaryRef *)&properties,
951 kCFAllocatorDefault,
952 kNilOptions);
953 if (status != KERN_SUCCESS)
954 errx(1, "device has no properties");
955
956 /* get name from properties */
957 name = (CFStringRef)CFDictionaryGetValue(properties,
958 CFSTR(kIOBSDNameKey));
959 if (name)
960 CFStringGetCString(name, drivestat[num_devices].name,
961 MAXDRIVENAME, kCFStringEncodingUTF8);
962 else {
963 errx(1, "device does not have a BSD name");
964 }
965
966 /* get blocksize from properties */
967 number = (CFNumberRef)CFDictionaryGetValue(properties,
968 CFSTR(kIOMediaPreferredBlockSizeKey));
969 if (number)
970 CFNumberGetValue(number, kCFNumberSInt64Type,
971 &drivestat[num_devices].blocksize);
972 else
973 errx(1, "device does not have a preferred block size");
974
975 // radar:18700383
976 if (drivestat[num_devices].blocksize == 0) {
977 warnx("%s claims a blocksize of 0; defaulting to 512. Its statistics may be inaccurate.", drivestat[num_devices].name);
978 drivestat[num_devices].blocksize = 512;
979 }
980
981 /* clean up, return success */
982 CFRelease(properties);
983 num_devices++;
984 return(0);
985 }
986
987 /* failed, don't keep parent */
988 IOObjectRelease(parent);
989 return(1);
990 }
991
992 static int
993 compare_drivestats(const void* pa, const void* pb)
994 {
995 struct drivestats* a = (struct drivestats*)pa;
996 struct drivestats* b = (struct drivestats*)pb;
997 return strcmp(a->name, b->name);
998 }