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