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 146struct 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 155static struct drivestats drivestat[MAXDRIVES]; 156 157static struct timeval cur_time, last_time; 158 159struct statinfo { 160 long tk_nin; 161 long tk_nout; 162 host_cpu_load_info_data_t load; 163}; 164 165static struct statinfo cur, last; 166 167static mach_port_t host_priv_port; 168static mach_port_t masterPort; 169 170static int num_devices; 171static int maxshowdevs; 172static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Uflag = 0, Kflag = 0; 173static volatile sig_atomic_t phdr_flag = 0; 174static IONotificationPortRef notifyPort; 175 176/* local function declarations */ 177static void usage(void); 178static void phdr(int signo); 179static void do_phdr(); 180static void devstats(int perf_select, long double etime, int havelast); 181static void cpustats(void); 182static void loadstats(void); 183static int readvar(const char *name, void *ptr, size_t len); 184 185static int record_all_devices(void); 186static void record_drivelist(void* context, io_iterator_t drivelist); 187static void remove_drivelist(void* context, io_iterator_t drivelist); 188static int record_one_device(char *name); 189static int record_device(io_registry_entry_t drive); 190 191static int compare_drivestats(const void* pa, const void* pb); 192 193static long double compute_etime(struct timeval cur_time, 194 struct timeval prev_time); 195 196static void 197usage(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 209int 210main(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", *argv); 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 463static void 464phdr(int signo) 465{ 466 467 phdr_flag = 1; 468} 469 470static void 471do_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 519static void 520devstats(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 673static void 674cpustats(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 722static void 723loadstats(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 733static int 734readvar(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 773static long double 774compute_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 */ 794static int 795record_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 835static 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 848static 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, 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 */ 891static int 892record_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 */ 931static int 932record_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 /* 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 986static int 987compare_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} 993