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