iostat.c revision 61148
11590Srgrimes/*
239230Sgibbs * Copyright (c) 1998 Kenneth D. Merry.
339230Sgibbs * All rights reserved.
439230Sgibbs *
539230Sgibbs * Redistribution and use in source and binary forms, with or without
639230Sgibbs * modification, are permitted provided that the following conditions
739230Sgibbs * are met:
839230Sgibbs * 1. Redistributions of source code must retain the above copyright
939230Sgibbs *    notice, this list of conditions and the following disclaimer.
1039230Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1139230Sgibbs *    notice, this list of conditions and the following disclaimer in the
1239230Sgibbs *    documentation and/or other materials provided with the distribution.
1339230Sgibbs * 3. The name of the author may not be used to endorse or promote products
1439230Sgibbs *    derived from this software without specific prior written permission.
1539230Sgibbs *
1639230Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1739230Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1839230Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1939230Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2039230Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2139230Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2239230Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2339230Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2439230Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2539230Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2639230Sgibbs * SUCH DAMAGE.
2739230Sgibbs *
2850477Speter * $FreeBSD: head/usr.bin/systat/iostat.c 61148 2000-06-01 08:49:46Z phk $
2939230Sgibbs */
3039230Sgibbs/*
311590Srgrimes * Copyright (c) 1980, 1992, 1993
321590Srgrimes *	The Regents of the University of California.  All rights reserved.
331590Srgrimes *
341590Srgrimes * Redistribution and use in source and binary forms, with or without
351590Srgrimes * modification, are permitted provided that the following conditions
361590Srgrimes * are met:
371590Srgrimes * 1. Redistributions of source code must retain the above copyright
381590Srgrimes *    notice, this list of conditions and the following disclaimer.
391590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
401590Srgrimes *    notice, this list of conditions and the following disclaimer in the
411590Srgrimes *    documentation and/or other materials provided with the distribution.
421590Srgrimes * 3. All advertising materials mentioning features or use of this software
431590Srgrimes *    must display the following acknowledgement:
441590Srgrimes *	This product includes software developed by the University of
451590Srgrimes *	California, Berkeley and its contributors.
461590Srgrimes * 4. Neither the name of the University nor the names of its contributors
471590Srgrimes *    may be used to endorse or promote products derived from this software
481590Srgrimes *    without specific prior written permission.
491590Srgrimes *
501590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
511590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
521590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
531590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
541590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
551590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
561590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
571590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
581590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
591590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
601590Srgrimes * SUCH DAMAGE.
611590Srgrimes */
621590Srgrimes
631590Srgrimes#ifndef lint
641590Srgrimesstatic char sccsid[] = "@(#)iostat.c	8.1 (Berkeley) 6/6/93";
651590Srgrimes#endif not lint
661590Srgrimes
671590Srgrimes#include <sys/param.h>
681590Srgrimes#include <sys/dkstat.h>
691590Srgrimes
701590Srgrimes#include <string.h>
711590Srgrimes#include <stdlib.h>
721590Srgrimes#include <nlist.h>
731590Srgrimes#include <paths.h>
7439230Sgibbs#include <devstat.h>
7540060Sobrien#include <err.h>
761590Srgrimes#include "systat.h"
771590Srgrimes#include "extern.h"
7840060Sobrien#include "devs.h"
791590Srgrimes
801590Srgrimesstatic struct nlist namelist[] = {
8139230Sgibbs#define X_CP_TIME	0
821590Srgrimes	{ "_cp_time" },
831590Srgrimes#ifdef vax
841590Srgrimes#define X_MBDINIT	(X_CP_TIME+1)
851590Srgrimes	{ "_mbdinit" },
861590Srgrimes#define X_UBDINIT	(X_CP_TIME+2)
871590Srgrimes	{ "_ubdinit" },
881590Srgrimes#endif
891590Srgrimes#ifdef tahoe
901590Srgrimes#define	X_VBDINIT	(X_CP_TIME+1)
911590Srgrimes	{ "_vbdinit" },
921590Srgrimes#endif
931590Srgrimes	{ "" },
941590Srgrimes};
951590Srgrimes
9639230Sgibbsstruct statinfo cur, last;
971590Srgrimes
981590Srgrimesstatic  int linesperregion;
991590Srgrimesstatic  double etime;
1001590Srgrimesstatic  int numbers = 0;		/* default display bar graphs */
10139230Sgibbsstatic  int kbpt = 0;			/* default ms/seek shown */
1021590Srgrimes
1031590Srgrimesstatic int barlabels __P((int));
10439230Sgibbsstatic void histogram __P((long double, int, double));
1051590Srgrimesstatic int numlabels __P((int));
10639230Sgibbsstatic int devstats __P((int, int, int));
1071590Srgrimesstatic void stat1 __P((int, int));
1081590Srgrimes
1091590SrgrimesWINDOW *
1101590Srgrimesopeniostat()
1111590Srgrimes{
1121590Srgrimes	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
1131590Srgrimes}
1141590Srgrimes
1151590Srgrimesvoid
1161590Srgrimescloseiostat(w)
1171590Srgrimes	WINDOW *w;
1181590Srgrimes{
1191590Srgrimes	if (w == NULL)
1201590Srgrimes		return;
1211590Srgrimes	wclear(w);
1221590Srgrimes	wrefresh(w);
1231590Srgrimes	delwin(w);
1241590Srgrimes}
1251590Srgrimes
1261590Srgrimesint
1271590Srgrimesinitiostat()
1281590Srgrimes{
12939230Sgibbs	if (num_devices = getnumdevs() < 0)
1301590Srgrimes		return(0);
13139230Sgibbs
13239230Sgibbs	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
13339230Sgibbs	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
13439230Sgibbs	bzero(cur.dinfo, sizeof(struct devinfo));
13539230Sgibbs	bzero(last.dinfo, sizeof(struct devinfo));
13639230Sgibbs
13739230Sgibbs	/*
13839230Sgibbs	 * This value for maxshowdevs (100) is bogus.  I'm not sure exactly
13939230Sgibbs	 * how to calculate it, though.
14039230Sgibbs	 */
14139230Sgibbs	if (dsinit(100, &cur, &last, NULL) != 1)
14239230Sgibbs		return(0);
14339230Sgibbs
14439230Sgibbs	if (kvm_nlist(kd, namelist)) {
14539230Sgibbs       		nlisterr(namelist);
14639230Sgibbs		return(0);
1471590Srgrimes	}
14839230Sgibbs
1491590Srgrimes	return(1);
1501590Srgrimes}
1511590Srgrimes
1521590Srgrimesvoid
1531590Srgrimesfetchiostat()
1541590Srgrimes{
15539230Sgibbs	struct devinfo *tmp_dinfo;
15639230Sgibbs
15739230Sgibbs	NREAD(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
15839230Sgibbs	tmp_dinfo = last.dinfo;
15939230Sgibbs	last.dinfo = cur.dinfo;
16039230Sgibbs	cur.dinfo = tmp_dinfo;
16139230Sgibbs
16239230Sgibbs	last.busy_time = cur.busy_time;
16339230Sgibbs
16439230Sgibbs	/*
16539230Sgibbs	 * Here what we want to do is refresh our device stats.
16639230Sgibbs	 * getdevs() returns 1 when the device list has changed.
16739230Sgibbs	 * If the device list has changed, we want to go through
16839230Sgibbs	 * the selection process again, in case a device that we
16939230Sgibbs	 * were previously displaying has gone away.
17039230Sgibbs	 */
17139230Sgibbs	switch (getdevs(&cur)) {
17239230Sgibbs	case -1:
17339230Sgibbs		errx(1, "%s", devstat_errbuf);
17439230Sgibbs		break;
17539230Sgibbs	case 1:
17639230Sgibbs		cmdiostat("refresh", NULL);
17739230Sgibbs		break;
17839230Sgibbs	default:
17939230Sgibbs		break;
18039230Sgibbs	}
18139230Sgibbs	num_devices = cur.dinfo->numdevs;
18239230Sgibbs	generation = cur.dinfo->generation;
18339230Sgibbs
1841590Srgrimes}
1851590Srgrimes
1861590Srgrimes#define	INSET	10
1871590Srgrimes
1881590Srgrimesvoid
1891590Srgrimeslabeliostat()
1901590Srgrimes{
1911590Srgrimes	int row;
1921590Srgrimes
1931590Srgrimes	row = 0;
1941590Srgrimes	wmove(wnd, row, 0); wclrtobot(wnd);
1951590Srgrimes	mvwaddstr(wnd, row++, INSET,
1961590Srgrimes	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
1971590Srgrimes	mvwaddstr(wnd, row++, 0, "cpu  user|");
1981590Srgrimes	mvwaddstr(wnd, row++, 0, "     nice|");
1991590Srgrimes	mvwaddstr(wnd, row++, 0, "   system|");
2004930Sbde	mvwaddstr(wnd, row++, 0, "interrupt|");
2011590Srgrimes	mvwaddstr(wnd, row++, 0, "     idle|");
2021590Srgrimes	if (numbers)
2031590Srgrimes		row = numlabels(row + 1);
2041590Srgrimes	else
2051590Srgrimes		row = barlabels(row + 1);
2061590Srgrimes}
2071590Srgrimes
2081590Srgrimesstatic int
2091590Srgrimesnumlabels(row)
2101590Srgrimes	int row;
2111590Srgrimes{
2121590Srgrimes	int i, col, regions, ndrives;
21339230Sgibbs	char tmpstr[10];
2141590Srgrimes
21539230Sgibbs#define COLWIDTH	17
21650635Speter#define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
21739230Sgibbs	for (ndrives = 0, i = 0; i < num_devices; i++)
21839230Sgibbs		if (dev_select[i].selected)
2191590Srgrimes			ndrives++;
2201590Srgrimes	regions = howmany(ndrives, DRIVESPERLINE);
2211590Srgrimes	/*
2221590Srgrimes	 * Deduct -regions for blank line after each scrolling region.
2231590Srgrimes	 */
22450635Speter	linesperregion = (wnd->_maxy - row - regions) / regions;
2251590Srgrimes	/*
2261590Srgrimes	 * Minimum region contains space for two
2271590Srgrimes	 * label lines and one line of statistics.
2281590Srgrimes	 */
2291590Srgrimes	if (linesperregion < 3)
2301590Srgrimes		linesperregion = 3;
23116301Sjkh	col = INSET;
23239230Sgibbs	for (i = 0; i < num_devices; i++)
23339230Sgibbs		if (dev_select[i].selected) {
23450635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
23516301Sjkh				col = INSET, row += linesperregion + 1;
23650635Speter				if (row > wnd->_maxy - (linesperregion + 1))
2371590Srgrimes					break;
2381590Srgrimes			}
23939230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
24039230Sgibbs				dev_select[i].unit_number);
24139230Sgibbs			mvwaddstr(wnd, row, col + 4, tmpstr);
24239230Sgibbs			mvwaddstr(wnd, row + 1, col, "  KB/t tps  MB/s ");
2431590Srgrimes			col += COLWIDTH;
2441590Srgrimes		}
2451590Srgrimes	if (col)
2461590Srgrimes		row += linesperregion + 1;
2471590Srgrimes	return (row);
2481590Srgrimes}
2491590Srgrimes
2501590Srgrimesstatic int
2511590Srgrimesbarlabels(row)
2521590Srgrimes	int row;
2531590Srgrimes{
2541590Srgrimes	int i;
25539230Sgibbs	char tmpstr[10];
2561590Srgrimes
2571590Srgrimes	mvwaddstr(wnd, row++, INSET,
25861147Sphk	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
25939230Sgibbs	linesperregion = 2 + kbpt;
26039230Sgibbs	for (i = 0; i < num_devices; i++)
26139230Sgibbs		if (dev_select[i].selected) {
26250635Speter			if (row > wnd->_maxy - linesperregion)
2631590Srgrimes				break;
26439230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
26539230Sgibbs				dev_select[i].unit_number);
26639230Sgibbs			mvwprintw(wnd, row++, 0, "%-5.5s MB/s|",
26739230Sgibbs				  tmpstr);
2681590Srgrimes			mvwaddstr(wnd, row++, 0, "      tps|");
26939230Sgibbs			if (kbpt)
27039230Sgibbs				mvwaddstr(wnd, row++, 0, "     KB/t|");
2711590Srgrimes		}
2721590Srgrimes	return (row);
2731590Srgrimes}
2741590Srgrimes
2751590Srgrimes
2761590Srgrimesvoid
2771590Srgrimesshowiostat()
2781590Srgrimes{
2791590Srgrimes	register long t;
2801590Srgrimes	register int i, row, col;
2811590Srgrimes
28239230Sgibbs#define X(fld)	t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t
2831590Srgrimes	etime = 0;
2841590Srgrimes	for(i = 0; i < CPUSTATES; i++) {
2851590Srgrimes		X(cp_time);
28639230Sgibbs		etime += cur.cp_time[i];
2871590Srgrimes	}
2881590Srgrimes	if (etime == 0.0)
2891590Srgrimes		etime = 1.0;
2904930Sbde	etime /= hertz;
2911590Srgrimes	row = 1;
2924930Sbde	for (i = 0; i < CPUSTATES; i++)
2931590Srgrimes		stat1(row++, i);
2941590Srgrimes	if (!numbers) {
2951590Srgrimes		row += 2;
29639230Sgibbs		for (i = 0; i < num_devices; i++)
29739230Sgibbs			if (dev_select[i].selected) {
29850635Speter				if (row > wnd->_maxy - linesperregion)
2991590Srgrimes					break;
30039230Sgibbs				row = devstats(row, INSET, i);
3011590Srgrimes			}
3021590Srgrimes		return;
3031590Srgrimes	}
30416301Sjkh	col = INSET;
3051590Srgrimes	wmove(wnd, row + linesperregion, 0);
3061590Srgrimes	wdeleteln(wnd);
3071590Srgrimes	wmove(wnd, row + 3, 0);
3081590Srgrimes	winsertln(wnd);
30939230Sgibbs	for (i = 0; i < num_devices; i++)
31039230Sgibbs		if (dev_select[i].selected) {
31150635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
31216301Sjkh				col = INSET, row += linesperregion + 1;
31350635Speter				if (row > wnd->_maxy - (linesperregion + 1))
3141590Srgrimes					break;
3151590Srgrimes				wmove(wnd, row + linesperregion, 0);
3161590Srgrimes				wdeleteln(wnd);
3171590Srgrimes				wmove(wnd, row + 3, 0);
3181590Srgrimes				winsertln(wnd);
3191590Srgrimes			}
32039230Sgibbs			(void) devstats(row + 3, col, i);
3211590Srgrimes			col += COLWIDTH;
3221590Srgrimes		}
3231590Srgrimes}
3241590Srgrimes
3251590Srgrimesstatic int
32639230Sgibbsdevstats(row, col, dn)
3271590Srgrimes	int row, col, dn;
3281590Srgrimes{
32939230Sgibbs	long double transfers_per_second;
33039230Sgibbs	long double kb_per_transfer, mb_per_second;
33139230Sgibbs	long double busy_seconds;
33239230Sgibbs	int di;
33339230Sgibbs
33439230Sgibbs	di = dev_select[dn].position;
3351590Srgrimes
33639230Sgibbs	busy_seconds = compute_etime(cur.busy_time, last.busy_time);
33739230Sgibbs
33839230Sgibbs	if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di],
33939230Sgibbs			  busy_seconds, NULL, NULL, NULL,
34039230Sgibbs			  &kb_per_transfer, &transfers_per_second,
34139230Sgibbs			  &mb_per_second, NULL, NULL) != 0)
34239230Sgibbs		errx(1, "%s", devstat_errbuf);
34339230Sgibbs
3441590Srgrimes	if (numbers) {
34539230Sgibbs		mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ",
34639230Sgibbs			 kb_per_transfer, transfers_per_second,
34739230Sgibbs			 mb_per_second);
34839230Sgibbs		return(row);
3491590Srgrimes	}
3501590Srgrimes	wmove(wnd, row++, col);
35161148Sphk	histogram(mb_per_second, 50, .5);
3521590Srgrimes	wmove(wnd, row++, col);
35361148Sphk	histogram(transfers_per_second, 50, .5);
35439230Sgibbs	if (kbpt) {
3551590Srgrimes		wmove(wnd, row++, col);
35661148Sphk		histogram(kb_per_transfer, 50, .5);
3571590Srgrimes	}
35839230Sgibbs
35939230Sgibbs	return(row);
36039230Sgibbs
3611590Srgrimes}
3621590Srgrimes
3631590Srgrimesstatic void
3641590Srgrimesstat1(row, o)
3651590Srgrimes	int row, o;
3661590Srgrimes{
3671590Srgrimes	register int i;
3681590Srgrimes	double time;
3691590Srgrimes
3701590Srgrimes	time = 0;
3711590Srgrimes	for (i = 0; i < CPUSTATES; i++)
37239230Sgibbs		time += cur.cp_time[i];
3731590Srgrimes	if (time == 0.0)
3741590Srgrimes		time = 1.0;
3751590Srgrimes	wmove(wnd, row, INSET);
3761590Srgrimes#define CPUSCALE	0.5
37739230Sgibbs	histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE);
3781590Srgrimes}
3791590Srgrimes
3801590Srgrimesstatic void
3811590Srgrimeshistogram(val, colwidth, scale)
38239230Sgibbs	long double val;
3831590Srgrimes	int colwidth;
3841590Srgrimes	double scale;
3851590Srgrimes{
3861590Srgrimes	char buf[10];
3871590Srgrimes	register int k;
3881590Srgrimes	register int v = (int)(val * scale) + 0.5;
3891590Srgrimes
3901590Srgrimes	k = MIN(v, colwidth);
3911590Srgrimes	if (v > colwidth) {
39239230Sgibbs		snprintf(buf, sizeof(buf), "%5.2Lf", val);
3931590Srgrimes		k -= strlen(buf);
3941590Srgrimes		while (k--)
3951590Srgrimes			waddch(wnd, 'X');
3961590Srgrimes		waddstr(wnd, buf);
3971590Srgrimes		return;
3981590Srgrimes	}
3991590Srgrimes	while (k--)
4001590Srgrimes		waddch(wnd, 'X');
4011590Srgrimes	wclrtoeol(wnd);
4021590Srgrimes}
4031590Srgrimes
4041590Srgrimesint
4051590Srgrimescmdiostat(cmd, args)
4061590Srgrimes	char *cmd, *args;
4071590Srgrimes{
4081590Srgrimes
40939230Sgibbs	if (prefix(cmd, "kbpt"))
41039230Sgibbs		kbpt = !kbpt;
4111590Srgrimes	else if (prefix(cmd, "numbers"))
4121590Srgrimes		numbers = 1;
4131590Srgrimes	else if (prefix(cmd, "bars"))
4141590Srgrimes		numbers = 0;
41539230Sgibbs	else if (!dscmd(cmd, args, 100, &cur))
4161590Srgrimes		return (0);
4171590Srgrimes	wclear(wnd);
4181590Srgrimes	labeliostat();
4191590Srgrimes	refresh();
4201590Srgrimes	return (1);
4211590Srgrimes}
422