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