iostat.c revision 83131
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 83131 2001-09-06 04:06:12Z ken $
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>
6974671Stmm#include <sys/sysctl.h>
701590Srgrimes
711590Srgrimes#include <string.h>
721590Srgrimes#include <stdlib.h>
731590Srgrimes#include <nlist.h>
741590Srgrimes#include <paths.h>
7539230Sgibbs#include <devstat.h>
7640060Sobrien#include <err.h>
771590Srgrimes#include "systat.h"
781590Srgrimes#include "extern.h"
7940060Sobrien#include "devs.h"
801590Srgrimes
8139230Sgibbsstruct statinfo cur, last;
821590Srgrimes
831590Srgrimesstatic  int linesperregion;
841590Srgrimesstatic  double etime;
851590Srgrimesstatic  int numbers = 0;		/* default display bar graphs */
8639230Sgibbsstatic  int kbpt = 0;			/* default ms/seek shown */
871590Srgrimes
881590Srgrimesstatic int barlabels __P((int));
8939230Sgibbsstatic void histogram __P((long double, int, double));
901590Srgrimesstatic int numlabels __P((int));
9139230Sgibbsstatic int devstats __P((int, int, int));
921590Srgrimesstatic void stat1 __P((int, int));
931590Srgrimes
941590SrgrimesWINDOW *
951590Srgrimesopeniostat()
961590Srgrimes{
971590Srgrimes	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
981590Srgrimes}
991590Srgrimes
1001590Srgrimesvoid
1011590Srgrimescloseiostat(w)
1021590Srgrimes	WINDOW *w;
1031590Srgrimes{
1041590Srgrimes	if (w == NULL)
1051590Srgrimes		return;
1061590Srgrimes	wclear(w);
1071590Srgrimes	wrefresh(w);
1081590Srgrimes	delwin(w);
1091590Srgrimes}
1101590Srgrimes
1111590Srgrimesint
1121590Srgrimesinitiostat()
1131590Srgrimes{
11483131Sken	if ((num_devices = devstat_getnumdevs(NULL)) < 0)
1151590Srgrimes		return(0);
11639230Sgibbs
11739230Sgibbs	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
11839230Sgibbs	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
11939230Sgibbs	bzero(cur.dinfo, sizeof(struct devinfo));
12039230Sgibbs	bzero(last.dinfo, sizeof(struct devinfo));
12139230Sgibbs
12239230Sgibbs	/*
12339230Sgibbs	 * This value for maxshowdevs (100) is bogus.  I'm not sure exactly
12439230Sgibbs	 * how to calculate it, though.
12539230Sgibbs	 */
12639230Sgibbs	if (dsinit(100, &cur, &last, NULL) != 1)
12739230Sgibbs		return(0);
12839230Sgibbs
1291590Srgrimes	return(1);
1301590Srgrimes}
1311590Srgrimes
1321590Srgrimesvoid
1331590Srgrimesfetchiostat()
1341590Srgrimes{
13539230Sgibbs	struct devinfo *tmp_dinfo;
13669141Srwatson	size_t len;
13769141Srwatson	int err;
13839230Sgibbs
13969141Srwatson	len = sizeof(cur.cp_time);
14069141Srwatson	err = sysctlbyname("kern.cp_time", &cur.cp_time, &len, NULL, 0);
14169141Srwatson	if (err || len != sizeof(cur.cp_time)) {
14269141Srwatson		perror("kern.cp_time");
14369141Srwatson		exit (1);
14469141Srwatson	}
14539230Sgibbs	tmp_dinfo = last.dinfo;
14639230Sgibbs	last.dinfo = cur.dinfo;
14739230Sgibbs	cur.dinfo = tmp_dinfo;
14839230Sgibbs
14939230Sgibbs	last.busy_time = cur.busy_time;
15039230Sgibbs
15139230Sgibbs	/*
15239230Sgibbs	 * Here what we want to do is refresh our device stats.
15339230Sgibbs	 * getdevs() returns 1 when the device list has changed.
15439230Sgibbs	 * If the device list has changed, we want to go through
15539230Sgibbs	 * the selection process again, in case a device that we
15639230Sgibbs	 * were previously displaying has gone away.
15739230Sgibbs	 */
15883131Sken	switch (devstat_getdevs(NULL, &cur)) {
15939230Sgibbs	case -1:
16039230Sgibbs		errx(1, "%s", devstat_errbuf);
16139230Sgibbs		break;
16239230Sgibbs	case 1:
16339230Sgibbs		cmdiostat("refresh", NULL);
16439230Sgibbs		break;
16539230Sgibbs	default:
16639230Sgibbs		break;
16739230Sgibbs	}
16839230Sgibbs	num_devices = cur.dinfo->numdevs;
16939230Sgibbs	generation = cur.dinfo->generation;
17039230Sgibbs
1711590Srgrimes}
1721590Srgrimes
1731590Srgrimes#define	INSET	10
1741590Srgrimes
1751590Srgrimesvoid
1761590Srgrimeslabeliostat()
1771590Srgrimes{
1781590Srgrimes	int row;
1791590Srgrimes
1801590Srgrimes	row = 0;
1811590Srgrimes	wmove(wnd, row, 0); wclrtobot(wnd);
1821590Srgrimes	mvwaddstr(wnd, row++, INSET,
1831590Srgrimes	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
1841590Srgrimes	mvwaddstr(wnd, row++, 0, "cpu  user|");
1851590Srgrimes	mvwaddstr(wnd, row++, 0, "     nice|");
1861590Srgrimes	mvwaddstr(wnd, row++, 0, "   system|");
1874930Sbde	mvwaddstr(wnd, row++, 0, "interrupt|");
1881590Srgrimes	mvwaddstr(wnd, row++, 0, "     idle|");
1891590Srgrimes	if (numbers)
1901590Srgrimes		row = numlabels(row + 1);
1911590Srgrimes	else
1921590Srgrimes		row = barlabels(row + 1);
1931590Srgrimes}
1941590Srgrimes
1951590Srgrimesstatic int
1961590Srgrimesnumlabels(row)
1971590Srgrimes	int row;
1981590Srgrimes{
1991590Srgrimes	int i, col, regions, ndrives;
20039230Sgibbs	char tmpstr[10];
2011590Srgrimes
20239230Sgibbs#define COLWIDTH	17
20350635Speter#define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
20439230Sgibbs	for (ndrives = 0, i = 0; i < num_devices; i++)
20539230Sgibbs		if (dev_select[i].selected)
2061590Srgrimes			ndrives++;
2071590Srgrimes	regions = howmany(ndrives, DRIVESPERLINE);
2081590Srgrimes	/*
2091590Srgrimes	 * Deduct -regions for blank line after each scrolling region.
2101590Srgrimes	 */
21150635Speter	linesperregion = (wnd->_maxy - row - regions) / regions;
2121590Srgrimes	/*
2131590Srgrimes	 * Minimum region contains space for two
2141590Srgrimes	 * label lines and one line of statistics.
2151590Srgrimes	 */
2161590Srgrimes	if (linesperregion < 3)
2171590Srgrimes		linesperregion = 3;
21816301Sjkh	col = INSET;
21939230Sgibbs	for (i = 0; i < num_devices; i++)
22039230Sgibbs		if (dev_select[i].selected) {
22150635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
22216301Sjkh				col = INSET, row += linesperregion + 1;
22350635Speter				if (row > wnd->_maxy - (linesperregion + 1))
2241590Srgrimes					break;
2251590Srgrimes			}
22639230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
22739230Sgibbs				dev_select[i].unit_number);
22839230Sgibbs			mvwaddstr(wnd, row, col + 4, tmpstr);
22939230Sgibbs			mvwaddstr(wnd, row + 1, col, "  KB/t tps  MB/s ");
2301590Srgrimes			col += COLWIDTH;
2311590Srgrimes		}
2321590Srgrimes	if (col)
2331590Srgrimes		row += linesperregion + 1;
2341590Srgrimes	return (row);
2351590Srgrimes}
2361590Srgrimes
2371590Srgrimesstatic int
2381590Srgrimesbarlabels(row)
2391590Srgrimes	int row;
2401590Srgrimes{
2411590Srgrimes	int i;
24239230Sgibbs	char tmpstr[10];
2431590Srgrimes
2441590Srgrimes	mvwaddstr(wnd, row++, INSET,
24561147Sphk	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
24639230Sgibbs	linesperregion = 2 + kbpt;
24739230Sgibbs	for (i = 0; i < num_devices; i++)
24839230Sgibbs		if (dev_select[i].selected) {
24950635Speter			if (row > wnd->_maxy - linesperregion)
2501590Srgrimes				break;
25139230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
25239230Sgibbs				dev_select[i].unit_number);
25339230Sgibbs			mvwprintw(wnd, row++, 0, "%-5.5s MB/s|",
25439230Sgibbs				  tmpstr);
2551590Srgrimes			mvwaddstr(wnd, row++, 0, "      tps|");
25639230Sgibbs			if (kbpt)
25739230Sgibbs				mvwaddstr(wnd, row++, 0, "     KB/t|");
2581590Srgrimes		}
2591590Srgrimes	return (row);
2601590Srgrimes}
2611590Srgrimes
2621590Srgrimes
2631590Srgrimesvoid
2641590Srgrimesshowiostat()
2651590Srgrimes{
2661590Srgrimes	register long t;
2671590Srgrimes	register int i, row, col;
2681590Srgrimes
26939230Sgibbs#define X(fld)	t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t
2701590Srgrimes	etime = 0;
2711590Srgrimes	for(i = 0; i < CPUSTATES; i++) {
2721590Srgrimes		X(cp_time);
27339230Sgibbs		etime += cur.cp_time[i];
2741590Srgrimes	}
2751590Srgrimes	if (etime == 0.0)
2761590Srgrimes		etime = 1.0;
2774930Sbde	etime /= hertz;
2781590Srgrimes	row = 1;
2794930Sbde	for (i = 0; i < CPUSTATES; i++)
2801590Srgrimes		stat1(row++, i);
2811590Srgrimes	if (!numbers) {
2821590Srgrimes		row += 2;
28339230Sgibbs		for (i = 0; i < num_devices; i++)
28439230Sgibbs			if (dev_select[i].selected) {
28550635Speter				if (row > wnd->_maxy - linesperregion)
2861590Srgrimes					break;
28739230Sgibbs				row = devstats(row, INSET, i);
2881590Srgrimes			}
2891590Srgrimes		return;
2901590Srgrimes	}
29116301Sjkh	col = INSET;
2921590Srgrimes	wmove(wnd, row + linesperregion, 0);
2931590Srgrimes	wdeleteln(wnd);
2941590Srgrimes	wmove(wnd, row + 3, 0);
2951590Srgrimes	winsertln(wnd);
29639230Sgibbs	for (i = 0; i < num_devices; i++)
29739230Sgibbs		if (dev_select[i].selected) {
29850635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
29916301Sjkh				col = INSET, row += linesperregion + 1;
30050635Speter				if (row > wnd->_maxy - (linesperregion + 1))
3011590Srgrimes					break;
3021590Srgrimes				wmove(wnd, row + linesperregion, 0);
3031590Srgrimes				wdeleteln(wnd);
3041590Srgrimes				wmove(wnd, row + 3, 0);
3051590Srgrimes				winsertln(wnd);
3061590Srgrimes			}
30739230Sgibbs			(void) devstats(row + 3, col, i);
3081590Srgrimes			col += COLWIDTH;
3091590Srgrimes		}
3101590Srgrimes}
3111590Srgrimes
3121590Srgrimesstatic int
31339230Sgibbsdevstats(row, col, dn)
3141590Srgrimes	int row, col, dn;
3151590Srgrimes{
31639230Sgibbs	long double transfers_per_second;
31739230Sgibbs	long double kb_per_transfer, mb_per_second;
31839230Sgibbs	long double busy_seconds;
31939230Sgibbs	int di;
32039230Sgibbs
32139230Sgibbs	di = dev_select[dn].position;
3221590Srgrimes
32383131Sken	busy_seconds = devstat_compute_etime(cur.busy_time, last.busy_time);
32439230Sgibbs
32583131Sken	if (devstat_compute_statistics(&cur.dinfo->devices[di],
32683131Sken	    &last.dinfo->devices[di], busy_seconds,
32783131Sken	    DSM_KB_PER_TRANSFER, &kb_per_transfer,
32883131Sken	    DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
32983131Sken	    DSM_MB_PER_SECOND, &mb_per_second, DSM_NONE) != 0)
33039230Sgibbs		errx(1, "%s", devstat_errbuf);
33139230Sgibbs
3321590Srgrimes	if (numbers) {
33339230Sgibbs		mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ",
33439230Sgibbs			 kb_per_transfer, transfers_per_second,
33539230Sgibbs			 mb_per_second);
33639230Sgibbs		return(row);
3371590Srgrimes	}
3381590Srgrimes	wmove(wnd, row++, col);
33961148Sphk	histogram(mb_per_second, 50, .5);
3401590Srgrimes	wmove(wnd, row++, col);
34161148Sphk	histogram(transfers_per_second, 50, .5);
34239230Sgibbs	if (kbpt) {
3431590Srgrimes		wmove(wnd, row++, col);
34461148Sphk		histogram(kb_per_transfer, 50, .5);
3451590Srgrimes	}
34639230Sgibbs
34739230Sgibbs	return(row);
34839230Sgibbs
3491590Srgrimes}
3501590Srgrimes
3511590Srgrimesstatic void
3521590Srgrimesstat1(row, o)
3531590Srgrimes	int row, o;
3541590Srgrimes{
3551590Srgrimes	register int i;
3561590Srgrimes	double time;
3571590Srgrimes
3581590Srgrimes	time = 0;
3591590Srgrimes	for (i = 0; i < CPUSTATES; i++)
36039230Sgibbs		time += cur.cp_time[i];
3611590Srgrimes	if (time == 0.0)
3621590Srgrimes		time = 1.0;
3631590Srgrimes	wmove(wnd, row, INSET);
3641590Srgrimes#define CPUSCALE	0.5
36539230Sgibbs	histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE);
3661590Srgrimes}
3671590Srgrimes
3681590Srgrimesstatic void
3691590Srgrimeshistogram(val, colwidth, scale)
37039230Sgibbs	long double val;
3711590Srgrimes	int colwidth;
3721590Srgrimes	double scale;
3731590Srgrimes{
3741590Srgrimes	char buf[10];
3751590Srgrimes	register int k;
3761590Srgrimes	register int v = (int)(val * scale) + 0.5;
3771590Srgrimes
3781590Srgrimes	k = MIN(v, colwidth);
3791590Srgrimes	if (v > colwidth) {
38039230Sgibbs		snprintf(buf, sizeof(buf), "%5.2Lf", val);
3811590Srgrimes		k -= strlen(buf);
3821590Srgrimes		while (k--)
3831590Srgrimes			waddch(wnd, 'X');
3841590Srgrimes		waddstr(wnd, buf);
3851590Srgrimes		return;
3861590Srgrimes	}
3871590Srgrimes	while (k--)
3881590Srgrimes		waddch(wnd, 'X');
3891590Srgrimes	wclrtoeol(wnd);
3901590Srgrimes}
3911590Srgrimes
3921590Srgrimesint
3931590Srgrimescmdiostat(cmd, args)
3941590Srgrimes	char *cmd, *args;
3951590Srgrimes{
3961590Srgrimes
39739230Sgibbs	if (prefix(cmd, "kbpt"))
39839230Sgibbs		kbpt = !kbpt;
3991590Srgrimes	else if (prefix(cmd, "numbers"))
4001590Srgrimes		numbers = 1;
4011590Srgrimes	else if (prefix(cmd, "bars"))
4021590Srgrimes		numbers = 0;
40339230Sgibbs	else if (!dscmd(cmd, args, 100, &cur))
4041590Srgrimes		return (0);
4051590Srgrimes	wclear(wnd);
4061590Srgrimes	labeliostat();
4071590Srgrimes	refresh();
4081590Srgrimes	return (1);
4091590Srgrimes}
410