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