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 */
2839230Sgibbs/*
291590Srgrimes * Copyright (c) 1980, 1992, 1993
301590Srgrimes *	The Regents of the University of California.  All rights reserved.
311590Srgrimes *
321590Srgrimes * Redistribution and use in source and binary forms, with or without
331590Srgrimes * modification, are permitted provided that the following conditions
341590Srgrimes * are met:
351590Srgrimes * 1. Redistributions of source code must retain the above copyright
361590Srgrimes *    notice, this list of conditions and the following disclaimer.
371590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
381590Srgrimes *    notice, this list of conditions and the following disclaimer in the
391590Srgrimes *    documentation and/or other materials provided with the distribution.
401590Srgrimes * 4. Neither the name of the University nor the names of its contributors
411590Srgrimes *    may be used to endorse or promote products derived from this software
421590Srgrimes *    without specific prior written permission.
431590Srgrimes *
441590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
451590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
461590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
471590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
481590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
491590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
501590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
511590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
521590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
531590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
541590Srgrimes * SUCH DAMAGE.
551590Srgrimes */
561590Srgrimes
5787715Smarkm#include <sys/cdefs.h>
581590Srgrimes
5987715Smarkm__FBSDID("$FreeBSD$");
6087715Smarkm
6187715Smarkm#ifdef lint
6287715Smarkmstatic const char sccsid[] = "@(#)iostat.c	8.1 (Berkeley) 6/6/93";
6387715Smarkm#endif
6487715Smarkm
651590Srgrimes#include <sys/param.h>
6674671Stmm#include <sys/sysctl.h>
67111007Sphk#include <sys/resource.h>
681590Srgrimes
6987715Smarkm#include <devstat.h>
7087715Smarkm#include <err.h>
711590Srgrimes#include <nlist.h>
72200462Sdelphij#include <paths.h>
7387715Smarkm#include <stdlib.h>
7487715Smarkm#include <string.h>
7587715Smarkm
761590Srgrimes#include "systat.h"
771590Srgrimes#include "extern.h"
7840060Sobrien#include "devs.h"
791590Srgrimes
8039230Sgibbsstruct statinfo cur, last;
811590Srgrimes
821590Srgrimesstatic  int linesperregion;
831590Srgrimesstatic  double etime;
841590Srgrimesstatic  int numbers = 0;		/* default display bar graphs */
8539230Sgibbsstatic  int kbpt = 0;			/* default ms/seek shown */
861590Srgrimes
8792922Simpstatic int barlabels(int);
8892922Simpstatic void histogram(long double, int, double);
8992922Simpstatic int numlabels(int);
9092922Simpstatic int devstats(int, int, int);
9192922Simpstatic void stat1(int, int);
921590Srgrimes
931590SrgrimesWINDOW *
94175387Sdelphijopeniostat(void)
951590Srgrimes{
96158160Sbde	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
971590Srgrimes}
981590Srgrimes
991590Srgrimesvoid
100175387Sdelphijcloseiostat(WINDOW *w)
1011590Srgrimes{
1021590Srgrimes	if (w == NULL)
1031590Srgrimes		return;
1041590Srgrimes	wclear(w);
1051590Srgrimes	wrefresh(w);
1061590Srgrimes	delwin(w);
1071590Srgrimes}
1081590Srgrimes
1091590Srgrimesint
110175387Sdelphijinitiostat(void)
1111590Srgrimes{
11283131Sken	if ((num_devices = devstat_getnumdevs(NULL)) < 0)
1131590Srgrimes		return(0);
11439230Sgibbs
11539230Sgibbs	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
11639230Sgibbs	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
11739230Sgibbs	bzero(cur.dinfo, sizeof(struct devinfo));
11839230Sgibbs	bzero(last.dinfo, sizeof(struct devinfo));
119158161Sbde
12039230Sgibbs	/*
12139230Sgibbs	 * This value for maxshowdevs (100) is bogus.  I'm not sure exactly
12239230Sgibbs	 * how to calculate it, though.
12339230Sgibbs	 */
12439230Sgibbs	if (dsinit(100, &cur, &last, NULL) != 1)
12539230Sgibbs		return(0);
12639230Sgibbs
1271590Srgrimes	return(1);
1281590Srgrimes}
1291590Srgrimes
1301590Srgrimesvoid
131175387Sdelphijfetchiostat(void)
1321590Srgrimes{
13339230Sgibbs	struct devinfo *tmp_dinfo;
13469141Srwatson	size_t len;
13539230Sgibbs
13669141Srwatson	len = sizeof(cur.cp_time);
13787715Smarkm	if (sysctlbyname("kern.cp_time", &cur.cp_time, &len, NULL, 0)
13887715Smarkm	    || len != sizeof(cur.cp_time)) {
13969141Srwatson		perror("kern.cp_time");
14069141Srwatson		exit (1);
14169141Srwatson	}
14239230Sgibbs	tmp_dinfo = last.dinfo;
14339230Sgibbs	last.dinfo = cur.dinfo;
14439230Sgibbs	cur.dinfo = tmp_dinfo;
145121836Stjr
146121836Stjr	last.snap_time = cur.snap_time;
147158161Sbde
14839230Sgibbs	/*
14939230Sgibbs	 * Here what we want to do is refresh our device stats.
15039230Sgibbs	 * getdevs() returns 1 when the device list has changed.
15139230Sgibbs	 * If the device list has changed, we want to go through
15239230Sgibbs	 * the selection process again, in case a device that we
15339230Sgibbs	 * were previously displaying has gone away.
15439230Sgibbs	 */
15583131Sken	switch (devstat_getdevs(NULL, &cur)) {
15639230Sgibbs	case -1:
15739230Sgibbs		errx(1, "%s", devstat_errbuf);
15839230Sgibbs		break;
15939230Sgibbs	case 1:
16039230Sgibbs		cmdiostat("refresh", NULL);
16139230Sgibbs		break;
16239230Sgibbs	default:
16339230Sgibbs		break;
16439230Sgibbs	}
16539230Sgibbs	num_devices = cur.dinfo->numdevs;
16639230Sgibbs	generation = cur.dinfo->generation;
16739230Sgibbs
1681590Srgrimes}
1691590Srgrimes
1701590Srgrimes#define	INSET	10
1711590Srgrimes
1721590Srgrimesvoid
173175387Sdelphijlabeliostat(void)
1741590Srgrimes{
1751590Srgrimes	int row;
1761590Srgrimes
1771590Srgrimes	row = 0;
1781590Srgrimes	wmove(wnd, row, 0); wclrtobot(wnd);
1791590Srgrimes	mvwaddstr(wnd, row++, INSET,
180164689Syar	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
1811590Srgrimes	mvwaddstr(wnd, row++, 0, "cpu  user|");
1821590Srgrimes	mvwaddstr(wnd, row++, 0, "     nice|");
1831590Srgrimes	mvwaddstr(wnd, row++, 0, "   system|");
1844930Sbde	mvwaddstr(wnd, row++, 0, "interrupt|");
1851590Srgrimes	mvwaddstr(wnd, row++, 0, "     idle|");
1861590Srgrimes	if (numbers)
1871590Srgrimes		row = numlabels(row + 1);
1881590Srgrimes	else
1891590Srgrimes		row = barlabels(row + 1);
1901590Srgrimes}
1911590Srgrimes
1921590Srgrimesstatic int
193175387Sdelphijnumlabels(int row)
1941590Srgrimes{
19587715Smarkm	int i, _col, regions, ndrives;
19639230Sgibbs	char tmpstr[10];
1971590Srgrimes
19839230Sgibbs#define COLWIDTH	17
19950635Speter#define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
20039230Sgibbs	for (ndrives = 0, i = 0; i < num_devices; i++)
20139230Sgibbs		if (dev_select[i].selected)
2021590Srgrimes			ndrives++;
2031590Srgrimes	regions = howmany(ndrives, DRIVESPERLINE);
2041590Srgrimes	/*
2051590Srgrimes	 * Deduct -regions for blank line after each scrolling region.
2061590Srgrimes	 */
20750635Speter	linesperregion = (wnd->_maxy - row - regions) / regions;
2081590Srgrimes	/*
2091590Srgrimes	 * Minimum region contains space for two
2101590Srgrimes	 * label lines and one line of statistics.
2111590Srgrimes	 */
2121590Srgrimes	if (linesperregion < 3)
2131590Srgrimes		linesperregion = 3;
21487715Smarkm	_col = INSET;
21539230Sgibbs	for (i = 0; i < num_devices; i++)
21639230Sgibbs		if (dev_select[i].selected) {
21787715Smarkm			if (_col + COLWIDTH >= wnd->_maxx - INSET) {
21887715Smarkm				_col = INSET, row += linesperregion + 1;
21950635Speter				if (row > wnd->_maxy - (linesperregion + 1))
2201590Srgrimes					break;
2211590Srgrimes			}
22239230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
22339230Sgibbs				dev_select[i].unit_number);
22487715Smarkm			mvwaddstr(wnd, row, _col + 4, tmpstr);
22587715Smarkm			mvwaddstr(wnd, row + 1, _col, "  KB/t tps  MB/s ");
22687715Smarkm			_col += COLWIDTH;
2271590Srgrimes		}
22887715Smarkm	if (_col)
2291590Srgrimes		row += linesperregion + 1;
2301590Srgrimes	return (row);
2311590Srgrimes}
2321590Srgrimes
2331590Srgrimesstatic int
234175387Sdelphijbarlabels(int row)
2351590Srgrimes{
2361590Srgrimes	int i;
23739230Sgibbs	char tmpstr[10];
2381590Srgrimes
2391590Srgrimes	mvwaddstr(wnd, row++, INSET,
240164689Syar	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
24139230Sgibbs	linesperregion = 2 + kbpt;
24239230Sgibbs	for (i = 0; i < num_devices; i++)
24339230Sgibbs		if (dev_select[i].selected) {
24450635Speter			if (row > wnd->_maxy - linesperregion)
2451590Srgrimes				break;
24639230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
24739230Sgibbs				dev_select[i].unit_number);
248158161Sbde			mvwprintw(wnd, row++, 0, "%-5.5s MB/s|",
24939230Sgibbs				  tmpstr);
2501590Srgrimes			mvwaddstr(wnd, row++, 0, "      tps|");
25139230Sgibbs			if (kbpt)
25239230Sgibbs				mvwaddstr(wnd, row++, 0, "     KB/t|");
2531590Srgrimes		}
2541590Srgrimes	return (row);
2551590Srgrimes}
2561590Srgrimes
2571590Srgrimes
2581590Srgrimesvoid
259175387Sdelphijshowiostat(void)
2601590Srgrimes{
26187715Smarkm	long t;
26287715Smarkm	int i, row, _col;
2631590Srgrimes
26439230Sgibbs#define X(fld)	t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t
2651590Srgrimes	etime = 0;
2661590Srgrimes	for(i = 0; i < CPUSTATES; i++) {
2671590Srgrimes		X(cp_time);
26839230Sgibbs		etime += cur.cp_time[i];
2691590Srgrimes	}
2701590Srgrimes	if (etime == 0.0)
2711590Srgrimes		etime = 1.0;
2724930Sbde	etime /= hertz;
2731590Srgrimes	row = 1;
2744930Sbde	for (i = 0; i < CPUSTATES; i++)
2751590Srgrimes		stat1(row++, i);
2761590Srgrimes	if (!numbers) {
2771590Srgrimes		row += 2;
27839230Sgibbs		for (i = 0; i < num_devices; i++)
27939230Sgibbs			if (dev_select[i].selected) {
28050635Speter				if (row > wnd->_maxy - linesperregion)
2811590Srgrimes					break;
28239230Sgibbs				row = devstats(row, INSET, i);
2831590Srgrimes			}
2841590Srgrimes		return;
2851590Srgrimes	}
28687715Smarkm	_col = INSET;
2871590Srgrimes	wmove(wnd, row + linesperregion, 0);
2881590Srgrimes	wdeleteln(wnd);
2891590Srgrimes	wmove(wnd, row + 3, 0);
2901590Srgrimes	winsertln(wnd);
29139230Sgibbs	for (i = 0; i < num_devices; i++)
29239230Sgibbs		if (dev_select[i].selected) {
29387715Smarkm			if (_col + COLWIDTH >= wnd->_maxx - INSET) {
29487715Smarkm				_col = INSET, row += linesperregion + 1;
29550635Speter				if (row > wnd->_maxy - (linesperregion + 1))
2961590Srgrimes					break;
2971590Srgrimes				wmove(wnd, row + linesperregion, 0);
2981590Srgrimes				wdeleteln(wnd);
2991590Srgrimes				wmove(wnd, row + 3, 0);
3001590Srgrimes				winsertln(wnd);
3011590Srgrimes			}
30287715Smarkm			(void) devstats(row + 3, _col, i);
30387715Smarkm			_col += COLWIDTH;
3041590Srgrimes		}
3051590Srgrimes}
3061590Srgrimes
3071590Srgrimesstatic int
308175387Sdelphijdevstats(int row, int _col, int dn)
3091590Srgrimes{
31039230Sgibbs	long double transfers_per_second;
31139230Sgibbs	long double kb_per_transfer, mb_per_second;
31239230Sgibbs	long double busy_seconds;
31339230Sgibbs	int di;
314158161Sbde
31539230Sgibbs	di = dev_select[dn].position;
3161590Srgrimes
317112288Sphk	busy_seconds = cur.snap_time - last.snap_time;
31839230Sgibbs
31983131Sken	if (devstat_compute_statistics(&cur.dinfo->devices[di],
32083131Sken	    &last.dinfo->devices[di], busy_seconds,
32183131Sken	    DSM_KB_PER_TRANSFER, &kb_per_transfer,
32283131Sken	    DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
32383131Sken	    DSM_MB_PER_SECOND, &mb_per_second, DSM_NONE) != 0)
32439230Sgibbs		errx(1, "%s", devstat_errbuf);
32539230Sgibbs
3261590Srgrimes	if (numbers) {
32787715Smarkm		mvwprintw(wnd, row, _col, " %5.2Lf %3.0Lf %5.2Lf ",
32839230Sgibbs			 kb_per_transfer, transfers_per_second,
32939230Sgibbs			 mb_per_second);
33039230Sgibbs		return(row);
3311590Srgrimes	}
33287715Smarkm	wmove(wnd, row++, _col);
33361148Sphk	histogram(mb_per_second, 50, .5);
33487715Smarkm	wmove(wnd, row++, _col);
33561148Sphk	histogram(transfers_per_second, 50, .5);
33639230Sgibbs	if (kbpt) {
33787715Smarkm		wmove(wnd, row++, _col);
33861148Sphk		histogram(kb_per_transfer, 50, .5);
3391590Srgrimes	}
34039230Sgibbs
34139230Sgibbs	return(row);
34239230Sgibbs
3431590Srgrimes}
3441590Srgrimes
3451590Srgrimesstatic void
346175387Sdelphijstat1(int row, int o)
3471590Srgrimes{
34887715Smarkm	int i;
34987715Smarkm	double dtime;
3501590Srgrimes
35187715Smarkm	dtime = 0.0;
3521590Srgrimes	for (i = 0; i < CPUSTATES; i++)
35387715Smarkm		dtime += cur.cp_time[i];
35487715Smarkm	if (dtime == 0.0)
35587715Smarkm		dtime = 1.0;
3561590Srgrimes	wmove(wnd, row, INSET);
3571590Srgrimes#define CPUSCALE	0.5
35887715Smarkm	histogram(100.0 * cur.cp_time[o] / dtime, 50, CPUSCALE);
3591590Srgrimes}
3601590Srgrimes
3611590Srgrimesstatic void
362175387Sdelphijhistogram(long double val, int colwidth, double scale)
3631590Srgrimes{
3641590Srgrimes	char buf[10];
36587715Smarkm	int k;
36687715Smarkm	int v = (int)(val * scale) + 0.5;
3671590Srgrimes
3681590Srgrimes	k = MIN(v, colwidth);
3691590Srgrimes	if (v > colwidth) {
37039230Sgibbs		snprintf(buf, sizeof(buf), "%5.2Lf", val);
3711590Srgrimes		k -= strlen(buf);
3721590Srgrimes		while (k--)
3731590Srgrimes			waddch(wnd, 'X');
3741590Srgrimes		waddstr(wnd, buf);
3751590Srgrimes		return;
3761590Srgrimes	}
3771590Srgrimes	while (k--)
3781590Srgrimes		waddch(wnd, 'X');
3791590Srgrimes	wclrtoeol(wnd);
3801590Srgrimes}
3811590Srgrimes
3821590Srgrimesint
383175387Sdelphijcmdiostat(const char *cmd, const char *args)
3841590Srgrimes{
3851590Srgrimes
38639230Sgibbs	if (prefix(cmd, "kbpt"))
38739230Sgibbs		kbpt = !kbpt;
3881590Srgrimes	else if (prefix(cmd, "numbers"))
3891590Srgrimes		numbers = 1;
3901590Srgrimes	else if (prefix(cmd, "bars"))
3911590Srgrimes		numbers = 0;
39239230Sgibbs	else if (!dscmd(cmd, args, 100, &cur))
3931590Srgrimes		return (0);
3941590Srgrimes	wclear(wnd);
3951590Srgrimes	labeliostat();
3961590Srgrimes	refresh();
3971590Srgrimes	return (1);
3981590Srgrimes}
399