]> git.saurik.com Git - apple/system_cmds.git/blob - iostat.tproj/iostat.c
system_cmds-498.0.10.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 #include <mach/mach_host.h> /* host_statistics */
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, Uflag = 0, Kflag = 0;
173 static volatile sig_atomic_t phdr_flag = 0;
174 static IONotificationPortRef notifyPort;
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 void loadstats(void);
183 static int readvar(const char *name, void *ptr, size_t len);
184
185 static int record_all_devices(void);
186 static void record_drivelist(void* context, io_iterator_t drivelist);
187 static void remove_drivelist(void* context, io_iterator_t drivelist);
188 static int record_one_device(char *name);
189 static int record_device(io_registry_entry_t drive);
190
191 static int compare_drivestats(const void* pa, const void* pb);
192
193 static long double compute_etime(struct timeval cur_time,
194 struct timeval prev_time);
195
196 static void
197 usage(void)
198 {
199 /*
200 * We also support the following 'traditional' syntax:
201 * iostat [drives] [wait [count]]
202 * This isn't mentioned in the man page, or the usage statement,
203 * but it is supported.
204 */
205 fprintf(stderr, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
206 "\t [-w wait] [drives]\n");
207 }
208
209 int
210 main(int argc, char **argv)
211 {
212 int c;
213 int hflag = 0, cflag = 0, wflag = 0, nflag = 0;
214 int count = 0, waittime = 0;
215 int headercount;
216 int num_devices_specified;
217 int havelast = 0;
218
219 CFRunLoopSourceRef rls;
220
221 maxshowdevs = 3;
222
223 while ((c = getopt(argc, argv, "c:CdIKM:n:oTUw:?")) != -1) {
224 switch(c) {
225 case 'c':
226 cflag++;
227 count = atoi(optarg);
228 if (count < 1)
229 errx(1, "count %d is < 1", count);
230 break;
231 case 'C':
232 Cflag++;
233 break;
234 case 'd':
235 dflag++;
236 break;
237 case 'I':
238 Iflag++;
239 break;
240 case 'K':
241 Kflag++;
242 break;
243 case 'n':
244 nflag++;
245 maxshowdevs = atoi(optarg);
246 if (maxshowdevs < 0)
247 errx(1, "number of devices %d is < 0",
248 maxshowdevs);
249 break;
250 case 'o':
251 oflag++;
252 break;
253 case 'T':
254 Tflag++;
255 break;
256 case 'U':
257 Uflag++;
258 break;
259 case 'w':
260 wflag++;
261 waittime = atoi(optarg);
262 if (waittime < 1)
263 errx(1, "wait time is < 1");
264 break;
265 default:
266 usage();
267 exit(1);
268 break;
269 }
270 }
271
272 argc -= optind;
273 argv += optind;
274
275 /*
276 * Get the Mach private port.
277 */
278 host_priv_port = mach_host_self();
279
280 /*
281 * Get the I/O Kit communication handle.
282 */
283 IOMasterPort(bootstrap_port, &masterPort);
284
285 notifyPort = IONotificationPortCreate(masterPort);
286 rls = IONotificationPortGetRunLoopSource(notifyPort);
287 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
288
289 /*
290 * Make sure Tflag, Cflag and Uflag are set if dflag == 0. If dflag is
291 * greater than 0, they may be 0 or non-zero.
292 */
293 if (dflag == 0) {
294 Cflag = 1;
295 Tflag = 1;
296 Uflag = 1;
297 }
298
299 /*
300 * TTY statistics are broken, disabling them.
301 */
302 Tflag = 0;
303
304 /*
305 * Figure out how many devices we should display if not given
306 * an explicit value.
307 */
308 if (nflag == 0) {
309 if (oflag > 0) {
310 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
311 maxshowdevs = 5;
312 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
313 maxshowdevs = 5;
314 else
315 maxshowdevs = 4;
316 } else {
317 if ((dflag > 0) && (Cflag == 0))
318 maxshowdevs = 4;
319 else
320 maxshowdevs = 3;
321 }
322 }
323
324 /*
325 * If the user specified any devices on the command line, record
326 * them for monitoring.
327 */
328 for (num_devices_specified = 0; *argv; ++argv) {
329 if (isdigit(**argv))
330 break;
331 if (record_one_device(*argv))
332 errx(1, "can't record '%s' for monitoring");
333 num_devices_specified++;
334 }
335 if (nflag == 0 && maxshowdevs < num_devices_specified)
336 maxshowdevs = num_devices_specified;
337
338 /* if no devices were specified, pick them ourselves */
339 if ((num_devices_specified == 0) && record_all_devices())
340 err(1, "can't find any devices to display");
341
342 /*
343 * Look for the traditional wait time and count arguments.
344 */
345 if (*argv) {
346 waittime = atoi(*argv);
347
348 /* Let the user know he goofed, but keep going anyway */
349 if (wflag != 0)
350 warnx("discarding previous wait interval, using"
351 " %d instead", waittime);
352 wflag++;
353
354 if (*++argv) {
355 count = atoi(*argv);
356 if (cflag != 0)
357 warnx("discarding previous count, using %d"
358 " instead", count);
359 cflag++;
360 } else
361 count = -1;
362 }
363
364 /*
365 * If the user specified a count, but not an interval, we default
366 * to an interval of 1 second.
367 */
368 if ((wflag == 0) && (cflag > 0))
369 waittime = 1;
370
371 /*
372 * If the user specified a wait time, but not a count, we want to
373 * go on ad infinitum. This can be redundant if the user uses the
374 * traditional method of specifying the wait, since in that case we
375 * already set count = -1 above. Oh well.
376 */
377 if ((wflag > 0) && (cflag == 0))
378 count = -1;
379
380 cur.tk_nout = 0;
381 cur.tk_nin = 0;
382
383 /*
384 * Set the busy time to the system boot time, so the stats are
385 * calculated since system boot.
386 */
387 if (readvar("kern.boottime", &cur_time, sizeof(cur_time)) != 0)
388 exit(1);
389
390 /*
391 * If the user stops the program (control-Z) and then resumes it,
392 * print out the header again.
393 */
394 (void)signal(SIGCONT, phdr);
395
396 for (headercount = 1;;) {
397 long tmp;
398 long double etime;
399
400 if (Tflag > 0) {
401 if ((readvar("kern.tty_nin", &cur.tk_nin,
402 sizeof(cur.tk_nin)) != 0)
403 || (readvar("kern.tty_nout",
404 &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
405 Tflag = 0;
406 warnx("disabling TTY statistics");
407 }
408 }
409
410 if (!--headercount || phdr_flag) {
411 phdr_flag = 0;
412 headercount = 20;
413 do_phdr();
414 }
415
416 last_time = cur_time;
417 gettimeofday(&cur_time, NULL);
418
419 if (Tflag > 0) {
420 tmp = cur.tk_nin;
421 cur.tk_nin -= last.tk_nin;
422 last.tk_nin = tmp;
423 tmp = cur.tk_nout;
424 cur.tk_nout -= last.tk_nout;
425 last.tk_nout = tmp;
426 }
427
428 etime = compute_etime(cur_time, last_time);
429
430 if (etime == 0.0)
431 etime = 1.0;
432
433 if (Tflag > 0)
434 printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
435 cur.tk_nout / etime);
436
437 devstats(hflag, etime, havelast);
438
439 if (Cflag > 0)
440 cpustats();
441
442 if (Uflag > 0)
443 loadstats();
444
445 printf("\n");
446 fflush(stdout);
447
448 if (count >= 0 && --count <= 0)
449 break;
450
451 /*
452 * Instead of sleep(waittime), wait in
453 * the RunLoop for IONotifications.
454 */
455 CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)waittime, 1);
456
457 havelast = 1;
458 }
459
460 exit(0);
461 }
462
463 static void
464 phdr(int signo)
465 {
466
467 phdr_flag = 1;
468 }
469
470 static void
471 do_phdr()
472 {
473 register int i;
474
475 if (Tflag > 0)
476 (void)printf(" tty");
477
478 for (i = 0; i < num_devices && i < maxshowdevs; i++){
479 if (oflag > 0)
480 (void)printf("%12.6s ", drivestat[i].name);
481 else
482 printf("%15.6s ", drivestat[i].name);
483 }
484
485 if (Cflag > 0)
486 (void)printf(" cpu");
487
488 if (Uflag > 0)
489 (void)printf(" load average\n");
490 else
491 (void)printf("\n");
492
493 if (Tflag > 0)
494 (void)printf(" tin tout");
495
496 for (i=0; i < num_devices && i < maxshowdevs; i++){
497 if (oflag > 0) {
498 if (Iflag == 0)
499 (void)printf(" sps tps msps ");
500 else
501 (void)printf(" blk xfr msps ");
502 } else {
503 if (Iflag == 0)
504 printf(" KB/t tps MB/s ");
505 else
506 printf(" KB/t xfrs MB ");
507 }
508 }
509
510 if (Cflag > 0)
511 (void)printf(" us sy id");
512
513 if (Uflag > 0)
514 (void)printf(" 1m 5m 15m\n");
515 else
516 printf("\n");
517 }
518
519 static void
520 devstats(int perf_select, long double etime, int havelast)
521 {
522 CFNumberRef number;
523 CFDictionaryRef properties;
524 CFDictionaryRef statistics;
525 long double transfers_per_second;
526 long double kb_per_transfer, mb_per_second;
527 u_int64_t value;
528 u_int64_t total_bytes, total_transfers, total_blocks, total_time;
529 u_int64_t interval_bytes, interval_transfers, interval_blocks;
530 u_int64_t interval_time;
531 long double interval_mb;
532 long double blocks_per_second, ms_per_transaction;
533 kern_return_t status;
534 int i;
535
536 for (i = 0; i < num_devices && i < maxshowdevs; i++) {
537
538 /*
539 * If the drive goes away, we may not get any properties
540 * for it. So take some defaults.
541 */
542 total_bytes = 0;
543 total_transfers = 0;
544 total_time = 0;
545
546 /* get drive properties */
547 status = IORegistryEntryCreateCFProperties(drivestat[i].driver,
548 (CFMutableDictionaryRef *)&properties,
549 kCFAllocatorDefault,
550 kNilOptions);
551 if (status != KERN_SUCCESS)
552 continue;
553
554 /* get statistics from properties */
555 statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
556 CFSTR(kIOBlockStorageDriverStatisticsKey));
557 if (statistics) {
558
559 /*
560 * Get I/O volume.
561 */
562 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
563 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
564 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
565 total_bytes += value;
566 }
567 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
568 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
569 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
570 total_bytes += value;
571 }
572
573 /*
574 * Get I/O counts.
575 */
576 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
577 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
578 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
579 total_transfers += value;
580 }
581 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
582 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
583 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
584 total_transfers += value;
585 }
586
587 /*
588 * Get I/O time.
589 */
590 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
591 CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
592 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
593 total_time += value;
594 }
595 if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
596 CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
597 CFNumberGetValue(number, kCFNumberSInt64Type, &value);
598 total_time += value;
599 }
600
601 }
602 CFRelease(properties);
603
604 /*
605 * Compute delta values and stats.
606 */
607 interval_bytes = total_bytes - drivestat[i].total_bytes;
608 interval_transfers = total_transfers
609 - drivestat[i].total_transfers;
610 interval_time = total_time - drivestat[i].total_time;
611
612 /* update running totals, only once for -I */
613 if ((Iflag == 0) || (drivestat[i].total_bytes == 0)) {
614 drivestat[i].total_bytes = total_bytes;
615 drivestat[i].total_transfers = total_transfers;
616 drivestat[i].total_time = total_time;
617 }
618
619 interval_blocks = interval_bytes / drivestat[i].blocksize;
620 total_blocks = total_bytes / drivestat[i].blocksize;
621
622 blocks_per_second = interval_blocks / etime;
623 transfers_per_second = interval_transfers / etime;
624 mb_per_second = (interval_bytes / etime) / (1024 * 1024);
625
626 kb_per_transfer = (interval_transfers > 0) ?
627 ((long double)interval_bytes / interval_transfers)
628 / 1024 : 0;
629
630 /* times are in nanoseconds, convert to milliseconds */
631 ms_per_transaction = (interval_transfers > 0) ?
632 ((long double)interval_time / interval_transfers)
633 / 1000 : 0;
634
635 if (Kflag)
636 total_blocks = total_blocks * drivestat[i].blocksize
637 / 1024;
638
639 if (oflag > 0) {
640 int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
641
642 if (Iflag == 0)
643 printf("%4.0Lf%4.0Lf%5.*Lf ",
644 blocks_per_second,
645 transfers_per_second,
646 msdig,
647 ms_per_transaction);
648 else
649 printf("%4.1qu%4.1qu%5.*Lf ",
650 interval_blocks,
651 interval_transfers,
652 msdig,
653 ms_per_transaction);
654 } else {
655 if (Iflag == 0)
656 printf(" %7.2Lf %3.0Lf %5.2Lf ",
657 kb_per_transfer,
658 transfers_per_second,
659 mb_per_second);
660 else {
661 interval_mb = interval_bytes;
662 interval_mb /= 1024 * 1024;
663
664 printf(" %7.2Lf %3.1qu %5.2Lf ",
665 kb_per_transfer,
666 interval_transfers,
667 interval_mb);
668 }
669 }
670 }
671 }
672
673 static void
674 cpustats(void)
675 {
676 mach_msg_type_number_t count;
677 kern_return_t status;
678 double time;
679
680 /*
681 * Get CPU usage counters.
682 */
683 count = HOST_CPU_LOAD_INFO_COUNT;
684 status = host_statistics(host_priv_port, HOST_CPU_LOAD_INFO,
685 (host_info_t)&cur.load, &count);
686 if (status != KERN_SUCCESS)
687 errx(1, "couldn't fetch CPU stats");
688
689 /*
690 * Make 'cur' fields relative, update 'last' fields to current values,
691 * calculate total elapsed time.
692 */
693 time = 0.0;
694 cur.load.cpu_ticks[CPU_STATE_USER]
695 -= last.load.cpu_ticks[CPU_STATE_USER];
696 last.load.cpu_ticks[CPU_STATE_USER]
697 += cur.load.cpu_ticks[CPU_STATE_USER];
698 time += cur.load.cpu_ticks[CPU_STATE_USER];
699 cur.load.cpu_ticks[CPU_STATE_SYSTEM]
700 -= last.load.cpu_ticks[CPU_STATE_SYSTEM];
701 last.load.cpu_ticks[CPU_STATE_SYSTEM]
702 += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
703 time += cur.load.cpu_ticks[CPU_STATE_SYSTEM];
704 cur.load.cpu_ticks[CPU_STATE_IDLE]
705 -= last.load.cpu_ticks[CPU_STATE_IDLE];
706 last.load.cpu_ticks[CPU_STATE_IDLE]
707 += cur.load.cpu_ticks[CPU_STATE_IDLE];
708 time += cur.load.cpu_ticks[CPU_STATE_IDLE];
709
710 /*
711 * Print times.
712 */
713 #define PTIME(kind) { \
714 double cpu = rint(100. * cur.load.cpu_ticks[kind] / (time ? time : 1));\
715 printf("%*.0f", (100 == cpu) ? 4 : 3, cpu); \
716 }
717 PTIME(CPU_STATE_USER);
718 PTIME(CPU_STATE_SYSTEM);
719 PTIME(CPU_STATE_IDLE);
720 }
721
722 static void
723 loadstats(void)
724 {
725 double loadavg[3];
726
727 if(getloadavg(loadavg,3)!=3)
728 errx(1, "couldn't fetch load average");
729
730 printf(" %4.2f %4.2f %4.2f",loadavg[0],loadavg[1],loadavg[2]);
731 }
732
733 static int
734 readvar(const char *name, void *ptr, size_t len)
735 {
736 int oid[4];
737 int oidlen;
738
739 size_t nlen = len;
740
741 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
742 if (errno != ENOENT) {
743 warn("sysctl(%s) failed", name);
744 return (1);
745 }
746 /*
747 * XXX fallback code to deal with systems where
748 * sysctlbyname can't find "old" OIDs, should be removed.
749 */
750 if (!strcmp(name, "kern.boottime")) {
751 oid[0] = CTL_KERN;
752 oid[1] = KERN_BOOTTIME;
753 oidlen = 2;
754 } else {
755 warn("sysctl(%s) failed", name);
756 return (1);
757 }
758
759 nlen = len;
760 if (sysctl(oid, oidlen, ptr, &nlen, NULL, 0) == -1) {
761 warn("sysctl(%s) failed", name);
762 return (1);
763 }
764 }
765 if (nlen != len) {
766 warnx("sysctl(%s): expected %lu, got %lu", name,
767 (unsigned long)len, (unsigned long)nlen);
768 return (1);
769 }
770 return (0);
771 }
772
773 static long double
774 compute_etime(struct timeval cur_time, struct timeval prev_time)
775 {
776 struct timeval busy_time;
777 u_int64_t busy_usec;
778 long double etime;
779
780 timersub(&cur_time, &prev_time, &busy_time);
781
782 busy_usec = busy_time.tv_sec;
783 busy_usec *= 1000000;
784 busy_usec += busy_time.tv_usec;
785 etime = busy_usec;
786 etime /= 1000000;
787
788 return(etime);
789 }
790
791 /*
792 * Record all "whole" IOMedia objects as being interesting.
793 */
794 static int
795 record_all_devices(void)
796 {
797 io_iterator_t drivelist;
798 CFMutableDictionaryRef match;
799 kern_return_t status;
800
801 /*
802 * Get an iterator for IOMedia objects.
803 */
804 match = IOServiceMatching("IOMedia");
805 CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
806
807 CFRetain(match);
808 status = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, match, &record_drivelist, NULL, &drivelist);
809
810 if (status != KERN_SUCCESS)
811 errx(1, "couldn't match whole IOMedia devices");
812
813 /*
814 * Scan all of the IOMedia objects, and for each
815 * object that has a parent IOBlockStorageDriver, save
816 * the object's name and the parent (from which we can
817 * fetch statistics).
818 *
819 * XXX What about RAID devices?
820 */
821
822 record_drivelist(NULL, drivelist);
823
824
825 status = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, match, &remove_drivelist, NULL, &drivelist);
826
827 if (status != KERN_SUCCESS)
828 errx(1, "couldn't match whole IOMedia device removal");
829
830 remove_drivelist(NULL, drivelist);
831
832 return(0);
833 }
834
835 static void record_drivelist(void* context, io_iterator_t drivelist)
836 {
837 io_registry_entry_t drive;
838 while ((drive = IOIteratorNext(drivelist))) {
839 if (num_devices < MAXDRIVES) {
840 record_device(drive);
841 phdr_flag = 1;
842 }
843 IOObjectRelease(drive);
844 }
845 qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
846 }
847
848 static void 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, CFStringGetSystemEncoding())) {
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, CFStringGetSystemEncoding());
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 /* clean up, return success */
976 CFRelease(properties);
977 num_devices++;
978 return(0);
979 }
980
981 /* failed, don't keep parent */
982 IOObjectRelease(parent);
983 return(1);
984 }
985
986 static int
987 compare_drivestats(const void* pa, const void* pb)
988 {
989 struct drivestats* a = (struct drivestats*)pa;
990 struct drivestats* b = (struct drivestats*)pb;
991 return strcmp(a->name, b->name);
992 }