1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30 /*
31 * Parts of this program are derived from the original FreeBSD iostat
32 * program:
33 */
34 /*-
35 * Copyright (c) 1986, 1991, 1993
36 * The Regents of the University of California. All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 4. Neither the name of the University nor the names of its contributors
47 * may be used to endorse or promote products derived from this software
48 * without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 */
62 /*
63 * Ideas for the new iostat statistics output modes taken from the NetBSD
64 * version of iostat:
65 */
66 /*
67 * Copyright (c) 1996 John M. Vinopal
68 * All rights reserved.
69 *
70 * Redistribution and use in source and binary forms, with or without
71 * modification, are permitted provided that the following conditions
72 * are met:
73 * 1. Redistributions of source code must retain the above copyright
74 * notice, this list of conditions and the following disclaimer.
75 * 2. Redistributions in binary form must reproduce the above copyright
76 * notice, this list of conditions and the following disclaimer in the
77 * documentation and/or other materials provided with the distribution.
78 * 3. All advertising materials mentioning features or use of this software
79 * must display the following acknowledgement:
80 * This product includes software developed for the NetBSD Project
81 * by John M. Vinopal.
82 * 4. The name of the author may not be used to endorse or promote products
83 * derived from this software without specific prior written permission.
84 *
85 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
86 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
87 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
88 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
89 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
90 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
91 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
92 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
93 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
94 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
95 * SUCH DAMAGE.
96 */
97
98
99 #include <sys/param.h>
100 #include <sys/errno.h>
101 #include <sys/resource.h>
102 #include <sys/sysctl.h>
103
104 #include <ctype.h>
105 #include <devstat.h>
106 #include <err.h>
107 #include <fcntl.h>
108 #include <inttypes.h>
109 #include <kvm.h>
110 #include <limits.h>
111 #include <math.h>
112 #include <nlist.h>
113 #include <signal.h>
114 #include <stdio.h>
115 #include <stdlib.h>
116 #include <string.h>
117 #include <termios.h>
118 #include <unistd.h>
119
120 static struct nlist namelist[] = {
121 #define X_TTY_NIN 0
122 { .n_name = "_tty_nin",
123 .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 },
124 #define X_TTY_NOUT 1
125 { .n_name = "_tty_nout",
126 .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 },
127 #define X_BOOTTIME 2
128 { .n_name = "_boottime",
129 .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 },
130 #define X_END 2
131 { .n_name = NULL,
132 .n_type = 0, .n_other = 0, .n_desc = 0, .n_value = 0 },
133 };
134
135 #define IOSTAT_DEFAULT_ROWS 20 /* Traditional default `wrows' */
136
137 static struct statinfo cur, last;
138 static int num_devices;
139 static struct device_selection *dev_select;
140 static int maxshowdevs;
141 static volatile sig_atomic_t headercount;
142 static volatile sig_atomic_t wresized; /* Tty resized, when non-zero. */
143 static volatile sig_atomic_t alarm_rang;
144 static volatile sig_atomic_t return_requested;
145 static unsigned short wrows; /* Current number of tty rows. */
146 static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
147 static int xflag = 0, zflag = 0;
148
149 /* local function declarations */
150 static void usage(void) __dead2;
151 static void needhdr(int signo);
152 static void needresize(int signo);
153 static void needreturn(int signo);
154 static void alarm_clock(int signo);
155 static void doresize(void);
156 static void phdr(void);
157 static void devstats(int perf_select, long double etime, int havelast);
158 static void cpustats(void);
159 static int readvar(kvm_t *kd, const char *name, int nlid, void *ptr,
160 size_t len);
161
162 static void
usage(void)163 usage(void)
164 {
165 /*
166 * We also support the following 'traditional' syntax:
167 * iostat [drives] [wait [count]]
168 * This isn't mentioned in the man page, or the usage statement,
169 * but it is supported.
170 */
171 fprintf(stderr, "usage: iostat [-CdhIKoTxz] [-c count] [-M core]"
172 " [-n devs] [-N system]\n"
173 "\t [-t type,if,pass] [-w wait] [drives]\n");
174 exit(1);
175 }
176
177 int
main(int argc,char ** argv)178 main(int argc, char **argv)
179 {
180 int c, i;
181 int hflag = 0, cflag = 0, wflag = 0, nflag = 0;
182 int count = 0, waittime = 0;
183 char *memf = NULL, *nlistf = NULL;
184 struct devstat_match *matches;
185 struct itimerval alarmspec;
186 int num_matches = 0;
187 char errbuf[_POSIX2_LINE_MAX];
188 kvm_t *kd = NULL;
189 long generation;
190 int num_devices_specified;
191 int num_selected, num_selections;
192 long select_generation;
193 char **specified_devices;
194 devstat_select_mode select_mode;
195 float f;
196 int havelast = 0;
197
198 matches = NULL;
199 maxshowdevs = 3;
200
201 while ((c = getopt(argc, argv, "Cc:dhIKM:N:n:oTt:w:xz")) != -1) {
202 switch (c) {
203 case 'C':
204 Cflag++;
205 break;
206 case 'c':
207 cflag++;
208 count = atoi(optarg);
209 if (count < 1)
210 errx(1, "count %d is < 1", count);
211 break;
212 case 'd':
213 dflag++;
214 break;
215 case 'h':
216 hflag++;
217 break;
218 case 'I':
219 Iflag++;
220 break;
221 case 'K':
222 Kflag++;
223 break;
224 case 'M':
225 memf = optarg;
226 break;
227 case 'N':
228 nlistf = optarg;
229 break;
230 case 'n':
231 nflag++;
232 maxshowdevs = atoi(optarg);
233 if (maxshowdevs < 0)
234 errx(1, "number of devices %d is < 0",
235 maxshowdevs);
236 break;
237 case 'o':
238 oflag++;
239 break;
240 case 'T':
241 Tflag++;
242 break;
243 case 't':
244 if (devstat_buildmatch(optarg, &matches,
245 &num_matches) != 0)
246 errx(1, "%s", devstat_errbuf);
247 break;
248 case 'w':
249 wflag++;
250 f = atof(optarg);
251 waittime = f * 1000;
252 if (waittime < 1)
253 errx(1, "wait time is < 1ms");
254 break;
255 case 'x':
256 xflag++;
257 break;
258 case 'z':
259 zflag++;
260 break;
261 default:
262 usage();
263 }
264 }
265
266 argc -= optind;
267 argv += optind;
268
269 if (nlistf != NULL || memf != NULL) {
270 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
271
272 if (kd == NULL)
273 errx(1, "kvm_openfiles: %s", errbuf);
274
275 if (kvm_nlist(kd, namelist) == -1)
276 errx(1, "kvm_nlist: %s", kvm_geterr(kd));
277 }
278
279 /*
280 * Make sure that the userland devstat version matches the kernel
281 * devstat version. If not, exit and print a message informing
282 * the user of his mistake.
283 */
284 if (devstat_checkversion(kd) < 0)
285 errx(1, "%s", devstat_errbuf);
286
287 /*
288 * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is
289 * greater than 0, they may be 0 or non-zero.
290 */
291 if (dflag == 0 && xflag == 0) {
292 Cflag = 1;
293 Tflag = 1;
294 }
295
296 /* find out how many devices we have */
297 if ((num_devices = devstat_getnumdevs(kd)) < 0)
298 err(1, "can't get number of devices");
299
300 /*
301 * Figure out how many devices we should display.
302 */
303 if (nflag == 0) {
304 if (xflag > 0)
305 maxshowdevs = num_devices;
306 else if (oflag > 0) {
307 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
308 maxshowdevs = 5;
309 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
310 maxshowdevs = 5;
311 else
312 maxshowdevs = 4;
313 } else {
314 if ((dflag > 0) && (Cflag == 0))
315 maxshowdevs = 4;
316 else
317 maxshowdevs = 3;
318 }
319 }
320
321 cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
322 if (cur.dinfo == NULL)
323 err(1, "calloc failed");
324
325 last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
326 if (last.dinfo == NULL)
327 err(1, "calloc failed");
328
329 /*
330 * Grab all the devices. We don't look to see if the list has
331 * changed here, since it almost certainly has. We only look for
332 * errors.
333 */
334 if (devstat_getdevs(kd, &cur) == -1)
335 errx(1, "%s", devstat_errbuf);
336
337 num_devices = cur.dinfo->numdevs;
338 generation = cur.dinfo->generation;
339
340 /*
341 * If the user specified any devices on the command line, see if
342 * they are in the list of devices we have now.
343 */
344 specified_devices = (char **)malloc(sizeof(char *));
345 if (specified_devices == NULL)
346 err(1, "malloc failed");
347
348 for (num_devices_specified = 0; *argv; ++argv) {
349 if (isdigit(**argv))
350 break;
351 num_devices_specified++;
352 specified_devices = (char **)realloc(specified_devices,
353 sizeof(char *) *
354 num_devices_specified);
355 if (specified_devices == NULL)
356 err(1, "realloc failed");
357
358 specified_devices[num_devices_specified - 1] = *argv;
359
360 }
361 if (nflag == 0 && maxshowdevs < num_devices_specified)
362 maxshowdevs = num_devices_specified;
363
364 dev_select = NULL;
365
366 if ((num_devices_specified == 0) && (num_matches == 0))
367 select_mode = DS_SELECT_ADD;
368 else
369 select_mode = DS_SELECT_ONLY;
370
371 /*
372 * At this point, selectdevs will almost surely indicate that the
373 * device list has changed, so we don't look for return values of 0
374 * or 1. If we get back -1, though, there is an error.
375 */
376 if (devstat_selectdevs(&dev_select, &num_selected,
377 &num_selections, &select_generation, generation,
378 cur.dinfo->devices, num_devices, matches,
379 num_matches, specified_devices,
380 num_devices_specified, select_mode, maxshowdevs,
381 hflag) == -1)
382 errx(1, "%s", devstat_errbuf);
383
384 /*
385 * Look for the traditional wait time and count arguments.
386 */
387 if (*argv) {
388 f = atof(*argv);
389 waittime = f * 1000;
390
391 /* Let the user know he goofed, but keep going anyway */
392 if (wflag != 0)
393 warnx("discarding previous wait interval, using"
394 " %g instead", waittime / 1000.0);
395 wflag++;
396
397 if (*++argv) {
398 count = atoi(*argv);
399 if (cflag != 0)
400 warnx("discarding previous count, using %d"
401 " instead", count);
402 cflag++;
403 } else
404 count = -1;
405 }
406
407 /*
408 * If the user specified a count, but not an interval, we default
409 * to an interval of 1 second.
410 */
411 if ((wflag == 0) && (cflag > 0))
412 waittime = 1 * 1000;
413
414 /*
415 * If the user specified a wait time, but not a count, we want to
416 * go on ad infinitum. This can be redundant if the user uses the
417 * traditional method of specifying the wait, since in that case we
418 * already set count = -1 above. Oh well.
419 */
420 if ((wflag > 0) && (cflag == 0))
421 count = -1;
422
423 bzero(cur.cp_time, sizeof(cur.cp_time));
424 cur.tk_nout = 0;
425 cur.tk_nin = 0;
426
427 /*
428 * Set the snap time to the system boot time (ie: zero), so the
429 * stats are calculated since system boot.
430 */
431 cur.snap_time = 0;
432
433 /*
434 * If the user stops the program (control-Z) and then resumes it,
435 * print out the header again.
436 */
437 (void)signal(SIGCONT, needhdr);
438
439 /*
440 * If our standard output is a tty, then install a SIGWINCH handler
441 * and set wresized so that our first iteration through the main
442 * iostat loop will peek at the terminal's current rows to find out
443 * how many lines can fit in a screenful of output.
444 */
445 if (isatty(fileno(stdout)) != 0) {
446 wresized = 1;
447 (void)signal(SIGWINCH, needresize);
448 } else {
449 wresized = 0;
450 wrows = IOSTAT_DEFAULT_ROWS;
451 }
452
453 /*
454 * Register a SIGINT handler so that we can print out final statistics
455 * when we get that signal
456 */
457 (void)signal(SIGINT, needreturn);
458
459 /*
460 * Register a SIGALRM handler to implement sleeps if the user uses the
461 * -c or -w options
462 */
463 (void)signal(SIGALRM, alarm_clock);
464 alarmspec.it_interval.tv_sec = waittime / 1000;
465 alarmspec.it_interval.tv_usec = 1000 * (waittime % 1000);
466 alarmspec.it_value.tv_sec = waittime / 1000;
467 alarmspec.it_value.tv_usec = 1000 * (waittime % 1000);
468 setitimer(ITIMER_REAL, &alarmspec, NULL);
469
470 for (headercount = 1;;) {
471 struct devinfo *tmp_dinfo;
472 long tmp;
473 long double etime;
474 sigset_t sigmask, oldsigmask;
475
476 if (Tflag > 0) {
477 if ((readvar(kd, "kern.tty_nin", X_TTY_NIN, &cur.tk_nin,
478 sizeof(cur.tk_nin)) != 0)
479 || (readvar(kd, "kern.tty_nout", X_TTY_NOUT,
480 &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
481 Tflag = 0;
482 warnx("disabling TTY statistics");
483 }
484 }
485
486 if (Cflag > 0) {
487 if (kd == NULL) {
488 if (readvar(kd, "kern.cp_time", 0,
489 &cur.cp_time, sizeof(cur.cp_time)) != 0)
490 Cflag = 0;
491 } else {
492 if (kvm_getcptime(kd, cur.cp_time) < 0) {
493 warnx("kvm_getcptime: %s",
494 kvm_geterr(kd));
495 Cflag = 0;
496 }
497 }
498 if (Cflag == 0)
499 warnx("disabling CPU time statistics");
500 }
501
502 if (!--headercount) {
503 phdr();
504 if (wresized != 0)
505 doresize();
506 headercount = wrows;
507 }
508
509 tmp_dinfo = last.dinfo;
510 last.dinfo = cur.dinfo;
511 cur.dinfo = tmp_dinfo;
512
513 last.snap_time = cur.snap_time;
514
515 /*
516 * Here what we want to do is refresh our device stats.
517 * devstat_getdevs() returns 1 when the device list has changed.
518 * If the device list has changed, we want to go through
519 * the selection process again, in case a device that we
520 * were previously displaying has gone away.
521 */
522 switch (devstat_getdevs(kd, &cur)) {
523 case -1:
524 errx(1, "%s", devstat_errbuf);
525 break;
526 case 1: {
527 int retval;
528
529 num_devices = cur.dinfo->numdevs;
530 generation = cur.dinfo->generation;
531 retval = devstat_selectdevs(&dev_select, &num_selected,
532 &num_selections,
533 &select_generation,
534 generation,
535 cur.dinfo->devices,
536 num_devices, matches,
537 num_matches,
538 specified_devices,
539 num_devices_specified,
540 select_mode, maxshowdevs,
541 hflag);
542 switch(retval) {
543 case -1:
544 errx(1, "%s", devstat_errbuf);
545 break;
546 case 1:
547 phdr();
548 if (wresized != 0)
549 doresize();
550 headercount = wrows;
551 break;
552 default:
553 break;
554 }
555 break;
556 }
557 default:
558 break;
559 }
560
561 /*
562 * We only want to re-select devices if we're in 'top'
563 * mode. This is the only mode where the devices selected
564 * could actually change.
565 */
566 if (hflag > 0) {
567 int retval;
568 retval = devstat_selectdevs(&dev_select, &num_selected,
569 &num_selections,
570 &select_generation,
571 generation,
572 cur.dinfo->devices,
573 num_devices, matches,
574 num_matches,
575 specified_devices,
576 num_devices_specified,
577 select_mode, maxshowdevs,
578 hflag);
579 switch(retval) {
580 case -1:
581 errx(1,"%s", devstat_errbuf);
582 break;
583 case 1:
584 phdr();
585 if (wresized != 0)
586 doresize();
587 headercount = wrows;
588 break;
589 default:
590 break;
591 }
592 }
593
594 if (Tflag > 0) {
595 tmp = cur.tk_nin;
596 cur.tk_nin -= last.tk_nin;
597 last.tk_nin = tmp;
598 tmp = cur.tk_nout;
599 cur.tk_nout -= last.tk_nout;
600 last.tk_nout = tmp;
601 }
602
603 etime = cur.snap_time - last.snap_time;
604
605 if (etime == 0.0)
606 etime = 1.0;
607
608 for (i = 0; i < CPUSTATES; i++) {
609 tmp = cur.cp_time[i];
610 cur.cp_time[i] -= last.cp_time[i];
611 last.cp_time[i] = tmp;
612 }
613
614 if (xflag == 0 && Tflag > 0)
615 printf("%4.0Lf %5.0Lf ", cur.tk_nin / etime,
616 cur.tk_nout / etime);
617
618 devstats(hflag, etime, havelast);
619
620 if (xflag == 0) {
621 if (Cflag > 0)
622 cpustats();
623
624 printf("\n");
625 }
626 fflush(stdout);
627
628 if ((count >= 0 && --count <= 0) || return_requested)
629 break;
630
631 /*
632 * Use sigsuspend to safely sleep until either signal is
633 * received
634 */
635 alarm_rang = 0;
636 sigemptyset(&sigmask);
637 sigaddset(&sigmask, SIGINT);
638 sigaddset(&sigmask, SIGALRM);
639 sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask);
640 while (! (alarm_rang || return_requested) ) {
641 sigsuspend(&oldsigmask);
642 }
643 sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
644
645 havelast = 1;
646 }
647
648 exit(0);
649 }
650
651 /*
652 * Force a header to be prepended to the next output.
653 */
654 void
needhdr(int signo __unused)655 needhdr(int signo __unused)
656 {
657
658 headercount = 1;
659 }
660
661 /*
662 * When the terminal is resized, force an update of the maximum number of rows
663 * printed between each header repetition. Then force a new header to be
664 * prepended to the next output.
665 */
666 void
needresize(int signo __unused)667 needresize(int signo __unused)
668 {
669
670 wresized = 1;
671 headercount = 1;
672 }
673
674 /*
675 * Record the alarm so the main loop can break its sleep
676 */
677 void
alarm_clock(int signo __unused)678 alarm_clock(int signo __unused)
679 {
680 alarm_rang = 1;
681 }
682
683 /*
684 * Request that the main loop exit soon
685 */
686 void
needreturn(int signo __unused)687 needreturn(int signo __unused)
688 {
689 return_requested = 1;
690 }
691
692 /*
693 * Update the global `wrows' count of terminal rows.
694 */
695 void
doresize(void)696 doresize(void)
697 {
698 int status;
699 struct winsize w;
700
701 for (;;) {
702 status = ioctl(fileno(stdout), TIOCGWINSZ, &w);
703 if (status == -1 && errno == EINTR)
704 continue;
705 else if (status == -1)
706 err(1, "ioctl");
707 if (w.ws_row > 3)
708 wrows = w.ws_row - 3;
709 else
710 wrows = IOSTAT_DEFAULT_ROWS;
711 break;
712 }
713
714 /*
715 * Inhibit doresize() calls until we are rescheduled by SIGWINCH.
716 */
717 wresized = 0;
718 }
719
720 static void
phdr(void)721 phdr(void)
722 {
723 int i, printed;
724 char devbuf[256];
725
726 /*
727 * If xflag is set, we need a per-loop header, not a page header, so
728 * just return. We'll print the header in devstats().
729 */
730 if (xflag > 0)
731 return;
732
733 if (Tflag > 0)
734 (void)printf(" tty ");
735 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
736 int di;
737 if ((dev_select[i].selected != 0)
738 && (dev_select[i].selected <= maxshowdevs)) {
739 di = dev_select[i].position;
740 snprintf(devbuf, sizeof(devbuf), "%s%d",
741 cur.dinfo->devices[di].device_name,
742 cur.dinfo->devices[di].unit_number);
743 if (oflag > 0)
744 (void)printf("%13.6s ", devbuf);
745 else
746 printf("%16.6s ", devbuf);
747 printed++;
748 }
749 }
750 if (Cflag > 0)
751 (void)printf(" cpu\n");
752 else
753 (void)printf("\n");
754
755 if (Tflag > 0)
756 (void)printf(" tin tout ");
757
758 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
759 if ((dev_select[i].selected != 0)
760 && (dev_select[i].selected <= maxshowdevs)) {
761 if (oflag > 0) {
762 if (Iflag == 0)
763 (void)printf(" sps tps msps ");
764 else
765 (void)printf(" blk xfr msps ");
766 } else {
767 if (Iflag == 0)
768 printf("KB/t tps MB/s ");
769 else
770 printf("KB/t xfrs MB ");
771 }
772 printed++;
773 }
774 }
775 if (Cflag > 0)
776 (void)printf(" us ni sy in id\n");
777 else
778 printf("\n");
779
780 }
781
782 static void
devstats(int perf_select,long double etime,int havelast)783 devstats(int perf_select, long double etime, int havelast)
784 {
785 int dn;
786 long double transfers_per_second, transfers_per_second_read;
787 long double transfers_per_second_write;
788 long double kb_per_transfer, mb_per_second, mb_per_second_read;
789 long double mb_per_second_write;
790 u_int64_t total_bytes, total_transfers, total_blocks;
791 u_int64_t total_bytes_read, total_transfers_read;
792 u_int64_t total_bytes_write, total_transfers_write;
793 long double busy_pct, busy_time;
794 u_int64_t queue_len;
795 long double total_mb, blocks_per_second, total_duration;
796 long double ms_per_other, ms_per_read, ms_per_write, ms_per_transaction;
797 int firstline = 1;
798 char *devicename;
799
800 if (xflag > 0) {
801 if (Cflag > 0) {
802 printf(" cpu\n");
803 printf(" us ni sy in id\n");
804 cpustats();
805 printf("\n");
806 }
807 printf(" extended device statistics ");
808 if (Tflag > 0)
809 printf(" tty ");
810 printf("\n");
811 if (Iflag == 0) {
812 printf("device r/s w/s kr/s kw/s "
813 " ms/r ms/w ms/o ms/t qlen %%b ");
814 } else {
815 printf("device r/i w/i kr/i"
816 " kw/i qlen tsvc_t/i sb/i ");
817 }
818 if (Tflag > 0)
819 printf("tin tout ");
820 printf("\n");
821 }
822
823 for (dn = 0; dn < num_devices; dn++) {
824 int di;
825
826 if (((perf_select == 0) && (dev_select[dn].selected == 0))
827 || (dev_select[dn].selected > maxshowdevs))
828 continue;
829
830 di = dev_select[dn].position;
831
832 if (devstat_compute_statistics(&cur.dinfo->devices[di],
833 havelast ? &last.dinfo->devices[di] : NULL, etime,
834 DSM_TOTAL_BYTES, &total_bytes,
835 DSM_TOTAL_BYTES_READ, &total_bytes_read,
836 DSM_TOTAL_BYTES_WRITE, &total_bytes_write,
837 DSM_TOTAL_TRANSFERS, &total_transfers,
838 DSM_TOTAL_TRANSFERS_READ, &total_transfers_read,
839 DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write,
840 DSM_TOTAL_BLOCKS, &total_blocks,
841 DSM_KB_PER_TRANSFER, &kb_per_transfer,
842 DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
843 DSM_TRANSFERS_PER_SECOND_READ, &transfers_per_second_read,
844 DSM_TRANSFERS_PER_SECOND_WRITE, &transfers_per_second_write,
845 DSM_MB_PER_SECOND, &mb_per_second,
846 DSM_MB_PER_SECOND_READ, &mb_per_second_read,
847 DSM_MB_PER_SECOND_WRITE, &mb_per_second_write,
848 DSM_BLOCKS_PER_SECOND, &blocks_per_second,
849 DSM_MS_PER_TRANSACTION, &ms_per_transaction,
850 DSM_MS_PER_TRANSACTION_READ, &ms_per_read,
851 DSM_MS_PER_TRANSACTION_WRITE, &ms_per_write,
852 DSM_MS_PER_TRANSACTION_OTHER, &ms_per_other,
853 DSM_BUSY_PCT, &busy_pct,
854 DSM_QUEUE_LENGTH, &queue_len,
855 DSM_TOTAL_DURATION, &total_duration,
856 DSM_TOTAL_BUSY_TIME, &busy_time,
857 DSM_NONE) != 0)
858 errx(1, "%s", devstat_errbuf);
859
860 if (perf_select != 0) {
861 dev_select[dn].bytes = total_bytes;
862 if ((dev_select[dn].selected == 0)
863 || (dev_select[dn].selected > maxshowdevs))
864 continue;
865 }
866
867 if (Kflag > 0 || xflag > 0) {
868 int block_size = cur.dinfo->devices[di].block_size;
869 total_blocks = total_blocks * (block_size ?
870 block_size : 512) / 1024;
871 }
872
873 if (xflag > 0) {
874 if (asprintf(&devicename, "%s%d",
875 cur.dinfo->devices[di].device_name,
876 cur.dinfo->devices[di].unit_number) == -1)
877 err(1, "asprintf");
878 /*
879 * If zflag is set, skip any devices with zero I/O.
880 */
881 if (zflag == 0 || transfers_per_second_read > 0.05 ||
882 transfers_per_second_write > 0.05 ||
883 mb_per_second_read > ((long double).0005)/1024 ||
884 mb_per_second_write > ((long double).0005)/1024 ||
885 busy_pct > 0.5) {
886 if (Iflag == 0)
887 printf("%-8.8s %7.0Lf %7.0Lf %8.1Lf "
888 "%8.1Lf %5.0Lf %5.0Lf %5.0Lf %5.0Lf"
889 " %4" PRIu64 " %3.0Lf ",
890 devicename,
891 transfers_per_second_read,
892 transfers_per_second_write,
893 mb_per_second_read * 1024,
894 mb_per_second_write * 1024,
895 ms_per_read, ms_per_write,
896 ms_per_other,
897 ms_per_transaction,
898 queue_len, busy_pct);
899 else
900 printf("%-8.8s %11.1Lf %11.1Lf "
901 "%12.1Lf %12.1Lf %4" PRIu64
902 " %10.1Lf %9.1Lf ",
903 devicename,
904 (long double)total_transfers_read,
905 (long double)total_transfers_write,
906 (long double)
907 total_bytes_read / 1024,
908 (long double)
909 total_bytes_write / 1024,
910 queue_len,
911 total_duration, busy_time);
912 if (firstline) {
913 /*
914 * If this is the first device
915 * we're printing, also print
916 * CPU or TTY stats if requested.
917 */
918 firstline = 0;
919 if (Tflag > 0)
920 printf("%4.0Lf%5.0Lf",
921 cur.tk_nin / etime,
922 cur.tk_nout / etime);
923 }
924 printf("\n");
925 }
926 free(devicename);
927 } else if (oflag > 0) {
928 int msdig = (ms_per_transaction < 99.94) ? 1 : 0;
929
930 if (Iflag == 0)
931 printf("%4.0Lf%4.0Lf%5.*Lf ",
932 blocks_per_second,
933 transfers_per_second,
934 msdig,
935 ms_per_transaction);
936 else
937 printf("%4" PRIu64 "%4" PRIu64 "%5.*Lf ",
938 total_blocks,
939 total_transfers,
940 msdig,
941 ms_per_transaction);
942 } else {
943 if (Iflag == 0)
944 printf("%4.*Lf %5.0Lf %5.*Lf ",
945 kb_per_transfer >= 100 ? 0 : 1,
946 kb_per_transfer,
947 transfers_per_second,
948 mb_per_second >= 1000 ? 0 :
949 (total_mb >= 100 ? 1 : 2),
950 mb_per_second);
951 else {
952 total_mb = total_bytes;
953 total_mb /= 1024 * 1024;
954
955 printf("%4.*Lf %5" PRIu64 " %5.*Lf ",
956 kb_per_transfer >= 100 ? 0 : 1,
957 kb_per_transfer,
958 total_transfers,
959 total_mb >= 1000 ? 0 :
960 (total_mb >= 100 ? 1 : 2),
961 total_mb);
962 }
963 }
964 }
965 if (xflag > 0 && zflag > 0 && firstline == 1 &&
966 (Tflag > 0 || Cflag > 0)) {
967 /*
968 * If zflag is set and we did not print any device
969 * lines I/O because they were all zero,
970 * print TTY/CPU stats.
971 */
972 printf("%52s","");
973 if (Tflag > 0)
974 printf("%4.0Lf %5.0Lf", cur.tk_nin / etime,
975 cur.tk_nout / etime);
976 if (Cflag > 0)
977 cpustats();
978 printf("\n");
979 }
980 }
981
982 static void
cpustats(void)983 cpustats(void)
984 {
985 int state;
986 double cptime;
987
988 cptime = 0.0;
989
990 for (state = 0; state < CPUSTATES; ++state)
991 cptime += cur.cp_time[state];
992 for (state = 0; state < CPUSTATES; ++state)
993 printf(" %2.0f",
994 rint(100. * cur.cp_time[state] / (cptime ? cptime : 1)));
995 }
996
997 static int
readvar(kvm_t * kd,const char * name,int nlid,void * ptr,size_t len)998 readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len)
999 {
1000 if (kd != NULL) {
1001 ssize_t nbytes;
1002
1003 nbytes = kvm_read(kd, namelist[nlid].n_value, ptr, len);
1004
1005 if (nbytes < 0) {
1006 warnx("kvm_read(%s): %s", namelist[nlid].n_name,
1007 kvm_geterr(kd));
1008 return (1);
1009 } else if ((size_t)nbytes != len) {
1010 warnx("kvm_read(%s): expected %zu bytes, got %zd bytes",
1011 namelist[nlid].n_name, len, nbytes);
1012 return (1);
1013 }
1014 } else {
1015 size_t nlen = len;
1016
1017 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
1018 warn("sysctl(%s...) failed", name);
1019 return (1);
1020 }
1021 if (nlen != len) {
1022 warnx("sysctl(%s...): expected %lu, got %lu", name,
1023 (unsigned long)len, (unsigned long)nlen);
1024 return (1);
1025 }
1026 }
1027 return (0);
1028 }
1029