1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * rewritten from UCB 4.13 83/09/25 27 * rewritten from SunOS 4.1 SID 1.18 89/10/06 28 */ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stdarg.h> 33 #include <ctype.h> 34 #include <unistd.h> 35 #include <memory.h> 36 #include <errno.h> 37 #include <string.h> 38 #include <signal.h> 39 #include <sys/types.h> 40 #include <time.h> 41 #include <sys/time.h> 42 #include <sys/sysinfo.h> 43 #include <inttypes.h> 44 #include <strings.h> 45 #include <sys/systeminfo.h> 46 #include <kstat.h> 47 #include <locale.h> 48 49 #include "dsr.h" 50 #include "statcommon.h" 51 52 #define DISK_OLD 0x0001 53 #define DISK_NEW 0x0002 54 #define DISK_EXTENDED 0x0004 55 #define DISK_ERRORS 0x0008 56 #define DISK_EXTENDED_ERRORS 0x0010 57 #define DISK_IOPATH_LI 0x0020 /* LunInitiator */ 58 #define DISK_IOPATH_LTI 0x0040 /* LunTargetInitiator */ 59 60 #define DISK_NORMAL (DISK_OLD | DISK_NEW) 61 #define DISK_IO_MASK (DISK_OLD | DISK_NEW | DISK_EXTENDED) 62 #define DISK_ERROR_MASK (DISK_ERRORS | DISK_EXTENDED_ERRORS) 63 #define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED) 64 65 #define REPRINT 19 66 67 #define NUMBER_OF_ERR_COUNTERS 3 68 69 /* 70 * It's really a pseudo-gigabyte. We use 1000000000 bytes so that the disk 71 * labels don't look bad. 1GB is really 1073741824 bytes. 72 */ 73 #define DISK_GIGABYTE 1000000000.0 74 75 /* 76 * Function desciptor to be called when extended 77 * headers are used. 78 */ 79 typedef struct formatter { 80 void (*nfunc)(void); 81 struct formatter *next; 82 } format_t; 83 84 /* 85 * Used to get formatting right when printing tty/cpu 86 * data to the right of disk data 87 */ 88 enum show_disk_mode { 89 SHOW_FIRST_ONLY, 90 SHOW_SECOND_ONWARDS, 91 SHOW_ALL 92 }; 93 94 enum show_disk_mode show_disk_mode = SHOW_ALL; 95 96 char *cmdname = "iostat"; 97 int caught_cont = 0; 98 99 static char one_blank[] = " "; 100 static char two_blanks[] = " "; 101 102 /* 103 * count for number of lines to be emitted before a header is 104 * shown again. Only used for the basic format. 105 */ 106 static uint_t tohdr = 1; 107 108 /* 109 * If we're in raw format, have we printed a header? We only do it 110 * once for raw but we emit it every REPRINT lines in non-raw format. 111 * This applies only for the basic header. The extended header is 112 * done only once in both formats. 113 */ 114 static uint_t hdr_out; 115 116 /* 117 * Flags representing arguments from command line 118 */ 119 static uint_t do_tty; /* show tty info (-t) */ 120 static uint_t do_disk; /* show disk info per selected */ 121 /* format (-d, -D, -e, -E, -x -X -Y) */ 122 static uint_t do_cpu; /* show cpu info (-c) */ 123 static uint_t do_interval; /* do intervals (-I) */ 124 static int do_partitions; /* per-partition stats (-p) */ 125 static int do_partitions_only; /* per-partition stats only (-P) */ 126 /* no per-device stats for disks */ 127 static uint_t do_conversions; /* display disks as cXtYdZ (-n) */ 128 static uint_t do_megabytes; /* display data in MB/sec (-M) */ 129 static uint_t do_controller; /* display controller info (-C) */ 130 static uint_t do_raw; /* emit raw format (-r) */ 131 static uint_t timestamp_fmt = NODATE; /* timestamp each display (-T) */ 132 static uint_t do_devid; /* -E should show devid */ 133 134 /* 135 * Default number of disk drives to be displayed in basic format 136 */ 137 #define DEFAULT_LIMIT 4 138 139 struct iodev_filter df; 140 141 static uint_t suppress_state; /* skip state change messages */ 142 static uint_t suppress_zero; /* skip zero valued lines */ 143 static uint_t show_mountpts; /* show mount points */ 144 static int interval; /* interval (seconds) to output */ 145 static int iter; /* iterations from command line */ 146 147 #define SMALL_SCRATCH_BUFLEN MAXNAMELEN 148 149 static int iodevs_nl; /* name field width */ 150 #define IODEVS_NL_MIN 6 /* not too thin for "device" */ 151 #define IODEVS_NL_MAX 24 /* but keep full width under 80 */ 152 153 static char disk_header[132]; 154 static uint_t dh_len; /* disk header length for centering */ 155 static int lineout; /* data waiting to be printed? */ 156 157 static struct snapshot *newss; 158 static struct snapshot *oldss; 159 static double getime; /* elapsed time */ 160 static double percent; /* 100 / etime */ 161 162 /* 163 * List of functions to be called which will construct the desired output 164 */ 165 static format_t *formatter_list; 166 static format_t *formatter_end; 167 168 static u_longlong_t ull_delta(u_longlong_t, u_longlong_t); 169 static uint_t u32_delta(uint_t, uint_t); 170 static void setup(void (*nfunc)(void)); 171 static void print_tty_hdr1(void); 172 static void print_tty_hdr2(void); 173 static void print_cpu_hdr1(void); 174 static void print_cpu_hdr2(void); 175 static void print_tty_data(void); 176 static void print_cpu_data(void); 177 static void print_err_hdr(void); 178 static void print_disk_header(void); 179 static void hdrout(void); 180 static void disk_errors(void); 181 static void do_newline(void); 182 static void push_out(const char *, ...); 183 static void printhdr(int); 184 static void printxhdr(void); 185 static void usage(void); 186 static void do_args(int, char **); 187 static void do_format(void); 188 static void show_all_disks(void); 189 static void show_first_disk(void); 190 static void show_other_disks(void); 191 static void show_disk_errors(void *, void *, void *); 192 static void write_core_header(void); 193 static int fzero(double value); 194 static int safe_strtoi(char const *val, char *errmsg); 195 196 int 197 main(int argc, char **argv) 198 { 199 enum snapshot_types types = SNAP_SYSTEM; 200 kstat_ctl_t *kc; 201 long hz; 202 int forever; 203 hrtime_t start_n; 204 hrtime_t period_n; 205 206 (void) setlocale(LC_ALL, ""); 207 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 208 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 209 #endif 210 (void) textdomain(TEXT_DOMAIN); 211 212 do_args(argc, argv); 213 214 /* 215 * iostat historically showed CPU changes, even though 216 * it doesn't provide much useful information 217 */ 218 types |= SNAP_CPUS; 219 220 if (do_disk) 221 types |= SNAP_IODEVS; 222 223 if (do_disk && !do_partitions_only) 224 df.if_allowed_types |= IODEV_DISK; 225 if (do_disk & DISK_IOPATH_LI) { 226 df.if_allowed_types |= IODEV_IOPATH_LTI; 227 types |= SNAP_IOPATHS_LI; 228 } 229 if (do_disk & DISK_IOPATH_LTI) { 230 df.if_allowed_types |= IODEV_IOPATH_LTI; 231 types |= SNAP_IOPATHS_LTI; 232 } 233 if (do_disk & DISK_ERROR_MASK) 234 types |= SNAP_IODEV_ERRORS; 235 if (do_partitions || do_partitions_only) 236 df.if_allowed_types |= IODEV_PARTITION; 237 if (do_conversions) 238 types |= SNAP_IODEV_PRETTY; 239 if (do_devid) 240 types |= SNAP_IODEV_DEVID; 241 if (do_controller) { 242 if (!(do_disk & PRINT_VERTICAL) || 243 (do_disk & DISK_EXTENDED_ERRORS)) 244 fail(0, "-C can only be used with -e or -x."); 245 types |= SNAP_CONTROLLERS; 246 df.if_allowed_types |= IODEV_CONTROLLER; 247 } 248 249 hz = sysconf(_SC_CLK_TCK); 250 251 /* 252 * Undocumented behavior - sending a SIGCONT will result 253 * in a new header being emitted. Used only if we're not 254 * doing extended headers. This is a historical 255 * artifact. 256 */ 257 if (!(do_disk & PRINT_VERTICAL)) 258 (void) signal(SIGCONT, printhdr); 259 260 if (interval) 261 period_n = (hrtime_t)interval * NANOSEC; 262 263 kc = open_kstat(); 264 if (interval) 265 start_n = gethrtime(); 266 newss = acquire_snapshot(kc, types, &df); 267 268 /* compute width of "device" field */ 269 iodevs_nl = newss->s_iodevs_is_name_maxlen; 270 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ? 271 IODEVS_NL_MIN : iodevs_nl; 272 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ? 273 IODEVS_NL_MAX : iodevs_nl; 274 275 do_format(); 276 277 forever = (iter == 0); 278 do { 279 if (do_conversions && show_mountpts) 280 do_mnttab(); 281 282 if (do_tty || do_cpu) { 283 kstat_t *oldks; 284 oldks = oldss ? &oldss->s_sys.ss_agg_sys : NULL; 285 getime = cpu_ticks_delta(oldks, 286 &newss->s_sys.ss_agg_sys); 287 percent = (getime > 0.0) ? 100.0 / getime : 0.0; 288 getime = (getime / nr_active_cpus(newss)) / hz; 289 if (getime == 0.0) 290 getime = (double)interval; 291 if (getime == 0.0 || do_interval) 292 getime = 1.0; 293 } 294 295 if (formatter_list) { 296 format_t *tmp; 297 tmp = formatter_list; 298 299 if (timestamp_fmt != NODATE) 300 print_timestamp(timestamp_fmt); 301 302 while (tmp) { 303 (tmp->nfunc)(); 304 tmp = tmp->next; 305 } 306 (void) fflush(stdout); 307 } 308 309 /* only remaining/doing a single iteration, we are done */ 310 if (iter == 1) 311 continue; 312 313 if (interval > 0) 314 /* Have a kip */ 315 sleep_until(&start_n, period_n, forever, &caught_cont); 316 317 free_snapshot(oldss); 318 oldss = newss; 319 newss = acquire_snapshot(kc, types, &df); 320 iodevs_nl = (newss->s_iodevs_is_name_maxlen > iodevs_nl) ? 321 newss->s_iodevs_is_name_maxlen : iodevs_nl; 322 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ? 323 IODEVS_NL_MIN : iodevs_nl; 324 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ? 325 IODEVS_NL_MAX : iodevs_nl; 326 327 if (!suppress_state) 328 snapshot_report_changes(oldss, newss); 329 330 /* if config changed, show stats from boot */ 331 if (snapshot_has_changed(oldss, newss)) { 332 free_snapshot(oldss); 333 oldss = NULL; 334 } 335 336 } while (--iter); 337 338 free_snapshot(oldss); 339 free_snapshot(newss); 340 (void) kstat_close(kc); 341 free(df.if_names); 342 return (0); 343 } 344 345 /* 346 * Some magic numbers used in header formatting. 347 * 348 * DISK_LEN = length of either "kps tps serv" or "wps rps util" 349 * using 0 as the first position 350 * 351 * DISK_ERROR_LEN = length of "s/w h/w trn tot" with one space on 352 * either side. Does not use zero as first pos. 353 * 354 * DEVICE_LEN = length of "device" + 1 character. 355 */ 356 357 #define DISK_LEN 11 358 #define DISK_ERROR_LEN 16 359 #define DEVICE_LEN 7 360 361 /*ARGSUSED*/ 362 static void 363 show_disk_name(void *v1, void *v2, void *data) 364 { 365 struct iodev_snapshot *dev = (struct iodev_snapshot *)v2; 366 size_t slen; 367 char *name; 368 char fbuf[SMALL_SCRATCH_BUFLEN]; 369 370 if (dev == NULL) 371 return; 372 373 name = do_conversions ? dev->is_pretty : dev->is_name; 374 name = name ? name : dev->is_name; 375 376 if (!do_raw) { 377 uint_t width; 378 379 slen = strlen(name); 380 /* 381 * The length is less 382 * than the section 383 * which will be displayed 384 * on the next line. 385 * Center the entry. 386 */ 387 388 width = (DISK_LEN + 1)/2 + (slen / 2); 389 (void) snprintf(fbuf, sizeof (fbuf), 390 "%*s", width, name); 391 name = fbuf; 392 push_out("%-13.13s ", name); 393 } else { 394 push_out(name); 395 } 396 } 397 398 /*ARGSUSED*/ 399 static void 400 show_disk_header(void *v1, void *v2, void *data) 401 { 402 push_out(disk_header); 403 } 404 405 /* 406 * Write out a two line header. What is written out depends on the flags 407 * selected but in the worst case consists of a tty header, a disk header 408 * providing information for 4 disks and a cpu header. 409 * 410 * The tty header consists of the word "tty" on the first line above the 411 * words "tin tout" on the next line. If present the tty portion consumes 412 * the first 10 characters of each line since "tin tout" is surrounded 413 * by single spaces. 414 * 415 * Each of the disk sections is a 14 character "block" in which the name of 416 * the disk is centered in the first 12 characters of the first line. 417 * 418 * The cpu section is an 11 character block with "cpu" centered over the 419 * section. 420 * 421 * The worst case should look as follows: 422 * 423 * 0---------1--------2---------3---------4---------5---------6---------7------- 424 * tty sd0 sd1 sd2 sd3 cpu 425 * tin tout kps tps serv kps tps serv kps tps serv kps tps serv us sy wt id 426 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN 427 * 428 * When -D is specified, the disk header looks as follows (worst case): 429 * 430 * 0---------1--------2---------3---------4---------5---------6---------7------- 431 * tty sd0 sd1 sd2 sd3 cpu 432 * tin tout rps wps util rps wps util rps wps util rps wps util us sy wt id 433 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN 434 */ 435 static void 436 printhdr(int sig) 437 { 438 /* 439 * If we're here because a signal fired, reenable the 440 * signal. 441 */ 442 if (sig) 443 (void) signal(SIGCONT, printhdr); 444 if (sig == SIGCONT) 445 caught_cont = 1; 446 /* 447 * Horizontal mode headers 448 * 449 * First line 450 */ 451 if (do_tty) 452 print_tty_hdr1(); 453 454 if (do_disk & DISK_NORMAL) { 455 (void) snapshot_walk(SNAP_IODEVS, NULL, newss, 456 show_disk_name, NULL); 457 } 458 459 if (do_cpu) 460 print_cpu_hdr1(); 461 do_newline(); 462 463 /* 464 * Second line 465 */ 466 if (do_tty) 467 print_tty_hdr2(); 468 469 if (do_disk & DISK_NORMAL) { 470 (void) snapshot_walk(SNAP_IODEVS, NULL, newss, 471 show_disk_header, NULL); 472 } 473 474 if (do_cpu) 475 print_cpu_hdr2(); 476 do_newline(); 477 478 tohdr = REPRINT; 479 } 480 481 /* 482 * Write out the extended header centered over the core information. 483 */ 484 static void 485 write_core_header(void) 486 { 487 char *edev = "extended device statistics"; 488 uint_t lead_space_ct; 489 uint_t follow_space_ct; 490 size_t edevlen; 491 492 if (do_raw == 0) { 493 /* 494 * The things we do to look nice... 495 * 496 * Center the core output header. Make sure we have the 497 * right number of trailing spaces for follow-on headers 498 * (i.e., cpu and/or tty and/or errors). 499 */ 500 edevlen = strlen(edev); 501 lead_space_ct = dh_len - edevlen; 502 lead_space_ct /= 2; 503 if (lead_space_ct > 0) { 504 follow_space_ct = dh_len - (lead_space_ct + edevlen); 505 if (do_disk & DISK_ERRORS) 506 follow_space_ct -= DISK_ERROR_LEN; 507 if ((do_disk & DISK_EXTENDED) && do_conversions) 508 follow_space_ct -= DEVICE_LEN; 509 510 push_out("%1$*2$.*2$s%3$s%4$*5$.*5$s", one_blank, 511 lead_space_ct, edev, one_blank, follow_space_ct); 512 } else 513 push_out("%56s", edev); 514 } else 515 push_out(edev); 516 } 517 518 /* 519 * In extended mode headers, we don't want to reprint the header on 520 * signals as they are printed every time anyways. 521 */ 522 static void 523 printxhdr(void) 524 { 525 526 /* 527 * Vertical mode headers 528 */ 529 if (do_disk & DISK_EXTENDED) 530 setup(write_core_header); 531 if (do_disk & DISK_ERRORS) 532 setup(print_err_hdr); 533 534 if (do_conversions) { 535 setup(do_newline); 536 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) 537 setup(print_disk_header); 538 setup(do_newline); 539 } else { 540 if (do_tty) 541 setup(print_tty_hdr1); 542 if (do_cpu) 543 setup(print_cpu_hdr1); 544 setup(do_newline); 545 546 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) 547 setup(print_disk_header); 548 if (do_tty) 549 setup(print_tty_hdr2); 550 if (do_cpu) 551 setup(print_cpu_hdr2); 552 setup(do_newline); 553 } 554 } 555 556 /* 557 * Write out a line for this disk - note that show_disk writes out 558 * full lines or blocks for each selected disk. 559 */ 560 static void 561 show_disk(void *v1, void *v2, void *data) 562 { 563 uint32_t err_counters[NUMBER_OF_ERR_COUNTERS]; 564 boolean_t display_err_counters = do_disk & DISK_ERRORS; 565 struct iodev_snapshot *old = (struct iodev_snapshot *)v1; 566 struct iodev_snapshot *new = (struct iodev_snapshot *)v2; 567 int *count = (int *)data; 568 double rps, wps, tps, mtps, krps, kwps, kps, avw, avr, w_pct, r_pct; 569 double wserv, rserv, serv; 570 double iosize; /* kb/sec or MB/sec */ 571 double etime, hr_etime; 572 char *disk_name; 573 u_longlong_t ldeltas; 574 uint_t udeltas; 575 uint64_t t_delta; 576 uint64_t w_delta; 577 uint64_t r_delta; 578 int doit = 1; 579 uint_t toterrs; 580 char *fstr; 581 582 if (new == NULL) 583 return; 584 585 switch (show_disk_mode) { 586 case SHOW_FIRST_ONLY: 587 if (count != NULL && *count) 588 return; 589 break; 590 591 case SHOW_SECOND_ONWARDS: 592 if (count != NULL && !*count) { 593 (*count)++; 594 return; 595 } 596 break; 597 598 default: 599 break; 600 } 601 602 disk_name = do_conversions ? new->is_pretty : new->is_name; 603 disk_name = disk_name ? disk_name : new->is_name; 604 605 /* 606 * Only do if we want IO stats - Avoids errors traveling this 607 * section if that's all we want to see. 608 */ 609 if (do_disk & DISK_IO_MASK) { 610 if (old) { 611 t_delta = hrtime_delta(old->is_snaptime, 612 new->is_snaptime); 613 } else { 614 t_delta = hrtime_delta(new->is_crtime, 615 new->is_snaptime); 616 } 617 618 if (new->is_nr_children) { 619 if (new->is_type == IODEV_CONTROLLER) { 620 t_delta /= new->is_nr_children; 621 } else if ((new->is_type == IODEV_IOPATH_LT) || 622 (new->is_type == IODEV_IOPATH_LI)) { 623 /* synthetic path */ 624 if (!old) { 625 t_delta = new->is_crtime; 626 } 627 t_delta /= new->is_nr_children; 628 } 629 } 630 631 hr_etime = (double)t_delta; 632 if (hr_etime == 0.0) 633 hr_etime = (double)NANOSEC; 634 etime = hr_etime / (double)NANOSEC; 635 636 /* reads per second */ 637 udeltas = u32_delta(old ? old->is_stats.reads : 0, 638 new->is_stats.reads); 639 rps = (double)udeltas; 640 rps /= etime; 641 642 /* writes per second */ 643 udeltas = u32_delta(old ? old->is_stats.writes : 0, 644 new->is_stats.writes); 645 wps = (double)udeltas; 646 wps /= etime; 647 648 tps = rps + wps; 649 /* transactions per second */ 650 651 /* 652 * report throughput as either kb/sec or MB/sec 653 */ 654 655 if (!do_megabytes) 656 iosize = 1024.0; 657 else 658 iosize = 1048576.0; 659 660 ldeltas = ull_delta(old ? old->is_stats.nread : 0, 661 new->is_stats.nread); 662 if (ldeltas) { 663 krps = (double)ldeltas; 664 krps /= etime; 665 krps /= iosize; 666 } else 667 krps = 0.0; 668 669 ldeltas = ull_delta(old ? old->is_stats.nwritten : 0, 670 new->is_stats.nwritten); 671 if (ldeltas) { 672 kwps = (double)ldeltas; 673 kwps /= etime; 674 kwps /= iosize; 675 } else 676 kwps = 0.0; 677 678 /* 679 * Blocks transferred per second 680 */ 681 kps = krps + kwps; 682 683 /* 684 * Average number of wait transactions waiting 685 */ 686 w_delta = hrtime_delta((u_longlong_t) 687 (old ? old->is_stats.wlentime : 0), 688 new->is_stats.wlentime); 689 if (w_delta) { 690 avw = (double)w_delta; 691 avw /= hr_etime; 692 } else 693 avw = 0.0; 694 695 /* 696 * Average number of run transactions waiting 697 */ 698 r_delta = hrtime_delta(old ? old->is_stats.rlentime : 0, 699 new->is_stats.rlentime); 700 if (r_delta) { 701 avr = (double)r_delta; 702 avr /= hr_etime; 703 } else 704 avr = 0.0; 705 706 /* 707 * Average wait service time in milliseconds 708 */ 709 if (tps > 0.0 && (avw != 0.0 || avr != 0.0)) { 710 mtps = 1000.0 / tps; 711 if (avw != 0.0) 712 wserv = avw * mtps; 713 else 714 wserv = 0.0; 715 716 if (avr != 0.0) 717 rserv = avr * mtps; 718 else 719 rserv = 0.0; 720 serv = rserv + wserv; 721 } else { 722 rserv = 0.0; 723 wserv = 0.0; 724 serv = 0.0; 725 } 726 727 /* % of time there is a transaction waiting for service */ 728 t_delta = hrtime_delta(old ? old->is_stats.wtime : 0, 729 new->is_stats.wtime); 730 if (t_delta) { 731 w_pct = (double)t_delta; 732 w_pct /= hr_etime; 733 w_pct *= 100.0; 734 735 /* 736 * Average the wait queue utilization over the 737 * the controller's devices, if this is a controller. 738 */ 739 if (new->is_type == IODEV_CONTROLLER) 740 w_pct /= new->is_nr_children; 741 } else 742 w_pct = 0.0; 743 744 /* % of time there is a transaction running */ 745 t_delta = hrtime_delta(old ? old->is_stats.rtime : 0, 746 new->is_stats.rtime); 747 if (t_delta) { 748 r_pct = (double)t_delta; 749 r_pct /= hr_etime; 750 r_pct *= 100.0; 751 752 /* 753 * Average the percent busy over the controller's 754 * devices, if this is a controller. 755 */ 756 if (new->is_type == IODEV_CONTROLLER) 757 w_pct /= new->is_nr_children; 758 } else { 759 r_pct = 0.0; 760 } 761 762 /* % of time there is a transaction running */ 763 if (do_interval) { 764 rps *= etime; 765 wps *= etime; 766 tps *= etime; 767 krps *= etime; 768 kwps *= etime; 769 kps *= etime; 770 } 771 } 772 773 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) { 774 if ((!do_conversions) && ((suppress_zero == 0) || 775 ((do_disk & DISK_EXTENDED) == 0))) { 776 if (do_raw == 0) { 777 push_out("%-*.*s", 778 iodevs_nl, iodevs_nl, disk_name); 779 } else { 780 push_out(disk_name); 781 } 782 } 783 } 784 785 /* 786 * The error counters are read first (if asked for and if they are 787 * available). 788 */ 789 bzero(err_counters, sizeof (err_counters)); 790 toterrs = 0; 791 if (display_err_counters && (new->is_errors.ks_data != NULL)) { 792 kstat_named_t *knp; 793 int i; 794 795 knp = KSTAT_NAMED_PTR(&new->is_errors); 796 for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++) { 797 switch (knp[i].data_type) { 798 case KSTAT_DATA_ULONG: 799 case KSTAT_DATA_ULONGLONG: 800 err_counters[i] = knp[i].value.ui32; 801 toterrs += knp[i].value.ui32; 802 break; 803 default: 804 break; 805 } 806 } 807 } 808 809 switch (do_disk & DISK_IO_MASK) { 810 case DISK_OLD: 811 if (do_raw == 0) 812 fstr = "%3.0f %3.0f %4.0f "; 813 else 814 fstr = "%.0f,%.0f,%.0f"; 815 push_out(fstr, kps, tps, serv); 816 break; 817 case DISK_NEW: 818 if (do_raw == 0) 819 fstr = "%3.0f %3.0f %4.1f "; 820 else 821 fstr = "%.0f,%.0f,%.1f"; 822 push_out(fstr, rps, wps, r_pct); 823 break; 824 case DISK_EXTENDED: 825 if (suppress_zero) { 826 if (fzero(rps) && fzero(wps) && fzero(krps) && 827 fzero(kwps) && fzero(avw) && fzero(avr) && 828 fzero(serv) && fzero(w_pct) && fzero(r_pct) && 829 (toterrs == 0)) { 830 doit = 0; 831 display_err_counters = B_FALSE; 832 } else if (do_conversions == 0) { 833 if (do_raw == 0) { 834 push_out("%-*.*s", 835 iodevs_nl, iodevs_nl, disk_name); 836 } else { 837 push_out(disk_name); 838 } 839 } 840 } 841 if (doit) { 842 if (!do_conversions) { 843 if (do_raw == 0) { 844 fstr = " %6.1f %6.1f %6.1f %6.1f " 845 "%4.1f %4.1f %6.1f %3.0f " 846 "%3.0f "; 847 } else { 848 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f," 849 "%.1f,%.0f,%.0f"; 850 } 851 push_out(fstr, rps, wps, krps, kwps, avw, avr, 852 serv, w_pct, r_pct); 853 } else { 854 if (do_raw == 0) { 855 fstr = " %6.1f %6.1f %6.1f %6.1f " 856 "%4.1f %4.1f %6.1f %6.1f " 857 "%3.0f %3.0f "; 858 } else { 859 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f," 860 "%.1f,%.1f,%.0f,%.0f"; 861 } 862 push_out(fstr, rps, wps, krps, kwps, avw, avr, 863 wserv, rserv, w_pct, r_pct); 864 } 865 } 866 break; 867 } 868 869 if (display_err_counters) { 870 char *efstr; 871 int i; 872 873 if (do_raw == 0) { 874 if (do_disk == DISK_ERRORS) 875 push_out(two_blanks); 876 efstr = "%3u "; 877 } else { 878 efstr = "%u"; 879 } 880 881 for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++) 882 push_out(efstr, err_counters[i]); 883 884 push_out(efstr, toterrs); 885 } 886 887 if (suppress_zero == 0 || doit == 1) { 888 if ((do_disk & (DISK_EXTENDED | DISK_ERRORS)) && 889 do_conversions) { 890 push_out("%s", disk_name); 891 if (show_mountpts && new->is_dname) { 892 mnt_t *mount_pt; 893 char *lu; 894 char *dnlu; 895 char lub[SMALL_SCRATCH_BUFLEN]; 896 897 lu = strrchr(new->is_dname, '/'); 898 if (lu) { 899 /* only the part after a possible '/' */ 900 dnlu = strrchr(disk_name, '/'); 901 if (dnlu != NULL && 902 strcmp(dnlu, lu) == 0) 903 lu = new->is_dname; 904 else { 905 *lu = 0; 906 (void) strcpy(lub, 907 new->is_dname); 908 *lu = '/'; 909 (void) strcat(lub, "/"); 910 (void) strcat(lub, 911 disk_name); 912 lu = lub; 913 } 914 } else 915 lu = disk_name; 916 mount_pt = lookup_mntent_byname(lu); 917 if (mount_pt) { 918 if (do_raw == 0) 919 push_out(" (%s)", 920 mount_pt->mount_point); 921 else 922 push_out("(%s)", 923 mount_pt->mount_point); 924 } 925 } 926 } 927 } 928 929 if ((do_disk & PRINT_VERTICAL) && show_disk_mode != SHOW_FIRST_ONLY) 930 do_newline(); 931 932 if (count != NULL) 933 (*count)++; 934 } 935 936 static void 937 usage(void) 938 { 939 (void) fprintf(stderr, 940 "Usage: iostat [-cCdDeEiImMnpPrstxXYz] " 941 " [-l n] [-T d|u] [disk ...] [interval [count]]\n" 942 "\t\t-c: report percentage of time system has spent\n" 943 "\t\t\tin user/system/wait/idle mode\n" 944 "\t\t-C: report disk statistics by controller\n" 945 "\t\t-d: display disk Kb/sec, transfers/sec, avg. \n" 946 "\t\t\tservice time in milliseconds \n" 947 "\t\t-D: display disk reads/sec, writes/sec, \n" 948 "\t\t\tpercentage disk utilization \n" 949 "\t\t-e: report device error summary statistics\n" 950 "\t\t-E: report extended device error statistics\n" 951 "\t\t-i: show device IDs for -E output\n" 952 "\t\t-I: report the counts in each interval,\n" 953 "\t\t\tinstead of rates, where applicable\n" 954 "\t\t-l n: Limit the number of disks to n\n" 955 "\t\t-m: Display mount points (most useful with -p)\n" 956 "\t\t-M: Display data throughput in MB/sec " 957 "instead of Kb/sec\n" 958 "\t\t-n: convert device names to cXdYtZ format\n" 959 "\t\t-p: report per-partition disk statistics\n" 960 "\t\t-P: report per-partition disk statistics only,\n" 961 "\t\t\tno per-device disk statistics\n" 962 "\t\t-r: Display data in comma separated format\n" 963 "\t\t-s: Suppress state change messages\n" 964 "\t\t-T d|u Display a timestamp in date (d) or unix " 965 "time_t (u)\n" 966 "\t\t-t: display chars read/written to terminals\n" 967 "\t\t-x: display extended disk statistics\n" 968 "\t\t-X: display I/O path statistics\n" 969 "\t\t-Y: display I/O path (I/T/L) statistics\n" 970 "\t\t-z: Suppress entries with all zero values\n"); 971 exit(1); 972 } 973 974 /*ARGSUSED*/ 975 static void 976 show_disk_errors(void *v1, void *v2, void *d) 977 { 978 struct iodev_snapshot *disk = (struct iodev_snapshot *)v2; 979 kstat_named_t *knp; 980 size_t col; 981 int i, len; 982 char *dev_name; 983 984 if (disk->is_errors.ks_ndata == 0) 985 return; 986 if (disk->is_type == IODEV_CONTROLLER) 987 return; 988 989 dev_name = do_conversions ? disk->is_pretty : disk->is_name; 990 dev_name = dev_name ? dev_name : disk->is_name; 991 992 len = strlen(dev_name); 993 if (len > 20) 994 push_out("%s ", dev_name); 995 else if (len > 16) 996 push_out("%-20.20s ", dev_name); 997 else { 998 if (do_conversions) 999 push_out("%-16.16s ", dev_name); 1000 else 1001 push_out("%-9.9s ", dev_name); 1002 } 1003 col = 0; 1004 1005 knp = KSTAT_NAMED_PTR(&disk->is_errors); 1006 for (i = 0; i < disk->is_errors.ks_ndata; i++) { 1007 /* skip kstats that the driver did not kstat_named_init */ 1008 if (knp[i].name[0] == 0) 1009 continue; 1010 1011 col += strlen(knp[i].name); 1012 1013 switch (knp[i].data_type) { 1014 case KSTAT_DATA_CHAR: 1015 if ((strcmp(knp[i].name, "Serial No") == 0) && 1016 do_devid) { 1017 if (disk->is_devid) { 1018 push_out("Device Id: %s ", 1019 disk->is_devid); 1020 col += strlen(disk->is_devid); 1021 } else 1022 push_out("Device Id: "); 1023 } else { 1024 push_out("%s: %-.16s ", knp[i].name, 1025 &knp[i].value.c[0]); 1026 col += strlen(&knp[i].value.c[0]); 1027 } 1028 break; 1029 case KSTAT_DATA_ULONG: 1030 push_out("%s: %u ", knp[i].name, 1031 knp[i].value.ui32); 1032 col += 4; 1033 break; 1034 case KSTAT_DATA_ULONGLONG: 1035 if (strcmp(knp[i].name, "Size") == 0) { 1036 push_out("%s: %2.2fGB <%llu bytes>\n", 1037 knp[i].name, 1038 (float)knp[i].value.ui64 / 1039 DISK_GIGABYTE, 1040 knp[i].value.ui64); 1041 col = 0; 1042 break; 1043 } 1044 push_out("%s: %u ", knp[i].name, 1045 knp[i].value.ui32); 1046 col += 4; 1047 break; 1048 } 1049 if ((col >= 62) || (i == 2)) { 1050 do_newline(); 1051 col = 0; 1052 } 1053 } 1054 if (col > 0) { 1055 do_newline(); 1056 } 1057 do_newline(); 1058 } 1059 1060 void 1061 do_args(int argc, char **argv) 1062 { 1063 int c; 1064 int errflg = 0; 1065 extern char *optarg; 1066 extern int optind; 1067 1068 while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF) 1069 switch (c) { 1070 case 't': 1071 do_tty++; 1072 break; 1073 case 'd': 1074 do_disk |= DISK_OLD; 1075 break; 1076 case 'D': 1077 do_disk |= DISK_NEW; 1078 break; 1079 case 'x': 1080 do_disk |= DISK_EXTENDED; 1081 break; 1082 case 'X': 1083 if (do_disk & DISK_IOPATH_LTI) 1084 errflg++; /* -Y already used */ 1085 else 1086 do_disk |= DISK_IOPATH_LI; 1087 break; 1088 case 'Y': 1089 if (do_disk & DISK_IOPATH_LI) 1090 errflg++; /* -X already used */ 1091 else 1092 do_disk |= DISK_IOPATH_LTI; 1093 break; 1094 case 'C': 1095 do_controller++; 1096 break; 1097 case 'c': 1098 do_cpu++; 1099 break; 1100 case 'I': 1101 do_interval++; 1102 break; 1103 case 'p': 1104 do_partitions++; 1105 break; 1106 case 'P': 1107 do_partitions_only++; 1108 break; 1109 case 'n': 1110 do_conversions++; 1111 break; 1112 case 'M': 1113 do_megabytes++; 1114 break; 1115 case 'e': 1116 do_disk |= DISK_ERRORS; 1117 break; 1118 case 'E': 1119 do_disk |= DISK_EXTENDED_ERRORS; 1120 break; 1121 case 'i': 1122 do_devid = 1; 1123 break; 1124 case 's': 1125 suppress_state = 1; 1126 break; 1127 case 'z': 1128 suppress_zero = 1; 1129 break; 1130 case 'm': 1131 show_mountpts = 1; 1132 break; 1133 case 'T': 1134 if (optarg) { 1135 if (*optarg == 'u') 1136 timestamp_fmt = UDATE; 1137 else if (*optarg == 'd') 1138 timestamp_fmt = DDATE; 1139 else 1140 errflg++; 1141 } else { 1142 errflg++; 1143 } 1144 break; 1145 case 'r': 1146 do_raw = 1; 1147 break; 1148 case 'l': 1149 df.if_max_iodevs = safe_strtoi(optarg, "invalid limit"); 1150 if (df.if_max_iodevs < 1) 1151 usage(); 1152 break; 1153 case '?': 1154 errflg++; 1155 } 1156 1157 if ((do_disk & DISK_OLD) && (do_disk & DISK_NEW)) { 1158 (void) fprintf(stderr, "-d and -D are incompatible.\n"); 1159 usage(); 1160 } 1161 1162 if (errflg) { 1163 usage(); 1164 } 1165 1166 /* if no output classes explicity specified, use defaults */ 1167 if (do_tty == 0 && do_disk == 0 && do_cpu == 0) 1168 do_tty = do_cpu = 1, do_disk = DISK_OLD; 1169 1170 /* 1171 * multi-path options (-X, -Y) without a specific vertical 1172 * output format (-x, -e, -E) imply extended -x format 1173 */ 1174 if ((do_disk & (DISK_IOPATH_LI | DISK_IOPATH_LTI)) && 1175 !(do_disk & PRINT_VERTICAL)) 1176 do_disk |= DISK_EXTENDED; 1177 1178 /* 1179 * If conflicting options take the preferred 1180 * -D and -x result in -x 1181 * -d or -D and -e or -E gives only whatever -d or -D was specified 1182 */ 1183 if ((do_disk & DISK_EXTENDED) && (do_disk & DISK_NORMAL)) 1184 do_disk &= ~DISK_NORMAL; 1185 if ((do_disk & DISK_NORMAL) && (do_disk & DISK_ERROR_MASK)) 1186 do_disk &= ~DISK_ERROR_MASK; 1187 1188 /* nfs, tape, always shown */ 1189 df.if_allowed_types = IODEV_NFS | IODEV_TAPE; 1190 1191 /* 1192 * If limit == 0 then no command line limit was set, else if any of 1193 * the flags that cause unlimited disks were not set, 1194 * use the default of 4 1195 */ 1196 if (df.if_max_iodevs == 0) { 1197 df.if_max_iodevs = DEFAULT_LIMIT; 1198 df.if_skip_floppy = 1; 1199 if (do_disk & (DISK_EXTENDED | DISK_ERRORS | 1200 DISK_EXTENDED_ERRORS)) { 1201 df.if_max_iodevs = UNLIMITED_IODEVS; 1202 df.if_skip_floppy = 0; 1203 } 1204 } 1205 if (do_disk) { 1206 size_t count = 0; 1207 size_t i = optind; 1208 1209 while (i < argc && !isdigit(argv[i][0])) { 1210 count++; 1211 i++; 1212 } 1213 1214 /* 1215 * "Note: disks explicitly requested 1216 * are not subject to this disk limit" 1217 */ 1218 if ((count > df.if_max_iodevs) || 1219 (count && (df.if_max_iodevs == UNLIMITED_IODEVS))) 1220 df.if_max_iodevs = count; 1221 1222 df.if_names = safe_alloc(count * sizeof (char *)); 1223 (void) memset(df.if_names, 0, count * sizeof (char *)); 1224 1225 df.if_nr_names = 0; 1226 while (optind < argc && !isdigit(argv[optind][0])) 1227 df.if_names[df.if_nr_names++] = argv[optind++]; 1228 } 1229 if (optind < argc) { 1230 interval = safe_strtoi(argv[optind], "invalid interval"); 1231 if (interval < 1) 1232 fail(0, "invalid interval"); 1233 optind++; 1234 1235 if (optind < argc) { 1236 iter = safe_strtoi(argv[optind], "invalid count"); 1237 if (iter < 1) 1238 fail(0, "invalid count"); 1239 optind++; 1240 } 1241 } 1242 if (interval == 0) 1243 iter = 1; 1244 if (optind < argc) 1245 usage(); 1246 } 1247 1248 /* 1249 * Driver for doing the extended header formatting. Will produce 1250 * the function stack needed to output an extended header based 1251 * on the options selected. 1252 */ 1253 1254 void 1255 do_format(void) 1256 { 1257 char header[SMALL_SCRATCH_BUFLEN]; 1258 char ch; 1259 char iosz; 1260 const char *fstr; 1261 1262 disk_header[0] = 0; 1263 ch = (do_interval ? 'i' : 's'); 1264 iosz = (do_megabytes ? 'M' : 'k'); 1265 if (do_disk & DISK_ERRORS) { 1266 if (do_raw == 0) { 1267 (void) sprintf(header, "s/w h/w trn tot "); 1268 } else 1269 (void) sprintf(header, "s/w,h/w,trn,tot"); 1270 } else 1271 *header = NULL; 1272 switch (do_disk & DISK_IO_MASK) { 1273 case DISK_OLD: 1274 if (do_raw == 0) 1275 fstr = "%cp%c tp%c serv "; 1276 else 1277 fstr = "%cp%c,tp%c,serv"; 1278 (void) snprintf(disk_header, sizeof (disk_header), 1279 fstr, iosz, ch, ch); 1280 break; 1281 case DISK_NEW: 1282 if (do_raw == 0) 1283 fstr = "rp%c wp%c util "; 1284 else 1285 fstr = "%rp%c,wp%c,util"; 1286 (void) snprintf(disk_header, sizeof (disk_header), 1287 fstr, ch, ch); 1288 break; 1289 case DISK_EXTENDED: 1290 /* This is -x option */ 1291 if (!do_conversions) { 1292 /* without -n option */ 1293 if (do_raw == 0) { 1294 /* without -r option */ 1295 (void) snprintf(disk_header, 1296 sizeof (disk_header), 1297 "%-*.*s r/%c w/%c " 1298 "%cr/%c %cw/%c wait actv " 1299 "svc_t %%%%w %%%%b %s", 1300 iodevs_nl, iodevs_nl, "device", 1301 ch, ch, iosz, ch, iosz, ch, header); 1302 } else { 1303 /* with -r option */ 1304 (void) snprintf(disk_header, 1305 sizeof (disk_header), 1306 "device,r/%c,w/%c,%cr/%c,%cw/%c," 1307 "wait,actv,svc_t,%%%%w," 1308 "%%%%b,%s", 1309 ch, ch, iosz, ch, iosz, ch, header); 1310 } 1311 } else { 1312 /* with -n option */ 1313 if (do_raw == 0) { 1314 fstr = " r/%c w/%c %cr/%c " 1315 "%cw/%c wait actv wsvc_t asvc_t " 1316 "%%%%w %%%%b %sdevice"; 1317 } else { 1318 fstr = "r/%c,w/%c,%cr/%c,%cw/%c," 1319 "wait,actv,wsvc_t,asvc_t," 1320 "%%%%w,%%%%b,%sdevice"; 1321 } 1322 (void) snprintf(disk_header, 1323 sizeof (disk_header), 1324 fstr, ch, ch, iosz, ch, iosz, 1325 ch, header); 1326 } 1327 break; 1328 default: 1329 break; 1330 } 1331 1332 /* do DISK_ERRORS header (already added above for DISK_EXTENDED) */ 1333 if ((do_disk & DISK_ERRORS) && 1334 ((do_disk & DISK_IO_MASK) != DISK_EXTENDED)) { 1335 if (!do_conversions) { 1336 if (do_raw == 0) 1337 (void) snprintf(disk_header, 1338 sizeof (disk_header), "%-*.*s %s", 1339 iodevs_nl, iodevs_nl, "device", header); 1340 else 1341 (void) snprintf(disk_header, 1342 sizeof (disk_header), "device,%s", header); 1343 } else { 1344 if (do_raw == 0) { 1345 (void) snprintf(disk_header, 1346 sizeof (disk_header), 1347 " %sdevice", header); 1348 } else { 1349 (void) snprintf(disk_header, 1350 sizeof (disk_header), 1351 "%s,device", header); 1352 } 1353 } 1354 } else { 1355 /* 1356 * Need to subtract two characters for the % escape in 1357 * the string. 1358 */ 1359 dh_len = strlen(disk_header) - 2; 1360 } 1361 1362 /* 1363 * -n *and* (-E *or* -e *or* -x) 1364 */ 1365 if (do_conversions && (do_disk & PRINT_VERTICAL)) { 1366 if (do_tty) 1367 setup(print_tty_hdr1); 1368 if (do_cpu) 1369 setup(print_cpu_hdr1); 1370 if (do_tty || do_cpu) 1371 setup(do_newline); 1372 if (do_tty) 1373 setup(print_tty_hdr2); 1374 if (do_cpu) 1375 setup(print_cpu_hdr2); 1376 if (do_tty || do_cpu) 1377 setup(do_newline); 1378 if (do_tty) 1379 setup(print_tty_data); 1380 if (do_cpu) 1381 setup(print_cpu_data); 1382 if (do_tty || do_cpu) 1383 setup(do_newline); 1384 printxhdr(); 1385 1386 setup(show_all_disks); 1387 } else { 1388 /* 1389 * These unholy gymnastics are necessary to place CPU/tty 1390 * data to the right of the disks/errors for the first 1391 * line in vertical mode. 1392 */ 1393 if (do_disk & PRINT_VERTICAL) { 1394 printxhdr(); 1395 1396 setup(show_first_disk); 1397 if (do_tty) 1398 setup(print_tty_data); 1399 if (do_cpu) 1400 setup(print_cpu_data); 1401 setup(do_newline); 1402 1403 setup(show_other_disks); 1404 } else { 1405 setup(hdrout); 1406 if (do_tty) 1407 setup(print_tty_data); 1408 setup(show_all_disks); 1409 if (do_cpu) 1410 setup(print_cpu_data); 1411 } 1412 1413 setup(do_newline); 1414 } 1415 if (do_disk & DISK_EXTENDED_ERRORS) 1416 setup(disk_errors); 1417 } 1418 1419 /* 1420 * Add a new function to the list of functions 1421 * for this invocation. Once on the stack the 1422 * function is never removed nor does its place 1423 * change. 1424 */ 1425 void 1426 setup(void (*nfunc)(void)) 1427 { 1428 format_t *tmp; 1429 1430 tmp = safe_alloc(sizeof (format_t)); 1431 tmp->nfunc = nfunc; 1432 tmp->next = 0; 1433 if (formatter_end) 1434 formatter_end->next = tmp; 1435 else 1436 formatter_list = tmp; 1437 formatter_end = tmp; 1438 1439 } 1440 1441 /* 1442 * The functions after this comment are devoted to printing 1443 * various parts of the header. They are selected based on the 1444 * options provided when the program was invoked. The functions 1445 * are either directly invoked in printhdr() or are indirectly 1446 * invoked by being placed on the list of functions used when 1447 * extended headers are used. 1448 */ 1449 void 1450 print_tty_hdr1(void) 1451 { 1452 char *fstr; 1453 char *dstr; 1454 1455 if (do_raw == 0) { 1456 fstr = "%10.10s"; 1457 dstr = "tty "; 1458 } else { 1459 fstr = "%s"; 1460 dstr = "tty"; 1461 } 1462 push_out(fstr, dstr); 1463 } 1464 1465 void 1466 print_tty_hdr2(void) 1467 { 1468 if (do_raw == 0) 1469 push_out("%-10.10s", " tin tout"); 1470 else 1471 push_out("tin,tout"); 1472 } 1473 1474 void 1475 print_cpu_hdr1(void) 1476 { 1477 char *dstr; 1478 1479 if (do_raw == 0) 1480 dstr = " cpu"; 1481 else 1482 dstr = "cpu"; 1483 push_out(dstr); 1484 } 1485 1486 void 1487 print_cpu_hdr2(void) 1488 { 1489 char *dstr; 1490 1491 if (do_raw == 0) 1492 dstr = " us sy wt id"; 1493 else 1494 dstr = "us,sy,wt,id"; 1495 push_out(dstr); 1496 } 1497 1498 /* 1499 * Assumption is that tty data is always first - no need for raw mode leading 1500 * comma. 1501 */ 1502 void 1503 print_tty_data(void) 1504 { 1505 char *fstr; 1506 uint64_t deltas; 1507 double raw; 1508 double outch; 1509 kstat_t *oldks = NULL; 1510 1511 if (oldss) 1512 oldks = &oldss->s_sys.ss_agg_sys; 1513 1514 if (do_raw == 0) 1515 fstr = " %3.0f %4.0f "; 1516 else 1517 fstr = "%.0f,%.0f"; 1518 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "rawch"); 1519 raw = deltas; 1520 raw /= getime; 1521 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "outch"); 1522 outch = deltas; 1523 outch /= getime; 1524 push_out(fstr, raw, outch); 1525 } 1526 1527 /* 1528 * Write out CPU data 1529 */ 1530 void 1531 print_cpu_data(void) 1532 { 1533 char *fstr; 1534 uint64_t idle; 1535 uint64_t user; 1536 uint64_t kern; 1537 uint64_t wait; 1538 kstat_t *oldks = NULL; 1539 1540 if (oldss) 1541 oldks = &oldss->s_sys.ss_agg_sys; 1542 1543 if (do_raw == 0) 1544 fstr = " %2.0f %2.0f %2.0f %2.0f"; 1545 else 1546 fstr = "%.0f,%.0f,%.0f,%.0f"; 1547 1548 idle = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_idle"); 1549 user = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_user"); 1550 kern = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_kernel"); 1551 wait = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_wait"); 1552 push_out(fstr, user * percent, kern * percent, 1553 wait * percent, idle * percent); 1554 } 1555 1556 /* 1557 * Emit the appropriate header. 1558 */ 1559 void 1560 hdrout(void) 1561 { 1562 if (do_raw == 0) { 1563 if (--tohdr == 0) 1564 printhdr(0); 1565 } else if (hdr_out == 0) { 1566 printhdr(0); 1567 hdr_out = 1; 1568 } 1569 } 1570 1571 /* 1572 * Write out disk errors when -E is specified. 1573 */ 1574 void 1575 disk_errors(void) 1576 { 1577 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk_errors, NULL); 1578 } 1579 1580 void 1581 show_first_disk(void) 1582 { 1583 int count = 0; 1584 1585 show_disk_mode = SHOW_FIRST_ONLY; 1586 1587 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count); 1588 } 1589 1590 void 1591 show_other_disks(void) 1592 { 1593 int count = 0; 1594 1595 show_disk_mode = SHOW_SECOND_ONWARDS; 1596 1597 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count); 1598 } 1599 1600 void 1601 show_all_disks(void) 1602 { 1603 int count = 0; 1604 1605 show_disk_mode = SHOW_ALL; 1606 1607 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count); 1608 } 1609 1610 /* 1611 * Write a newline out and clear the lineout flag. 1612 */ 1613 static void 1614 do_newline(void) 1615 { 1616 if (lineout) { 1617 (void) putchar('\n'); 1618 lineout = 0; 1619 } 1620 } 1621 1622 /* 1623 * Generalized printf function that determines what extra 1624 * to print out if we're in raw mode. At this time we 1625 * don't care about errors. 1626 */ 1627 static void 1628 push_out(const char *message, ...) 1629 { 1630 va_list args; 1631 1632 va_start(args, message); 1633 if (do_raw && lineout == 1) 1634 (void) putchar(','); 1635 (void) vprintf(message, args); 1636 va_end(args); 1637 lineout = 1; 1638 } 1639 1640 /* 1641 * Emit the header string when -e is specified. 1642 */ 1643 static void 1644 print_err_hdr(void) 1645 { 1646 char obuf[SMALL_SCRATCH_BUFLEN]; 1647 1648 if (do_raw) { 1649 push_out("errors"); 1650 return; 1651 } 1652 1653 if (do_conversions == 0) { 1654 if (!(do_disk & DISK_EXTENDED)) { 1655 (void) snprintf(obuf, sizeof (obuf), 1656 "%11s", one_blank); 1657 push_out(obuf); 1658 } 1659 } else if (do_disk == DISK_ERRORS) 1660 push_out(two_blanks); 1661 else 1662 push_out(one_blank); 1663 push_out("---- errors --- "); 1664 } 1665 1666 /* 1667 * Emit the header string when -e is specified. 1668 */ 1669 static void 1670 print_disk_header(void) 1671 { 1672 push_out(disk_header); 1673 } 1674 1675 /* 1676 * No, UINTMAX_MAX isn't the right thing here since 1677 * it is #defined to be either INT32_MAX or INT64_MAX 1678 * depending on the whether _LP64 is defined. 1679 * 1680 * We want to handle the odd future case of having 1681 * ulonglong_t be more than 64 bits but we have 1682 * no nice #define MAX value we can drop in place 1683 * without having to change this code in the future. 1684 */ 1685 1686 u_longlong_t 1687 ull_delta(u_longlong_t old, u_longlong_t new) 1688 { 1689 if (new >= old) 1690 return (new - old); 1691 else 1692 return ((UINT64_MAX - old) + new + 1); 1693 } 1694 1695 /* 1696 * Take the difference of an unsigned 32 1697 * bit int attempting to cater for 1698 * overflow. 1699 */ 1700 uint_t 1701 u32_delta(uint_t old, uint_t new) 1702 { 1703 if (new >= old) 1704 return (new - old); 1705 else 1706 return ((UINT32_MAX - old) + new + 1); 1707 } 1708 1709 /* 1710 * This is exactly what is needed for standard iostat output, 1711 * but make sure to use it only for that 1712 */ 1713 #define EPSILON (0.1) 1714 static int 1715 fzero(double value) 1716 { 1717 return (value >= 0.0 && value < EPSILON); 1718 } 1719 1720 static int 1721 safe_strtoi(char const *val, char *errmsg) 1722 { 1723 char *end; 1724 long tmp; 1725 1726 errno = 0; 1727 tmp = strtol(val, &end, 10); 1728 if (*end != '\0' || errno) 1729 fail(0, "%s %s", errmsg, val); 1730 return ((int)tmp); 1731 } 1732