iostat.c revision 287633
112115Sdyson/*
212115Sdyson * Copyright (c) 1998 Kenneth D. Merry.
312115Sdyson * All rights reserved.
412115Sdyson *
512115Sdyson * Redistribution and use in source and binary forms, with or without
612115Sdyson * modification, are permitted provided that the following conditions
712115Sdyson * are met:
812115Sdyson * 1. Redistributions of source code must retain the above copyright
912115Sdyson *    notice, this list of conditions and the following disclaimer.
1012115Sdyson * 2. Redistributions in binary form must reproduce the above copyright
1112115Sdyson *    notice, this list of conditions and the following disclaimer in the
1212115Sdyson *    documentation and/or other materials provided with the distribution.
1312115Sdyson * 3. The name of the author may not be used to endorse or promote products
1412115Sdyson *    derived from this software without specific prior written permission.
1512115Sdyson *
1612115Sdyson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1712115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1812115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1912115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2012115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2112115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2212115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2312115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2412115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2512115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2612115Sdyson * SUCH DAMAGE.
2712115Sdyson */
2812115Sdyson/*
2912115Sdyson * Copyright (c) 1980, 1992, 1993
3012115Sdyson *	The Regents of the University of California.  All rights reserved.
3112115Sdyson *
3212115Sdyson * Redistribution and use in source and binary forms, with or without
3312115Sdyson * modification, are permitted provided that the following conditions
3412115Sdyson * are met:
3512115Sdyson * 1. Redistributions of source code must retain the above copyright
3612115Sdyson *    notice, this list of conditions and the following disclaimer.
3712115Sdyson * 2. Redistributions in binary form must reproduce the above copyright
3812115Sdyson *    notice, this list of conditions and the following disclaimer in the
3912115Sdyson *    documentation and/or other materials provided with the distribution.
4012115Sdyson * 4. Neither the name of the University nor the names of its contributors
4112115Sdyson *    may be used to endorse or promote products derived from this software
4213260Swollman *    without specific prior written permission.
4312115Sdyson *
4412115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4512115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4612115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4712115Sdyson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
4812115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4912115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5012115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5112115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5229906Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5324131Sbde * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5412115Sdyson * SUCH DAMAGE.
5512115Sdyson */
5612115Sdyson
5712115Sdyson#include <sys/cdefs.h>
5812115Sdyson
5912115Sdyson__FBSDID("$FreeBSD: head/usr.bin/systat/iostat.c 287633 2015-09-10 22:07:52Z delphij $");
6012115Sdyson
6112115Sdyson#ifdef lint
6212115Sdysonstatic const char sccsid[] = "@(#)iostat.c	8.1 (Berkeley) 6/6/93";
6312115Sdyson#endif
6412115Sdyson
6512115Sdyson#include <sys/param.h>
6612115Sdyson#include <sys/sysctl.h>
6712115Sdyson#include <sys/resource.h>
6812115Sdyson
6912115Sdyson#include <devstat.h>
7028270Swollman#include <err.h>
7112911Sphk#include <nlist.h>
7212911Sphk#include <paths.h>
7312911Sphk#include <stdlib.h>
7412911Sphk#include <string.h>
7512911Sphk
7612911Sphk#include "systat.h"
7712911Sphk#include "extern.h"
7812911Sphk#include "devs.h"
7912911Sphk
8012911Sphkstruct statinfo cur, last;
8112911Sphk
8212911Sphkstatic  int linesperregion;
8312911Sphkstatic  double etime;
8412115Sdysonstatic  int numbers = 0;		/* default display bar graphs */
8531315Sbdestatic  int kbpt = 0;			/* default ms/seek shown */
8630280Sphk
8712911Sphkstatic int barlabels(int);
8812115Sdysonstatic void histogram(long double, int, double);
8912115Sdysonstatic int numlabels(int);
9012115Sdysonstatic int devstats(int, int, int);
9112115Sdysonstatic void stat1(int, int);
9212115Sdyson
9312115SdysonWINDOW *
9412115Sdysonopeniostat(void)
9512115Sdyson{
9612115Sdyson	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
9712115Sdyson}
9812115Sdyson
9912115Sdysonvoid
10012115Sdysoncloseiostat(WINDOW *w)
10138909Sbde{
10212115Sdyson	if (w == NULL)
10312115Sdyson		return;
10412115Sdyson	wclear(w);
10512911Sphk	wrefresh(w);
10612115Sdyson	delwin(w);
10716322Sgpalmer}
10816322Sgpalmer
10916322Sgpalmerint
11016322Sgpalmerinitiostat(void)
11116322Sgpalmer{
11216322Sgpalmer	if ((num_devices = devstat_getnumdevs(NULL)) < 0)
11316322Sgpalmer		return(0);
11412115Sdyson
11516322Sgpalmer	cur.dinfo = calloc(1, sizeof(struct devinfo));
11612115Sdyson	last.dinfo = calloc(1, sizeof(struct devinfo));
11712115Sdyson
11812115Sdyson	/*
11912115Sdyson	 * This value for maxshowdevs (100) is bogus.  I'm not sure exactly
12012115Sdyson	 * how to calculate it, though.
12112911Sphk	 */
12212115Sdyson	if (dsinit(100, &cur, &last, NULL) != 1)
12312115Sdyson		return(0);
12412115Sdyson
12512115Sdyson	return(1);
12612115Sdyson}
12712115Sdyson
12812115Sdysonvoid
12912115Sdysonfetchiostat(void)
13012115Sdyson{
13129208Sbde	struct devinfo *tmp_dinfo;
13229208Sbde	size_t len;
13329208Sbde
13429208Sbde	len = sizeof(cur.cp_time);
13512115Sdyson	if (sysctlbyname("kern.cp_time", &cur.cp_time, &len, NULL, 0)
13612115Sdyson	    || len != sizeof(cur.cp_time)) {
13712115Sdyson		perror("kern.cp_time");
13812115Sdyson		exit (1);
13929888Skato	}
14029888Skato	tmp_dinfo = last.dinfo;
14129888Skato	last.dinfo = cur.dinfo;
14229888Skato	cur.dinfo = tmp_dinfo;
14312115Sdyson
14412115Sdyson	last.snap_time = cur.snap_time;
14512115Sdyson
14612115Sdyson	/*
14712115Sdyson	 * Here what we want to do is refresh our device stats.
14812115Sdyson	 * getdevs() returns 1 when the device list has changed.
14912115Sdyson	 * If the device list has changed, we want to go through
15012115Sdyson	 * the selection process again, in case a device that we
15112115Sdyson	 * were previously displaying has gone away.
15230469Sjulian	 */
15312115Sdyson	switch (devstat_getdevs(NULL, &cur)) {
15412115Sdyson	case -1:
15512115Sdyson		errx(1, "%s", devstat_errbuf);
15612115Sdyson		break;
15712115Sdyson	case 1:
15812115Sdyson		cmdiostat("refresh", NULL);
15912115Sdyson		break;
16012115Sdyson	default:
16112115Sdyson		break;
16212115Sdyson	}
16312115Sdyson	num_devices = cur.dinfo->numdevs;
16412115Sdyson	generation = cur.dinfo->generation;
16512115Sdyson
16612115Sdyson}
16712115Sdyson
16812115Sdyson#define	INSET	10
16916322Sgpalmer
17012115Sdysonvoid
17112115Sdysonlabeliostat(void)
17212115Sdyson{
17312115Sdyson	int row;
17412115Sdyson
17512115Sdyson	row = 0;
17612911Sphk	wmove(wnd, row, 0); wclrtobot(wnd);
17712115Sdyson	mvwaddstr(wnd, row++, INSET,
17812115Sdyson	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
17912115Sdyson	mvwaddstr(wnd, row++, 0, "cpu  user|");
18012115Sdyson	mvwaddstr(wnd, row++, 0, "     nice|");
18112115Sdyson	mvwaddstr(wnd, row++, 0, "   system|");
18212115Sdyson	mvwaddstr(wnd, row++, 0, "interrupt|");
18312115Sdyson	mvwaddstr(wnd, row++, 0, "     idle|");
18412115Sdyson	if (numbers)
18512115Sdyson		row = numlabels(row + 1);
18612115Sdyson	else
18712115Sdyson		row = barlabels(row + 1);
18812115Sdyson}
18912115Sdyson
19039028Sbdestatic int
19112115Sdysonnumlabels(int row)
19243301Sdillon{
19312115Sdyson	int i, _col, regions, ndrives;
19412115Sdyson	char tmpstr[10];
19512115Sdyson
19612115Sdyson#define COLWIDTH	17
19729888Skato#define DRIVESPERLINE	((getmaxx(wnd) - 1 - INSET) / COLWIDTH)
19829888Skato	for (ndrives = 0, i = 0; i < num_devices; i++)
19912115Sdyson		if (dev_select[i].selected)
20012115Sdyson			ndrives++;
20112115Sdyson	regions = howmany(ndrives, DRIVESPERLINE);
20212115Sdyson	/*
20312115Sdyson	 * Deduct -regions for blank line after each scrolling region.
20429888Skato	 */
20529888Skato	linesperregion = (getmaxy(wnd) - 1 - row - regions) / regions;
20629888Skato	/*
20729888Skato	 * Minimum region contains space for two
20812115Sdyson	 * label lines and one line of statistics.
20912115Sdyson	 */
21012115Sdyson	if (linesperregion < 3)
21112115Sdyson		linesperregion = 3;
21222521Sdyson	_col = INSET;
21312115Sdyson	for (i = 0; i < num_devices; i++)
21412115Sdyson		if (dev_select[i].selected) {
21522521Sdyson			if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) {
21639670Sbde				_col = INSET, row += linesperregion + 1;
21739670Sbde				if (row > getmaxy(wnd) - 1 - (linesperregion + 1))
21839670Sbde					break;
21939670Sbde			}
22039670Sbde			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
22112115Sdyson				dev_select[i].unit_number);
22212115Sdyson			mvwaddstr(wnd, row, _col + 4, tmpstr);
22312115Sdyson			mvwaddstr(wnd, row + 1, _col, "  KB/t tps  MB/s ");
22412115Sdyson			_col += COLWIDTH;
22512115Sdyson		}
22639028Sbde	if (_col)
22739028Sbde		row += linesperregion + 1;
22839028Sbde	return (row);
22939028Sbde}
23039028Sbde
23139028Sbdestatic int
23239028Sbdebarlabels(int row)
23339028Sbde{
23443301Sdillon	int i;
23543301Sdillon	char tmpstr[10];
23639028Sbde
23739028Sbde	mvwaddstr(wnd, row++, INSET,
23839028Sbde	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
23939028Sbde	linesperregion = 2 + kbpt;
24039028Sbde	for (i = 0; i < num_devices; i++)
24139028Sbde		if (dev_select[i].selected) {
24239670Sbde			if (row > getmaxy(wnd) - 1 - linesperregion)
24339670Sbde				break;
24439670Sbde			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
24539670Sbde				dev_select[i].unit_number);
24639670Sbde			mvwprintw(wnd, row++, 0, "%-5.5s MB/s|",
24739670Sbde				  tmpstr);
24839670Sbde			mvwaddstr(wnd, row++, 0, "      tps|");
24939670Sbde			if (kbpt)
25039670Sbde				mvwaddstr(wnd, row++, 0, "     KB/t|");
25139670Sbde		}
25239670Sbde	return (row);
25339670Sbde}
25439670Sbde
25512115Sdysonvoid
25612115Sdysonshowiostat(void)
25739670Sbde{
25812115Sdyson	long t;
25912115Sdyson	int i, row, _col;
26012115Sdyson
26112115Sdyson#define X(fld)	t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t
26212115Sdyson	etime = 0;
26312115Sdyson	for(i = 0; i < CPUSTATES; i++) {
26412115Sdyson		X(cp_time);
26512115Sdyson		etime += cur.cp_time[i];
26612115Sdyson	}
26712115Sdyson	if (etime == 0.0)
26812115Sdyson		etime = 1.0;
26912115Sdyson	etime /= hertz;
27012115Sdyson	row = 1;
27143301Sdillon	for (i = 0; i < CPUSTATES; i++)
27212115Sdyson		stat1(row++, i);
27312115Sdyson	if (!numbers) {
27412115Sdyson		row += 2;
27512115Sdyson		for (i = 0; i < num_devices; i++)
27612115Sdyson			if (dev_select[i].selected) {
27712115Sdyson				if (row > getmaxy(wnd) - linesperregion)
27812115Sdyson					break;
27940651Sbde				row = devstats(row, INSET, i);
28040651Sbde			}
28112115Sdyson		return;
28212115Sdyson	}
28312115Sdyson	_col = INSET;
28439028Sbde	wmove(wnd, row + linesperregion, 0);
28539028Sbde	wdeleteln(wnd);
28639028Sbde	wmove(wnd, row + 3, 0);
28739028Sbde	winsertln(wnd);
28839028Sbde	for (i = 0; i < num_devices; i++)
28939028Sbde		if (dev_select[i].selected) {
29039028Sbde			if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) {
29139028Sbde				_col = INSET, row += linesperregion + 1;
29239028Sbde				if (row > getmaxy(wnd) - 1 - (linesperregion + 1))
29339028Sbde					break;
29443301Sdillon				wmove(wnd, row + linesperregion, 0);
29539028Sbde				wdeleteln(wnd);
29639028Sbde				wmove(wnd, row + 3, 0);
29739028Sbde				winsertln(wnd);
29839028Sbde			}
29939028Sbde			(void) devstats(row + 3, _col, i);
30039028Sbde			_col += COLWIDTH;
30129888Skato		}
30229888Skato}
30329888Skato
30429888Skatostatic int
30529888Skatodevstats(int row, int _col, int dn)
30612115Sdyson{
30729888Skato	long double transfers_per_second;
30812115Sdyson	long double kb_per_transfer, mb_per_second;
30912115Sdyson	long double busy_seconds;
31012115Sdyson	int di;
31112115Sdyson
31212115Sdyson	di = dev_select[dn].position;
31312115Sdyson
31412115Sdyson	busy_seconds = cur.snap_time - last.snap_time;
31512115Sdyson
31612115Sdyson	if (devstat_compute_statistics(&cur.dinfo->devices[di],
31712115Sdyson	    &last.dinfo->devices[di], busy_seconds,
31812115Sdyson	    DSM_KB_PER_TRANSFER, &kb_per_transfer,
31912115Sdyson	    DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
32012115Sdyson	    DSM_MB_PER_SECOND, &mb_per_second, DSM_NONE) != 0)
32112115Sdyson		errx(1, "%s", devstat_errbuf);
32212115Sdyson
32312115Sdyson	if (numbers) {
32412115Sdyson		mvwprintw(wnd, row, _col, " %5.2Lf %3.0Lf %5.2Lf ",
32512115Sdyson			 kb_per_transfer, transfers_per_second,
32612115Sdyson			 mb_per_second);
32712115Sdyson		return(row);
32812115Sdyson	}
32912115Sdyson	wmove(wnd, row++, _col);
33012115Sdyson	histogram(mb_per_second, 50, .5);
33112115Sdyson	wmove(wnd, row++, _col);
33212115Sdyson	histogram(transfers_per_second, 50, .5);
33312115Sdyson	if (kbpt) {
33412115Sdyson		wmove(wnd, row++, _col);
33512115Sdyson		histogram(kb_per_transfer, 50, .5);
33612115Sdyson	}
33712115Sdyson
33812115Sdyson	return(row);
33912115Sdyson
34012115Sdyson}
34112115Sdyson
34212115Sdysonstatic void
34312115Sdysonstat1(int row, int o)
34412115Sdyson{
34512115Sdyson	int i;
34612115Sdyson	double dtime;
34712115Sdyson
34812115Sdyson	dtime = 0.0;
34912115Sdyson	for (i = 0; i < CPUSTATES; i++)
35012115Sdyson		dtime += cur.cp_time[i];
35112115Sdyson	if (dtime == 0.0)
35212115Sdyson		dtime = 1.0;
35312115Sdyson	wmove(wnd, row, INSET);
35439671Sbde#define CPUSCALE	0.5
35512115Sdyson	histogram(100.0 * cur.cp_time[o] / dtime, 50, CPUSCALE);
35612115Sdyson}
35712115Sdyson
35812115Sdysonstatic void
35912115Sdysonhistogram(long double val, int colwidth, double scale)
36012115Sdyson{
36112115Sdyson	char buf[10];
36212115Sdyson	int k;
36339671Sbde	int v = (int)(val * scale) + 0.5;
36412115Sdyson
36512115Sdyson	k = MIN(v, colwidth);
36612115Sdyson	if (v > colwidth) {
36712115Sdyson		snprintf(buf, sizeof(buf), "%5.2Lf", val);
36812115Sdyson		k -= strlen(buf);
36912115Sdyson		while (k--)
37012115Sdyson			waddch(wnd, 'X');
37112115Sdyson		waddstr(wnd, buf);
37212115Sdyson		return;
37339671Sbde	}
37412115Sdyson	while (k--)
37512115Sdyson		waddch(wnd, 'X');
37612115Sdyson	wclrtoeol(wnd);
37712115Sdyson}
37812115Sdyson
37912115Sdysonint
38012115Sdysoncmdiostat(const char *cmd, const char *args)
38112115Sdyson{
38212115Sdyson
38312115Sdyson	if (prefix(cmd, "kbpt"))
38412115Sdyson		kbpt = !kbpt;
38512115Sdyson	else if (prefix(cmd, "numbers"))
38612115Sdyson		numbers = 1;
38712115Sdyson	else if (prefix(cmd, "bars"))
38812115Sdyson		numbers = 0;
38912115Sdyson	else if (!dscmd(cmd, args, 100, &cur))
39012115Sdyson		return (0);
39112115Sdyson	wclear(wnd);
39212115Sdyson	labeliostat();
39312115Sdyson	refresh();
39412115Sdyson	return (1);
39512115Sdyson}
39612115Sdyson