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