xref: /dragonfly/usr.sbin/iostat/iostat.c (revision 71126e33)
1 /*
2  * Copyright (c) 1997, 1998  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: src/usr.sbin/iostat/iostat.c,v 1.17.2.2 2001/07/19 04:15:42 kris Exp $
29  * $DragonFly: src/usr.sbin/iostat/iostat.c,v 1.4 2004/03/23 07:45:34 cpressey Exp $
30  */
31 /*
32  * Parts of this program are derived from the original FreeBSD iostat
33  * program:
34  */
35 /*-
36  * Copyright (c) 1986, 1991, 1993
37  *	The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *	This product includes software developed by the University of
50  *	California, Berkeley and its contributors.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 /*
68  * Ideas for the new iostat statistics output modes taken from the NetBSD
69  * version of iostat:
70  */
71 /*
72  * Copyright (c) 1996 John M. Vinopal
73  * All rights reserved.
74  *
75  * Redistribution and use in source and binary forms, with or without
76  * modification, are permitted provided that the following conditions
77  * are met:
78  * 1. Redistributions of source code must retain the above copyright
79  *    notice, this list of conditions and the following disclaimer.
80  * 2. Redistributions in binary form must reproduce the above copyright
81  *    notice, this list of conditions and the following disclaimer in the
82  *    documentation and/or other materials provided with the distribution.
83  * 3. All advertising materials mentioning features or use of this software
84  *    must display the following acknowledgement:
85  *      This product includes software developed for the NetBSD Project
86  *      by John M. Vinopal.
87  * 4. The name of the author may not be used to endorse or promote products
88  *    derived from this software without specific prior written permission.
89  *
90  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
91  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
92  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
93  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
94  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
95  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
96  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
97  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
98  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
99  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
100  * SUCH DAMAGE.
101  */
102 
103 #include <sys/param.h>
104 #include <sys/errno.h>
105 #include <sys/dkstat.h>
106 
107 #include <err.h>
108 #include <ctype.h>
109 #include <fcntl.h>
110 #include <kvm.h>
111 #include <stdio.h>
112 #include <stdlib.h>
113 #include <string.h>
114 #include <unistd.h>
115 #include <limits.h>
116 #include <devstat.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_CP_TIME	2
124 	{ "_cp_time" },
125 #define X_HZ		3
126 	{ "_hz" },
127 #define X_STATHZ	4
128 	{ "_stathz" },
129 #define X_END		4
130 	{ NULL },
131 };
132 
133 struct statinfo cur, last;
134 int num_devices;
135 struct device_selection *dev_select;
136 int maxshowdevs;
137 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
138 
139 #define nlread(x, v) \
140 	kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
141 
142 /* local function declarations */
143 static void usage(void);
144 static void phdr(int signo);
145 static void devstats(int perf_select);
146 static void cpustats(void);
147 
148 static void
149 usage(void)
150 {
151 	/*
152 	 * We also support the following 'traditional' syntax:
153 	 * iostat [drives] [wait [count]]
154 	 * This isn't mentioned in the man page, or the usage statement,
155 	 * but it is supported.
156 	 */
157 	fprintf(stderr, "usage: iostat [-CdhIKoT?] [-c count] [-M core]"
158 		" [-n devs] [-N system]\n"
159 		"\t      [-t type,if,pass] [-w wait] [drives]\n");
160 }
161 
162 int
163 main(int argc, char **argv)
164 {
165 	int c;
166 	int i;
167 	int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
168 	int count = 0, waittime = 0;
169 	char *memf = NULL, *nlistf = NULL;
170 	struct devstat_match *matches;
171 	int num_matches = 0;
172         char errbuf[_POSIX2_LINE_MAX];
173 	kvm_t *kd;
174 	int hz, stathz;
175 	int headercount;
176 	long generation;
177 	int num_devices_specified;
178 	int num_selected, num_selections;
179 	long select_generation;
180 	char **specified_devices;
181 	devstat_select_mode select_mode;
182 
183 	matches = NULL;
184 	maxshowdevs = 3;
185 
186 	while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:?")) != -1) {
187 		switch(c) {
188 			case 'c':
189 				cflag++;
190 				count = atoi(optarg);
191 				if (count < 1)
192 					errx(1, "count %d is < 1", count);
193 				break;
194 			case 'C':
195 				Cflag++;
196 				break;
197 			case 'd':
198 				dflag++;
199 				break;
200 			case 'h':
201 				hflag++;
202 				break;
203 			case 'I':
204 				Iflag++;
205 				break;
206 			case 'K':
207 				Kflag++;
208 				break;
209 			case 'M':
210 				memf = optarg;
211 				break;
212 			case 'n':
213 				nflag++;
214 				maxshowdevs = atoi(optarg);
215 				if (maxshowdevs < 0)
216 					errx(1, "number of devices %d is < 0",
217 					     maxshowdevs);
218 				break;
219 			case 'N':
220 				nlistf = optarg;
221 				break;
222 			case 'o':
223 				oflag++;
224 				break;
225 			case 't':
226 				tflag++;
227 				if (buildmatch(optarg, &matches,
228 					       &num_matches) != 0)
229 					errx(1, "%s", devstat_errbuf);
230 				break;
231 			case 'T':
232 				Tflag++;
233 				break;
234 			case 'w':
235 				wflag++;
236 				waittime = atoi(optarg);
237 				if (waittime < 1)
238 					errx(1, "wait time is < 1");
239 				break;
240 			default:
241 				usage();
242 				exit(1);
243 				break;
244 		}
245 	}
246 
247 	argc -= optind;
248 	argv += optind;
249 
250 	/*
251 	 * Discard setgid privileges if not the running kernel so that bad
252 	 * guys can't print interesting stuff from kernel memory.
253 	 */
254 	if (nlistf != NULL || memf != NULL)
255 		setgid(getgid());
256 
257 	/*
258 	 * Make sure that the userland devstat version matches the kernel
259 	 * devstat version.  If not, exit and print a message informing
260 	 * the user of his mistake.
261 	 */
262 	if (checkversion() < 0)
263 		errx(1, "%s", devstat_errbuf);
264 
265 	/*
266 	 * Figure out how many devices we should display.
267 	 */
268 	if (nflag == 0) {
269 		if (oflag > 0) {
270 			if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
271 				maxshowdevs = 5;
272 			else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
273 				maxshowdevs = 5;
274 			else
275 				maxshowdevs = 4;
276 		} else {
277 			if ((dflag > 0) && (Cflag == 0))
278 				maxshowdevs = 4;
279 			else
280 				maxshowdevs = 3;
281 		}
282 	}
283 
284 	/* find out how many devices we have */
285 	if ((num_devices = getnumdevs()) < 0)
286 		err(1, "can't get number of devices");
287 
288 	if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
289 	     NULL)
290 		err(1, "devinfo malloc failed");
291 	if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
292 	     NULL)
293 		err(1, "devinfo malloc failed");
294 	bzero(cur.dinfo, sizeof(struct devinfo));
295 	bzero(last.dinfo, sizeof(struct devinfo));
296 
297 	/*
298 	 * Grab all the devices.  We don't look to see if the list has
299 	 * changed here, since it almost certainly has.  We only look for
300 	 * errors.
301 	 */
302 	if (getdevs(&cur) == -1)
303 		errx(1, "%s", devstat_errbuf);
304 
305 	num_devices = cur.dinfo->numdevs;
306 	generation = cur.dinfo->generation;
307 
308 	/*
309 	 * If the user specified any devices on the command line, see if
310 	 * they are in the list of devices we have now.
311 	 */
312 	if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL)
313 		err(1, "specified_devices malloc failed");
314 	for (num_devices_specified = 0; *argv; ++argv) {
315 		if (isdigit(**argv))
316 			break;
317 		num_devices_specified++;
318 		specified_devices = (char **)realloc(specified_devices,
319 						     sizeof(char *) *
320 						     num_devices_specified);
321 		specified_devices[num_devices_specified - 1] = *argv;
322 
323 	}
324 	if (nflag == 0 && maxshowdevs < num_devices_specified)
325 		maxshowdevs = num_devices_specified;
326 
327 	dev_select = NULL;
328 
329 	if ((num_devices_specified == 0) && (num_matches == 0))
330 		select_mode = DS_SELECT_ADD;
331 	else
332 		select_mode = DS_SELECT_ONLY;
333 
334 	/*
335 	 * At this point, selectdevs will almost surely indicate that the
336 	 * device list has changed, so we don't look for return values of 0
337 	 * or 1.  If we get back -1, though, there is an error.
338 	 */
339 	if (selectdevs(&dev_select, &num_selected,
340 		       &num_selections, &select_generation,
341 		       generation, cur.dinfo->devices, num_devices,
342 		       matches, num_matches,
343 		       specified_devices, num_devices_specified,
344 		       select_mode, maxshowdevs, hflag) == -1)
345 		errx(1, "%s", devstat_errbuf);
346 
347 	/*
348 	 * Look for the traditional wait time and count arguments.
349 	 */
350 	if (*argv) {
351 		waittime = atoi(*argv);
352 
353 		/* Let the user know he goofed, but keep going anyway */
354 		if (wflag != 0)
355 			warnx("discarding previous wait interval, using"
356 			      " %d instead", waittime);
357 		wflag++;
358 
359 		if (*++argv) {
360 			count = atoi(*argv);
361 			if (cflag != 0)
362 				warnx("discarding previous count, using %d"
363 				      " instead", count);
364 			cflag++;
365 		} else
366 			count = -1;
367 	}
368 
369 	/*
370 	 * If the user specified a count, but not an interval, we default
371 	 * to an interval of 1 second.
372 	 */
373 	if ((wflag == 0) && (cflag > 0))
374 		waittime = 1;
375 
376 	/*
377 	 * If the user specified a wait time, but not a count, we want to
378 	 * go on ad infinitum.  This can be redundant if the user uses the
379 	 * traditional method of specifying the wait, since in that case we
380 	 * already set count = -1 above.  Oh well.
381 	 */
382 	if ((wflag > 0) && (cflag == 0))
383 		count = -1;
384 
385 	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
386 
387 	if (kd == 0)
388 		errx(1, "kvm_openfiles: %s", errbuf);
389 
390 	if (kvm_nlist(kd, namelist) == -1)
391 		errx(1, "kvm_nlist: %s", kvm_geterr(kd));
392 
393 	nlread(X_HZ, hz);
394 	nlread(X_STATHZ, stathz);
395 	if (stathz)
396 		hz = stathz;
397 
398 	/*
399 	 * If the user stops the program (control-Z) and then resumes it,
400 	 * print out the header again.
401 	 */
402 	signal(SIGCONT, phdr);
403 
404 	for (headercount = 1;;) {
405 		struct devinfo *tmp_dinfo;
406 		long tmp;
407 		double etime;
408 
409 		if (!--headercount) {
410 			phdr(0);
411 			headercount = 20;
412 		}
413 		kvm_read(kd, namelist[X_TK_NIN].n_value,
414 		    &cur.tk_nin, sizeof(cur.tk_nin));
415 		kvm_read(kd, namelist[X_TK_NOUT].n_value,
416 		    &cur.tk_nout, sizeof(cur.tk_nout));
417 		kvm_read(kd, namelist[X_CP_TIME].n_value,
418 		    cur.cp_time, sizeof(cur.cp_time));
419 
420 		tmp_dinfo = last.dinfo;
421 		last.dinfo = cur.dinfo;
422 		cur.dinfo = tmp_dinfo;
423 
424 		last.busy_time = cur.busy_time;
425 
426 		/*
427 		 * Here what we want to do is refresh our device stats.
428 		 * getdevs() returns 1 when the device list has changed.
429 		 * If the device list has changed, we want to go through
430 		 * the selection process again, in case a device that we
431 		 * were previously displaying has gone away.
432 		 */
433 		switch (getdevs(&cur)) {
434 		case -1:
435 			errx(1, "%s", devstat_errbuf);
436 			break;
437 		case 1: {
438 			int retval;
439 
440 			num_devices = cur.dinfo->numdevs;
441 			generation = cur.dinfo->generation;
442 			retval = selectdevs(&dev_select, &num_selected,
443 					    &num_selections, &select_generation,
444 					    generation, cur.dinfo->devices,
445 					    num_devices, matches, num_matches,
446 					    specified_devices,
447 					    num_devices_specified,
448 					    select_mode, maxshowdevs, hflag);
449 			switch(retval) {
450 			case -1:
451 				errx(1, "%s", devstat_errbuf);
452 				break;
453 			case 1:
454 				phdr(0);
455 				headercount = 20;
456 				break;
457 			default:
458 				break;
459 			}
460 			break;
461 		}
462 		default:
463 			break;
464 		}
465 
466 		/*
467 		 * We only want to re-select devices if we're in 'top'
468 		 * mode.  This is the only mode where the devices selected
469 		 * could actually change.
470 		 */
471 		if (hflag > 0) {
472 			int retval;
473 			retval = selectdevs(&dev_select, &num_selected,
474 					    &num_selections, &select_generation,
475 					    generation, cur.dinfo->devices,
476 					    num_devices, matches, num_matches,
477 					    specified_devices,
478 					    num_devices_specified,
479 					    select_mode, maxshowdevs, hflag);
480 			switch(retval) {
481 			case -1:
482 				errx(1,"%s", devstat_errbuf);
483 				break;
484 			case 1:
485 				phdr(0);
486 				headercount = 20;
487 				break;
488 			default:
489 				break;
490 			}
491 		}
492 
493 		tmp = cur.tk_nin;
494 		cur.tk_nin -= last.tk_nin;
495 		last.tk_nin = tmp;
496 		tmp = cur.tk_nout;
497 		cur.tk_nout -= last.tk_nout;
498 		last.tk_nout = tmp;
499 
500 		etime = 0.0;
501 
502 		for (i = 0; i < CPUSTATES; i++) {
503 			tmp = cur.cp_time[i];
504 			cur.cp_time[i] -= last.cp_time[i];
505 			last.cp_time[i] = tmp;
506 			etime += cur.cp_time[i];
507 		}
508 		if (etime == 0.0)
509 			etime = 1.0;
510 		etime /= (float)hz;
511 		if ((dflag == 0) || (Tflag > 0))
512 			printf("%4.0f%5.0f", cur.tk_nin / etime,
513 				cur.tk_nout/etime);
514 		devstats(hflag);
515 		if ((dflag == 0) || (Cflag > 0))
516 			cpustats();
517 		printf("\n");
518 		fflush(stdout);
519 
520 		if (count >= 0 && --count <= 0)
521 			break;
522 
523 		sleep(waittime);
524 	}
525 
526 	exit(0);
527 }
528 
529 static void
530 phdr(int signo)
531 {
532 	int i;
533 	int printed;
534 
535 	if ((dflag == 0) || (Tflag > 0))
536 		printf("      tty");
537 	for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
538 		int di;
539 		if ((dev_select[i].selected != 0)
540 		 && (dev_select[i].selected <= maxshowdevs)) {
541 			di = dev_select[i].position;
542 			if (oflag > 0)
543 				printf("%12.6s%d ",
544 					    cur.dinfo->devices[di].device_name,
545 					    cur.dinfo->devices[di].unit_number);
546 			else
547 				printf("%15.6s%d ",
548 					    cur.dinfo->devices[di].device_name,
549 					    cur.dinfo->devices[di].unit_number);
550 			printed++;
551 		}
552 	}
553 	if ((dflag == 0) || (Cflag > 0))
554 		printf("            cpu\n");
555 	else
556 		printf("\n");
557 
558 	if ((dflag == 0) || (Tflag > 0))
559 		printf(" tin tout");
560 
561 	for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
562 		if ((dev_select[i].selected != 0)
563 		 && (dev_select[i].selected <= maxshowdevs)) {
564 			if (oflag > 0) {
565 				if (Iflag == 0)
566 					printf(" sps tps msps ");
567 				else
568 					printf(" blk xfr msps ");
569 			} else {
570 				if (Iflag == 0)
571 					printf("  KB/t tps  MB/s ");
572 				else
573 					printf("  KB/t xfrs   MB ");
574 			}
575 			printed++;
576 		}
577 	}
578 	if ((dflag == 0) || (Cflag > 0))
579 		printf(" us ni sy in id\n");
580 	else
581 		printf("\n");
582 
583 }
584 
585 static void
586 devstats(int perf_select)
587 {
588 	int dn;
589 	long double transfers_per_second;
590 	long double kb_per_transfer, mb_per_second;
591 	u_int64_t total_bytes, total_transfers, total_blocks;
592 	long double busy_seconds;
593 	long double total_mb;
594 	long double blocks_per_second, ms_per_transaction;
595 
596 	/*
597 	 * Calculate elapsed time up front, since it's the same for all
598 	 * devices.
599 	 */
600 	busy_seconds = compute_etime(cur.busy_time, last.busy_time);
601 
602 	for (dn = 0; dn < num_devices; dn++) {
603 		int di;
604 
605 		if (((perf_select == 0) && (dev_select[dn].selected == 0))
606 		 || (dev_select[dn].selected > maxshowdevs))
607 			continue;
608 
609 		di = dev_select[dn].position;
610 
611 		if (compute_stats(&cur.dinfo->devices[di],
612 				  &last.dinfo->devices[di], busy_seconds,
613 				  &total_bytes, &total_transfers,
614 				  &total_blocks, &kb_per_transfer,
615 				  &transfers_per_second, &mb_per_second,
616 				  &blocks_per_second, &ms_per_transaction)!= 0)
617 			errx(1, "%s", devstat_errbuf);
618 
619 		if (perf_select != 0) {
620 			dev_select[dn].bytes = total_bytes;
621 			if ((dev_select[dn].selected == 0)
622 			 || (dev_select[dn].selected > maxshowdevs))
623 				continue;
624 		}
625 
626 		if (Kflag) {
627 			int block_size = cur.dinfo->devices[di].block_size;
628 			total_blocks = total_blocks * (block_size ?
629 						       block_size : 512) / 1024;
630 		}
631 
632 		if (oflag > 0) {
633 			int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
634 
635 			if (Iflag == 0)
636 				printf("%4.0Lf%4.0Lf%5.*Lf ",
637 				       blocks_per_second,
638 				       transfers_per_second,
639 				       msdig,
640 				       ms_per_transaction);
641 			else
642 				printf("%4.1qu%4.1qu%5.*Lf ",
643 				       total_blocks,
644 				       total_transfers,
645 				       msdig,
646 				       ms_per_transaction);
647 		} else {
648 			if (Iflag == 0)
649 				printf(" %5.2Lf %3.0Lf %5.2Lf ",
650 				       kb_per_transfer,
651 				       transfers_per_second,
652 				       mb_per_second);
653 			else {
654 				total_mb = total_bytes;
655 				total_mb /= 1024 * 1024;
656 
657 				printf(" %5.2Lf %3.1qu %5.2Lf ",
658 				       kb_per_transfer,
659 				       total_transfers,
660 				       total_mb);
661 			}
662 		}
663 	}
664 }
665 
666 static void
667 cpustats(void)
668 {
669 	int state;
670 	double time;
671 
672 	time = 0.0;
673 
674 	for (state = 0; state < CPUSTATES; ++state)
675 		time += cur.cp_time[state];
676 	for (state = 0; state < CPUSTATES; ++state)
677 		printf(" %2.0f",
678 		       100. * cur.cp_time[state] / (time ? time : 1));
679 }
680