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 /* 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 int caught_cont = 0; 96 97 static char one_blank[] = " "; 98 static char two_blanks[] = " "; 99 100 /* 101 * count for number of lines to be emitted before a header is 102 * shown again. Only used for the basic format. 103 */ 104 static uint_t tohdr = 1; 105 106 /* 107 * If we're in raw format, have we printed a header? We only do it 108 * once for raw but we emit it every REPRINT lines in non-raw format. 109 * This applies only for the basic header. The extended header is 110 * done only once in both formats. 111 */ 112 static uint_t hdr_out; 113 114 /* 115 * Flags representing arguments from command line 116 */ 117 static uint_t do_tty; /* show tty info (-t) */ 118 static uint_t do_disk; /* show disk info per selected */ 119 /* format (-d, -D, -e, -E, -x -X -Y) */ 120 static uint_t do_cpu; /* show cpu info (-c) */ 121 static uint_t do_interval; /* do intervals (-I) */ 122 static int do_partitions; /* per-partition stats (-p) */ 123 static int do_partitions_only; /* per-partition stats only (-P) */ 124 /* no per-device stats for disks */ 125 static uint_t do_conversions; /* display disks as cXtYdZ (-n) */ 126 static uint_t do_megabytes; /* display data in MB/sec (-M) */ 127 static uint_t do_controller; /* display controller info (-C) */ 128 static uint_t do_raw; /* emit raw format (-r) */ 129 extern uint_t timestamp_fmt; /* timestamp each display (-T) */ 130 static uint_t do_devid; /* -E should show devid */ 131 132 /* 133 * Default number of disk drives to be displayed in basic format 134 */ 135 #define DEFAULT_LIMIT 4 136 137 struct iodev_filter df; 138 139 static uint_t suppress_state; /* skip state change messages */ 140 static uint_t suppress_zero; /* skip zero valued lines */ 141 static uint_t show_mountpts; /* show mount points */ 142 static int interval; /* interval (seconds) to output */ 143 static int iter; /* iterations from command line */ 144 145 #define SMALL_SCRATCH_BUFLEN MAXNAMELEN 146 147 static int iodevs_nl; /* name field width */ 148 #define IODEVS_NL_MIN 6 /* not too thin for "device" */ 149 #define IODEVS_NL_MAX 24 /* but keep full width under 80 */ 150 151 static char disk_header[132]; 152 static uint_t dh_len; /* disk header length for centering */ 153 static int lineout; /* data waiting to be printed? */ 154 155 static struct snapshot *newss; 156 static struct snapshot *oldss; 157 static double getime; /* elapsed time */ 158 static double percent; /* 100 / etime */ 159 160 /* 161 * List of functions to be called which will construct the desired output 162 */ 163 static format_t *formatter_list; 164 static format_t *formatter_end; 165 166 static u_longlong_t ull_delta(u_longlong_t, u_longlong_t); 167 static uint_t u32_delta(uint_t, uint_t); 168 static void setup(void (*nfunc)(void)); 169 static void print_tty_hdr1(void); 170 static void print_tty_hdr2(void); 171 static void print_cpu_hdr1(void); 172 static void print_cpu_hdr2(void); 173 static void print_tty_data(void); 174 static void print_cpu_data(void); 175 static void print_err_hdr(void); 176 static void print_disk_header(void); 177 static void hdrout(void); 178 static void disk_errors(void); 179 static void do_newline(void); 180 static void push_out(const char *, ...); 181 static void printhdr(int); 182 static void printxhdr(void); 183 static void usage(void); 184 static void do_args(int, char **); 185 static void do_format(void); 186 static void show_all_disks(void); 187 static void show_first_disk(void); 188 static void show_other_disks(void); 189 static void show_disk_errors(void *, void *, void *); 190 static void write_core_header(void); 191 static int fzero(double value); 192 static int safe_strtoi(char const *val, char *errmsg); 193 194 int 195 main(int argc, char **argv) 196 { 197 enum snapshot_types types = SNAP_SYSTEM; 198 kstat_ctl_t *kc; 199 long hz; 200 int forever; 201 hrtime_t start_n; 202 hrtime_t period_n; 203 204 (void) setlocale(LC_ALL, ""); 205 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 206 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 207 #endif 208 (void) textdomain(TEXT_DOMAIN); 209 210 do_args(argc, argv); 211 212 /* 213 * iostat historically showed CPU changes, even though 214 * it doesn't provide much useful information 215 */ 216 types |= SNAP_CPUS; 217 218 if (do_disk) 219 types |= SNAP_IODEVS; 220 221 if (do_disk && !do_partitions_only) 222 df.if_allowed_types |= IODEV_DISK; 223 if (do_disk & DISK_IOPATH_LI) { 224 df.if_allowed_types |= IODEV_IOPATH_LTI; 225 types |= SNAP_IOPATHS_LI; 226 } 227 if (do_disk & DISK_IOPATH_LTI) { 228 df.if_allowed_types |= IODEV_IOPATH_LTI; 229 types |= SNAP_IOPATHS_LTI; 230 } 231 if (do_disk & DISK_ERROR_MASK) 232 types |= SNAP_IODEV_ERRORS; 233 if (do_partitions || do_partitions_only) 234 df.if_allowed_types |= IODEV_PARTITION; 235 if (do_conversions) 236 types |= SNAP_IODEV_PRETTY; 237 if (do_devid) 238 types |= SNAP_IODEV_DEVID; 239 if (do_controller) { 240 if (!(do_disk & PRINT_VERTICAL) || 241 (do_disk & DISK_EXTENDED_ERRORS)) 242 fail(0, "-C can only be used with -e or -x."); 243 types |= SNAP_CONTROLLERS; 244 df.if_allowed_types |= IODEV_CONTROLLER; 245 } 246 247 hz = sysconf(_SC_CLK_TCK); 248 249 /* 250 * Undocumented behavior - sending a SIGCONT will result 251 * in a new header being emitted. Used only if we're not 252 * doing extended headers. This is a historical 253 * artifact. 254 */ 255 if (!(do_disk & PRINT_VERTICAL)) 256 (void) signal(SIGCONT, printhdr); 257 258 if (interval) 259 period_n = (hrtime_t)interval * NANOSEC; 260 261 kc = open_kstat(); 262 if (interval) 263 start_n = gethrtime(); 264 newss = acquire_snapshot(kc, types, &df); 265 266 /* compute width of "device" field */ 267 iodevs_nl = newss->s_iodevs_is_name_maxlen; 268 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ? 269 IODEVS_NL_MIN : iodevs_nl; 270 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ? 271 IODEVS_NL_MAX : iodevs_nl; 272 273 do_format(); 274 275 forever = (iter == 0); 276 do { 277 if (do_conversions && show_mountpts) 278 do_mnttab(); 279 280 if (do_tty || do_cpu) { 281 kstat_t *oldks; 282 oldks = oldss ? &oldss->s_sys.ss_agg_sys : NULL; 283 getime = cpu_ticks_delta(oldks, 284 &newss->s_sys.ss_agg_sys); 285 percent = (getime > 0.0) ? 100.0 / getime : 0.0; 286 getime = (getime / nr_active_cpus(newss)) / hz; 287 if (getime == 0.0) 288 getime = (double)interval; 289 if (getime == 0.0 || do_interval) 290 getime = 1.0; 291 } 292 293 if (formatter_list) { 294 format_t *tmp; 295 tmp = formatter_list; 296 while (tmp) { 297 (tmp->nfunc)(); 298 tmp = tmp->next; 299 } 300 (void) fflush(stdout); 301 } 302 303 /* only remaining/doing a single iteration, we are done */ 304 if (iter == 1) 305 continue; 306 307 if (interval > 0) 308 /* Have a kip */ 309 sleep_until(&start_n, period_n, forever, &caught_cont); 310 311 free_snapshot(oldss); 312 oldss = newss; 313 newss = acquire_snapshot(kc, types, &df); 314 iodevs_nl = (newss->s_iodevs_is_name_maxlen > iodevs_nl) ? 315 newss->s_iodevs_is_name_maxlen : iodevs_nl; 316 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ? 317 IODEVS_NL_MIN : iodevs_nl; 318 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ? 319 IODEVS_NL_MAX : iodevs_nl; 320 321 if (!suppress_state) 322 snapshot_report_changes(oldss, newss); 323 324 /* if config changed, show stats from boot */ 325 if (snapshot_has_changed(oldss, newss)) { 326 free_snapshot(oldss); 327 oldss = NULL; 328 } 329 330 } while (--iter); 331 332 free_snapshot(oldss); 333 free_snapshot(newss); 334 (void) kstat_close(kc); 335 free(df.if_names); 336 return (0); 337 } 338 339 /* 340 * Some magic numbers used in header formatting. 341 * 342 * DISK_LEN = length of either "kps tps serv" or "wps rps util" 343 * using 0 as the first position 344 * 345 * DISK_ERROR_LEN = length of "s/w h/w trn tot" with one space on 346 * either side. Does not use zero as first pos. 347 * 348 * DEVICE_LEN = length of "device" + 1 character. 349 */ 350 351 #define DISK_LEN 11 352 #define DISK_ERROR_LEN 16 353 #define DEVICE_LEN 7 354 355 /*ARGSUSED*/ 356 static void 357 show_disk_name(void *v1, void *v2, void *data) 358 { 359 struct iodev_snapshot *dev = (struct iodev_snapshot *)v2; 360 size_t slen; 361 char *name; 362 char fbuf[SMALL_SCRATCH_BUFLEN]; 363 364 if (dev == NULL) 365 return; 366 367 name = do_conversions ? dev->is_pretty : dev->is_name; 368 name = name ? name : dev->is_name; 369 370 if (!do_raw) { 371 uint_t width; 372 373 slen = strlen(name); 374 /* 375 * The length is less 376 * than the section 377 * which will be displayed 378 * on the next line. 379 * Center the entry. 380 */ 381 382 width = (DISK_LEN + 1)/2 + (slen / 2); 383 (void) snprintf(fbuf, sizeof (fbuf), 384 "%*s", width, name); 385 name = fbuf; 386 push_out("%-13.13s ", name); 387 } else { 388 push_out(name); 389 } 390 } 391 392 /*ARGSUSED*/ 393 static void 394 show_disk_header(void *v1, void *v2, void *data) 395 { 396 push_out(disk_header); 397 } 398 399 /* 400 * Write out a two line header. What is written out depends on the flags 401 * selected but in the worst case consists of a tty header, a disk header 402 * providing information for 4 disks and a cpu header. 403 * 404 * The tty header consists of the word "tty" on the first line above the 405 * words "tin tout" on the next line. If present the tty portion consumes 406 * the first 10 characters of each line since "tin tout" is surrounded 407 * by single spaces. 408 * 409 * Each of the disk sections is a 14 character "block" in which the name of 410 * the disk is centered in the first 12 characters of the first line. 411 * 412 * The cpu section is an 11 character block with "cpu" centered over the 413 * section. 414 * 415 * The worst case should look as follows: 416 * 417 * 0---------1--------2---------3---------4---------5---------6---------7------- 418 * tty sd0 sd1 sd2 sd3 cpu 419 * tin tout kps tps serv kps tps serv kps tps serv kps tps serv us sy wt id 420 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN 421 * 422 * When -D is specified, the disk header looks as follows (worst case): 423 * 424 * 0---------1--------2---------3---------4---------5---------6---------7------- 425 * tty sd0 sd1 sd2 sd3 cpu 426 * tin tout rps wps util rps wps util rps wps util rps wps util us sy wt id 427 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN 428 */ 429 static void 430 printhdr(int sig) 431 { 432 /* 433 * If we're here because a signal fired, reenable the 434 * signal. 435 */ 436 if (sig) 437 (void) signal(SIGCONT, printhdr); 438 if (sig == SIGCONT) 439 caught_cont = 1; 440 /* 441 * Horizontal mode headers 442 * 443 * First line 444 */ 445 if (do_tty) 446 print_tty_hdr1(); 447 448 if (do_disk & DISK_NORMAL) { 449 (void) snapshot_walk(SNAP_IODEVS, NULL, newss, 450 show_disk_name, NULL); 451 } 452 453 if (do_cpu) 454 print_cpu_hdr1(); 455 do_newline(); 456 457 /* 458 * Second line 459 */ 460 if (do_tty) 461 print_tty_hdr2(); 462 463 if (do_disk & DISK_NORMAL) { 464 (void) snapshot_walk(SNAP_IODEVS, NULL, newss, 465 show_disk_header, NULL); 466 } 467 468 if (do_cpu) 469 print_cpu_hdr2(); 470 do_newline(); 471 472 tohdr = REPRINT; 473 } 474 475 /* 476 * Write out the extended header centered over the core information. 477 */ 478 static void 479 write_core_header(void) 480 { 481 char *edev = "extended device statistics"; 482 uint_t lead_space_ct; 483 uint_t follow_space_ct; 484 size_t edevlen; 485 486 if (do_raw == 0) { 487 /* 488 * The things we do to look nice... 489 * 490 * Center the core output header. Make sure we have the 491 * right number of trailing spaces for follow-on headers 492 * (i.e., cpu and/or tty and/or errors). 493 */ 494 edevlen = strlen(edev); 495 lead_space_ct = dh_len - edevlen; 496 lead_space_ct /= 2; 497 if (lead_space_ct > 0) { 498 follow_space_ct = dh_len - (lead_space_ct + edevlen); 499 if (do_disk & DISK_ERRORS) 500 follow_space_ct -= DISK_ERROR_LEN; 501 if ((do_disk & DISK_EXTENDED) && do_conversions) 502 follow_space_ct -= DEVICE_LEN; 503 504 push_out("%1$*2$.*2$s%3$s%4$*5$.*5$s", one_blank, 505 lead_space_ct, edev, one_blank, follow_space_ct); 506 } else 507 push_out("%56s", edev); 508 } else 509 push_out(edev); 510 } 511 512 /* 513 * In extended mode headers, we don't want to reprint the header on 514 * signals as they are printed every time anyways. 515 */ 516 static void 517 printxhdr(void) 518 { 519 520 /* 521 * Vertical mode headers 522 */ 523 if (do_disk & DISK_EXTENDED) 524 setup(write_core_header); 525 if (do_disk & DISK_ERRORS) 526 setup(print_err_hdr); 527 528 if (do_conversions) { 529 setup(do_newline); 530 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) 531 setup(print_disk_header); 532 setup(do_newline); 533 } else { 534 if (do_tty) 535 setup(print_tty_hdr1); 536 if (do_cpu) 537 setup(print_cpu_hdr1); 538 setup(do_newline); 539 540 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) 541 setup(print_disk_header); 542 if (do_tty) 543 setup(print_tty_hdr2); 544 if (do_cpu) 545 setup(print_cpu_hdr2); 546 setup(do_newline); 547 } 548 } 549 550 /* 551 * Write out a line for this disk - note that show_disk writes out 552 * full lines or blocks for each selected disk. 553 */ 554 static void 555 show_disk(void *v1, void *v2, void *data) 556 { 557 struct iodev_snapshot *old = (struct iodev_snapshot *)v1; 558 struct iodev_snapshot *new = (struct iodev_snapshot *)v2; 559 int *count = (int *)data; 560 double rps, wps, tps, mtps, krps, kwps, kps, avw, avr, w_pct, r_pct; 561 double wserv, rserv, serv; 562 double iosize; /* kb/sec or MB/sec */ 563 double etime, hr_etime; 564 char *disk_name; 565 u_longlong_t ldeltas; 566 uint_t udeltas; 567 uint64_t t_delta; 568 uint64_t w_delta; 569 uint64_t r_delta; 570 int doit = 1; 571 int i; 572 uint_t toterrs; 573 char *fstr; 574 575 if (new == NULL) 576 return; 577 578 switch (show_disk_mode) { 579 case SHOW_FIRST_ONLY: 580 if (count != NULL && *count) 581 return; 582 break; 583 584 case SHOW_SECOND_ONWARDS: 585 if (count != NULL && !*count) { 586 (*count)++; 587 return; 588 } 589 break; 590 591 default: 592 break; 593 } 594 595 disk_name = do_conversions ? new->is_pretty : new->is_name; 596 disk_name = disk_name ? disk_name : new->is_name; 597 598 /* 599 * Only do if we want IO stats - Avoids errors traveling this 600 * section if that's all we want to see. 601 */ 602 if (do_disk & DISK_IO_MASK) { 603 if (old) { 604 t_delta = hrtime_delta(old->is_snaptime, 605 new->is_snaptime); 606 } else { 607 t_delta = hrtime_delta(new->is_crtime, 608 new->is_snaptime); 609 } 610 611 if (new->is_nr_children) { 612 if (new->is_type == IODEV_CONTROLLER) { 613 t_delta /= new->is_nr_children; 614 } else if ((new->is_type == IODEV_IOPATH_LT) || 615 (new->is_type == IODEV_IOPATH_LI)) { 616 /* synthetic path */ 617 if (!old) { 618 t_delta = new->is_crtime; 619 } 620 t_delta /= new->is_nr_children; 621 } 622 } 623 624 hr_etime = (double)t_delta; 625 if (hr_etime == 0.0) 626 hr_etime = (double)NANOSEC; 627 etime = hr_etime / (double)NANOSEC; 628 629 /* reads per second */ 630 udeltas = u32_delta(old ? old->is_stats.reads : 0, 631 new->is_stats.reads); 632 rps = (double)udeltas; 633 rps /= etime; 634 635 /* writes per second */ 636 udeltas = u32_delta(old ? old->is_stats.writes : 0, 637 new->is_stats.writes); 638 wps = (double)udeltas; 639 wps /= etime; 640 641 tps = rps + wps; 642 /* transactions per second */ 643 644 /* 645 * report throughput as either kb/sec or MB/sec 646 */ 647 648 if (!do_megabytes) 649 iosize = 1024.0; 650 else 651 iosize = 1048576.0; 652 653 ldeltas = ull_delta(old ? old->is_stats.nread : 0, 654 new->is_stats.nread); 655 if (ldeltas) { 656 krps = (double)ldeltas; 657 krps /= etime; 658 krps /= iosize; 659 } else 660 krps = 0.0; 661 662 ldeltas = ull_delta(old ? old->is_stats.nwritten : 0, 663 new->is_stats.nwritten); 664 if (ldeltas) { 665 kwps = (double)ldeltas; 666 kwps /= etime; 667 kwps /= iosize; 668 } else 669 kwps = 0.0; 670 671 /* 672 * Blocks transferred per second 673 */ 674 kps = krps + kwps; 675 676 /* 677 * Average number of wait transactions waiting 678 */ 679 w_delta = hrtime_delta((u_longlong_t) 680 (old ? old->is_stats.wlentime : 0), 681 new->is_stats.wlentime); 682 if (w_delta) { 683 avw = (double)w_delta; 684 avw /= hr_etime; 685 } else 686 avw = 0.0; 687 688 /* 689 * Average number of run transactions waiting 690 */ 691 r_delta = hrtime_delta(old ? old->is_stats.rlentime : 0, 692 new->is_stats.rlentime); 693 if (r_delta) { 694 avr = (double)r_delta; 695 avr /= hr_etime; 696 } else 697 avr = 0.0; 698 699 /* 700 * Average wait service time in milliseconds 701 */ 702 if (tps > 0.0 && (avw != 0.0 || avr != 0.0)) { 703 mtps = 1000.0 / tps; 704 if (avw != 0.0) 705 wserv = avw * mtps; 706 else 707 wserv = 0.0; 708 709 if (avr != 0.0) 710 rserv = avr * mtps; 711 else 712 rserv = 0.0; 713 serv = rserv + wserv; 714 } else { 715 rserv = 0.0; 716 wserv = 0.0; 717 serv = 0.0; 718 } 719 720 /* % of time there is a transaction waiting for service */ 721 t_delta = hrtime_delta(old ? old->is_stats.wtime : 0, 722 new->is_stats.wtime); 723 if (t_delta) { 724 w_pct = (double)t_delta; 725 w_pct /= hr_etime; 726 w_pct *= 100.0; 727 728 /* 729 * Average the wait queue utilization over the 730 * the controller's devices, if this is a controller. 731 */ 732 if (new->is_type == IODEV_CONTROLLER) 733 w_pct /= new->is_nr_children; 734 } else 735 w_pct = 0.0; 736 737 /* % of time there is a transaction running */ 738 t_delta = hrtime_delta(old ? old->is_stats.rtime : 0, 739 new->is_stats.rtime); 740 if (t_delta) { 741 r_pct = (double)t_delta; 742 r_pct /= hr_etime; 743 r_pct *= 100.0; 744 745 /* 746 * Average the percent busy over the controller's 747 * devices, if this is a controller. 748 */ 749 if (new->is_type == IODEV_CONTROLLER) 750 w_pct /= new->is_nr_children; 751 } else { 752 r_pct = 0.0; 753 } 754 755 /* % of time there is a transaction running */ 756 if (do_interval) { 757 rps *= etime; 758 wps *= etime; 759 tps *= etime; 760 krps *= etime; 761 kwps *= etime; 762 kps *= etime; 763 } 764 } 765 766 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) { 767 if ((!do_conversions) && ((suppress_zero == 0) || 768 ((do_disk & DISK_EXTENDED) == 0))) { 769 if (do_raw == 0) { 770 push_out("%-*.*s", 771 iodevs_nl, iodevs_nl, disk_name); 772 } else { 773 push_out(disk_name); 774 } 775 } 776 } 777 778 switch (do_disk & DISK_IO_MASK) { 779 case DISK_OLD: 780 if (do_raw == 0) 781 fstr = "%3.0f %3.0f %4.0f "; 782 else 783 fstr = "%.0f,%.0f,%.0f"; 784 push_out(fstr, kps, tps, serv); 785 break; 786 case DISK_NEW: 787 if (do_raw == 0) 788 fstr = "%3.0f %3.0f %4.1f "; 789 else 790 fstr = "%.0f,%.0f,%.1f"; 791 push_out(fstr, rps, wps, r_pct); 792 break; 793 case DISK_EXTENDED: 794 if (suppress_zero) { 795 if (fzero(rps) && fzero(wps) && fzero(krps) && 796 fzero(kwps) && fzero(avw) && fzero(avr) && 797 fzero(serv) && fzero(w_pct) && fzero(r_pct)) { 798 doit = 0; 799 } else if (do_conversions == 0) { 800 if (do_raw == 0) { 801 push_out("%-*.*s", 802 iodevs_nl, iodevs_nl, disk_name); 803 } else { 804 push_out(disk_name); 805 } 806 } 807 } 808 if (doit) { 809 if (!do_conversions) { 810 if (do_raw == 0) { 811 fstr = " %6.1f %6.1f %6.1f %6.1f " 812 "%4.1f %4.1f %6.1f %3.0f " 813 "%3.0f "; 814 } else { 815 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f," 816 "%.1f,%.0f,%.0f"; 817 } 818 push_out(fstr, rps, wps, krps, kwps, avw, avr, 819 serv, w_pct, r_pct); 820 } else { 821 if (do_raw == 0) { 822 fstr = " %6.1f %6.1f %6.1f %6.1f " 823 "%4.1f %4.1f %6.1f %6.1f " 824 "%3.0f %3.0f "; 825 } else { 826 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f," 827 "%.1f,%.1f,%.0f,%.0f"; 828 } 829 push_out(fstr, rps, wps, krps, kwps, avw, avr, 830 wserv, rserv, w_pct, r_pct); 831 } 832 } 833 break; 834 } 835 836 if (do_disk & DISK_ERRORS) { 837 if ((do_disk == DISK_ERRORS)) { 838 if (do_raw == 0) 839 push_out(two_blanks); 840 } 841 842 if (new->is_errors.ks_data) { 843 kstat_named_t *knp; 844 char *efstr; 845 846 if (do_raw == 0) 847 efstr = "%3u "; 848 else 849 efstr = "%u"; 850 toterrs = 0; 851 knp = KSTAT_NAMED_PTR(&new->is_errors); 852 for (i = 0; i < 3; i++) { 853 switch (knp[i].data_type) { 854 case KSTAT_DATA_ULONG: 855 push_out(efstr, 856 knp[i].value.ui32); 857 toterrs += knp[i].value.ui32; 858 break; 859 case KSTAT_DATA_ULONGLONG: 860 /* 861 * We're only set up to 862 * write out the low 863 * order 32-bits so 864 * just grab that. 865 */ 866 push_out(efstr, 867 knp[i].value.ui32); 868 toterrs += knp[i].value.ui32; 869 break; 870 default: 871 break; 872 } 873 } 874 push_out(efstr, toterrs); 875 } else { 876 if (do_raw == 0) 877 push_out(" 0 0 0 0 "); 878 else 879 push_out("0,0,0,0"); 880 } 881 882 } 883 884 if (suppress_zero == 0 || doit == 1) { 885 if ((do_disk & (DISK_EXTENDED | DISK_ERRORS)) && 886 do_conversions) { 887 push_out("%s", disk_name); 888 if (show_mountpts && new->is_dname) { 889 mnt_t *mount_pt; 890 char *lu; 891 char *dnlu; 892 char lub[SMALL_SCRATCH_BUFLEN]; 893 894 lu = strrchr(new->is_dname, '/'); 895 if (lu) { 896 /* only the part after a possible '/' */ 897 dnlu = strrchr(disk_name, '/'); 898 if (dnlu != NULL && 899 strcmp(dnlu, lu) == 0) 900 lu = new->is_dname; 901 else { 902 *lu = 0; 903 (void) strcpy(lub, 904 new->is_dname); 905 *lu = '/'; 906 (void) strcat(lub, "/"); 907 (void) strcat(lub, 908 disk_name); 909 lu = lub; 910 } 911 } else 912 lu = disk_name; 913 mount_pt = lookup_mntent_byname(lu); 914 if (mount_pt) { 915 if (do_raw == 0) 916 push_out(" (%s)", 917 mount_pt->mount_point); 918 else 919 push_out("(%s)", 920 mount_pt->mount_point); 921 } 922 } 923 } 924 } 925 926 if ((do_disk & PRINT_VERTICAL) && show_disk_mode != SHOW_FIRST_ONLY) 927 do_newline(); 928 929 if (count != NULL) 930 (*count)++; 931 } 932 933 static void 934 usage(void) 935 { 936 (void) fprintf(stderr, 937 "Usage: iostat [-cCdDeEiImMnpPrstxXYz] " 938 " [-l n] [-T d|u] [disk ...] [interval [count]]\n" 939 "\t\t-c: report percentage of time system has spent\n" 940 "\t\t\tin user/system/wait/idle mode\n" 941 "\t\t-C: report disk statistics by controller\n" 942 "\t\t-d: display disk Kb/sec, transfers/sec, avg. \n" 943 "\t\t\tservice time in milliseconds \n" 944 "\t\t-D: display disk reads/sec, writes/sec, \n" 945 "\t\t\tpercentage disk utilization \n" 946 "\t\t-e: report device error summary statistics\n" 947 "\t\t-E: report extended device error statistics\n" 948 "\t\t-i: show device IDs for -E output\n" 949 "\t\t-I: report the counts in each interval,\n" 950 "\t\t\tinstead of rates, where applicable\n" 951 "\t\t-l n: Limit the number of disks to n\n" 952 "\t\t-m: Display mount points (most useful with -p)\n" 953 "\t\t-M: Display data throughput in MB/sec " 954 "instead of Kb/sec\n" 955 "\t\t-n: convert device names to cXdYtZ format\n" 956 "\t\t-p: report per-partition disk statistics\n" 957 "\t\t-P: report per-partition disk statistics only,\n" 958 "\t\t\tno per-device disk statistics\n" 959 "\t\t-r: Display data in comma separated format\n" 960 "\t\t-s: Suppress state change messages\n" 961 "\t\t-T d|u Display a timestamp in date (d) or unix " 962 "time_t (u)\n" 963 "\t\t-t: display chars read/written to terminals\n" 964 "\t\t-x: display extended disk statistics\n" 965 "\t\t-X: display I/O path statistics\n" 966 "\t\t-Y: display I/O path (I/T/L) statistics\n" 967 "\t\t-z: Suppress entries with all zero values\n"); 968 exit(1); 969 } 970 971 /*ARGSUSED*/ 972 static void 973 show_disk_errors(void *v1, void *v2, void *d) 974 { 975 struct iodev_snapshot *disk = (struct iodev_snapshot *)v2; 976 kstat_named_t *knp; 977 size_t col; 978 int i, len; 979 char *dev_name; 980 981 if (disk->is_errors.ks_ndata == 0) 982 return; 983 if (disk->is_type == IODEV_CONTROLLER) 984 return; 985 986 dev_name = do_conversions ? disk->is_pretty : disk->is_name; 987 dev_name = dev_name ? dev_name : disk->is_name; 988 989 len = strlen(dev_name); 990 if (len > 20) 991 push_out("%s ", dev_name); 992 else if (len > 16) 993 push_out("%-20.20s ", dev_name); 994 else { 995 if (do_conversions) 996 push_out("%-16.16s ", dev_name); 997 else 998 push_out("%-9.9s ", dev_name); 999 } 1000 col = 0; 1001 1002 knp = KSTAT_NAMED_PTR(&disk->is_errors); 1003 for (i = 0; i < disk->is_errors.ks_ndata; i++) { 1004 /* skip kstats that the driver did not kstat_named_init */ 1005 if (knp[i].name[0] == 0) 1006 continue; 1007 1008 col += strlen(knp[i].name); 1009 1010 switch (knp[i].data_type) { 1011 case KSTAT_DATA_CHAR: 1012 if ((strcmp(knp[i].name, "Serial No") == 0) && 1013 do_devid) { 1014 if (disk->is_devid) { 1015 push_out("Device Id: %s ", 1016 disk->is_devid); 1017 col += strlen(disk->is_devid); 1018 } else 1019 push_out("Device Id: "); 1020 } else { 1021 push_out("%s: %-.16s ", knp[i].name, 1022 &knp[i].value.c[0]); 1023 col += strlen(&knp[i].value.c[0]); 1024 } 1025 break; 1026 case KSTAT_DATA_ULONG: 1027 push_out("%s: %u ", knp[i].name, 1028 knp[i].value.ui32); 1029 col += 4; 1030 break; 1031 case KSTAT_DATA_ULONGLONG: 1032 if (strcmp(knp[i].name, "Size") == 0) { 1033 push_out("%s: %2.2fGB <%llu bytes>\n", 1034 knp[i].name, 1035 (float)knp[i].value.ui64 / 1036 DISK_GIGABYTE, 1037 knp[i].value.ui64); 1038 col = 0; 1039 break; 1040 } 1041 push_out("%s: %u ", knp[i].name, 1042 knp[i].value.ui32); 1043 col += 4; 1044 break; 1045 } 1046 if ((col >= 62) || (i == 2)) { 1047 do_newline(); 1048 col = 0; 1049 } 1050 } 1051 if (col > 0) { 1052 do_newline(); 1053 } 1054 do_newline(); 1055 } 1056 1057 void 1058 do_args(int argc, char **argv) 1059 { 1060 int c; 1061 int errflg = 0; 1062 extern char *optarg; 1063 extern int optind; 1064 1065 while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF) 1066 switch (c) { 1067 case 't': 1068 do_tty++; 1069 break; 1070 case 'd': 1071 do_disk |= DISK_OLD; 1072 break; 1073 case 'D': 1074 do_disk |= DISK_NEW; 1075 break; 1076 case 'x': 1077 do_disk |= DISK_EXTENDED; 1078 break; 1079 case 'X': 1080 if (do_disk & DISK_IOPATH_LTI) 1081 errflg++; /* -Y already used */ 1082 else 1083 do_disk |= DISK_IOPATH_LI; 1084 break; 1085 case 'Y': 1086 if (do_disk & DISK_IOPATH_LI) 1087 errflg++; /* -X already used */ 1088 else 1089 do_disk |= DISK_IOPATH_LTI; 1090 break; 1091 case 'C': 1092 do_controller++; 1093 break; 1094 case 'c': 1095 do_cpu++; 1096 break; 1097 case 'I': 1098 do_interval++; 1099 break; 1100 case 'p': 1101 do_partitions++; 1102 break; 1103 case 'P': 1104 do_partitions_only++; 1105 break; 1106 case 'n': 1107 do_conversions++; 1108 break; 1109 case 'M': 1110 do_megabytes++; 1111 break; 1112 case 'e': 1113 do_disk |= DISK_ERRORS; 1114 break; 1115 case 'E': 1116 do_disk |= DISK_EXTENDED_ERRORS; 1117 break; 1118 case 'i': 1119 do_devid = 1; 1120 break; 1121 case 's': 1122 suppress_state = 1; 1123 break; 1124 case 'z': 1125 suppress_zero = 1; 1126 break; 1127 case 'm': 1128 show_mountpts = 1; 1129 break; 1130 case 'T': 1131 if (optarg) { 1132 if (*optarg == 'u') 1133 timestamp_fmt = UDATE; 1134 else if (*optarg == 'd') 1135 timestamp_fmt = DDATE; 1136 else 1137 errflg++; 1138 } else { 1139 errflg++; 1140 } 1141 break; 1142 case 'r': 1143 do_raw = 1; 1144 break; 1145 case 'l': 1146 df.if_max_iodevs = safe_strtoi(optarg, "invalid limit"); 1147 if (df.if_max_iodevs < 1) 1148 usage(); 1149 break; 1150 case '?': 1151 errflg++; 1152 } 1153 1154 if ((do_disk & DISK_OLD) && (do_disk & DISK_NEW)) { 1155 (void) fprintf(stderr, "-d and -D are incompatible.\n"); 1156 usage(); 1157 } 1158 1159 if (errflg) { 1160 usage(); 1161 } 1162 1163 /* if no output classes explicity specified, use defaults */ 1164 if (do_tty == 0 && do_disk == 0 && do_cpu == 0) 1165 do_tty = do_cpu = 1, do_disk = DISK_OLD; 1166 1167 /* 1168 * multi-path options (-X, -Y) without a specific vertical 1169 * output format (-x, -e, -E) imply extended -x format 1170 */ 1171 if ((do_disk & (DISK_IOPATH_LI | DISK_IOPATH_LTI)) && 1172 !(do_disk & PRINT_VERTICAL)) 1173 do_disk |= DISK_EXTENDED; 1174 1175 /* 1176 * If conflicting options take the preferred 1177 * -D and -x result in -x 1178 * -d or -D and -e or -E gives only whatever -d or -D was specified 1179 */ 1180 if ((do_disk & DISK_EXTENDED) && (do_disk & DISK_NORMAL)) 1181 do_disk &= ~DISK_NORMAL; 1182 if ((do_disk & DISK_NORMAL) && (do_disk & DISK_ERROR_MASK)) 1183 do_disk &= ~DISK_ERROR_MASK; 1184 1185 /* nfs, tape, always shown */ 1186 df.if_allowed_types = IODEV_NFS | IODEV_TAPE; 1187 1188 /* 1189 * If limit == 0 then no command line limit was set, else if any of 1190 * the flags that cause unlimited disks were not set, 1191 * use the default of 4 1192 */ 1193 if (df.if_max_iodevs == 0) { 1194 df.if_max_iodevs = DEFAULT_LIMIT; 1195 df.if_skip_floppy = 1; 1196 if (do_disk & (DISK_EXTENDED | DISK_ERRORS | 1197 DISK_EXTENDED_ERRORS)) { 1198 df.if_max_iodevs = UNLIMITED_IODEVS; 1199 df.if_skip_floppy = 0; 1200 } 1201 } 1202 if (do_disk) { 1203 size_t count = 0; 1204 size_t i = optind; 1205 1206 while (i < argc && !isdigit(argv[i][0])) { 1207 count++; 1208 i++; 1209 } 1210 1211 /* 1212 * "Note: disks explicitly requested 1213 * are not subject to this disk limit" 1214 */ 1215 if ((count > df.if_max_iodevs) || 1216 (count && (df.if_max_iodevs == UNLIMITED_IODEVS))) 1217 df.if_max_iodevs = count; 1218 1219 df.if_names = safe_alloc(count * sizeof (char *)); 1220 (void) memset(df.if_names, 0, count * sizeof (char *)); 1221 1222 df.if_nr_names = 0; 1223 while (optind < argc && !isdigit(argv[optind][0])) 1224 df.if_names[df.if_nr_names++] = argv[optind++]; 1225 } 1226 if (optind < argc) { 1227 interval = safe_strtoi(argv[optind], "invalid interval"); 1228 if (interval < 1) 1229 fail(0, "invalid interval"); 1230 optind++; 1231 1232 if (optind < argc) { 1233 iter = safe_strtoi(argv[optind], "invalid count"); 1234 if (iter < 1) 1235 fail(0, "invalid count"); 1236 optind++; 1237 } 1238 } 1239 if (interval == 0) 1240 iter = 1; 1241 if (optind < argc) 1242 usage(); 1243 } 1244 1245 /* 1246 * Driver for doing the extended header formatting. Will produce 1247 * the function stack needed to output an extended header based 1248 * on the options selected. 1249 */ 1250 1251 void 1252 do_format(void) 1253 { 1254 char header[SMALL_SCRATCH_BUFLEN]; 1255 char ch; 1256 char iosz; 1257 const char *fstr; 1258 1259 disk_header[0] = 0; 1260 ch = (do_interval ? 'i' : 's'); 1261 iosz = (do_megabytes ? 'M' : 'k'); 1262 if (do_disk & DISK_ERRORS) { 1263 if (do_raw == 0) { 1264 (void) sprintf(header, "s/w h/w trn tot "); 1265 } else 1266 (void) sprintf(header, "s/w,h/w,trn,tot"); 1267 } else 1268 *header = NULL; 1269 switch (do_disk & DISK_IO_MASK) { 1270 case DISK_OLD: 1271 if (do_raw == 0) 1272 fstr = "%cp%c tp%c serv "; 1273 else 1274 fstr = "%cp%c,tp%c,serv"; 1275 (void) snprintf(disk_header, sizeof (disk_header), 1276 fstr, iosz, ch, ch); 1277 break; 1278 case DISK_NEW: 1279 if (do_raw == 0) 1280 fstr = "rp%c wp%c util "; 1281 else 1282 fstr = "%rp%c,wp%c,util"; 1283 (void) snprintf(disk_header, sizeof (disk_header), 1284 fstr, ch, ch); 1285 break; 1286 case DISK_EXTENDED: 1287 /* This is -x option */ 1288 if (!do_conversions) { 1289 /* without -n option */ 1290 if (do_raw == 0) { 1291 /* without -r option */ 1292 (void) snprintf(disk_header, 1293 sizeof (disk_header), 1294 "%-*.*s r/%c w/%c " 1295 "%cr/%c %cw/%c wait actv " 1296 "svc_t %%%%w %%%%b %s", 1297 iodevs_nl, iodevs_nl, "device", 1298 ch, ch, iosz, ch, iosz, ch, header); 1299 } else { 1300 /* with -r option */ 1301 (void) snprintf(disk_header, 1302 sizeof (disk_header), 1303 "device,r/%c,w/%c,%cr/%c,%cw/%c," 1304 "wait,actv,svc_t,%%%%w," 1305 "%%%%b,%s", 1306 ch, ch, iosz, ch, iosz, ch, header); 1307 } 1308 } else { 1309 /* with -n option */ 1310 if (do_raw == 0) { 1311 fstr = " r/%c w/%c %cr/%c " 1312 "%cw/%c wait actv wsvc_t asvc_t " 1313 "%%%%w %%%%b %sdevice"; 1314 } else { 1315 fstr = "r/%c,w/%c,%cr/%c,%cw/%c," 1316 "wait,actv,wsvc_t,asvc_t," 1317 "%%%%w,%%%%b,%sdevice"; 1318 } 1319 (void) snprintf(disk_header, 1320 sizeof (disk_header), 1321 fstr, ch, ch, iosz, ch, iosz, 1322 ch, header); 1323 } 1324 break; 1325 default: 1326 break; 1327 } 1328 1329 /* do DISK_ERRORS header (already added above for DISK_EXTENDED) */ 1330 if ((do_disk & DISK_ERRORS) && 1331 ((do_disk & DISK_IO_MASK) != DISK_EXTENDED)) { 1332 if (!do_conversions) { 1333 if (do_raw == 0) 1334 (void) snprintf(disk_header, 1335 sizeof (disk_header), "%-*.*s %s", 1336 iodevs_nl, iodevs_nl, "device", header); 1337 else 1338 (void) snprintf(disk_header, 1339 sizeof (disk_header), "device,%s", header); 1340 } else { 1341 if (do_raw == 0) { 1342 (void) snprintf(disk_header, 1343 sizeof (disk_header), 1344 " %sdevice", header); 1345 } else { 1346 (void) snprintf(disk_header, 1347 sizeof (disk_header), 1348 "%s,device", header); 1349 } 1350 } 1351 } else { 1352 /* 1353 * Need to subtract two characters for the % escape in 1354 * the string. 1355 */ 1356 dh_len = strlen(disk_header) - 2; 1357 } 1358 1359 if (timestamp_fmt != NODATE) 1360 setup(print_timestamp); 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