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