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