iostat.c revision 50635
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 50635 1999-08-30 08:18:09Z peter $
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>
6839230Sgibbs#include <sys/buf.h>
691590Srgrimes#include <sys/dkstat.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
811590Srgrimesstatic struct nlist namelist[] = {
8239230Sgibbs#define X_CP_TIME	0
831590Srgrimes	{ "_cp_time" },
841590Srgrimes#ifdef vax
851590Srgrimes#define X_MBDINIT	(X_CP_TIME+1)
861590Srgrimes	{ "_mbdinit" },
871590Srgrimes#define X_UBDINIT	(X_CP_TIME+2)
881590Srgrimes	{ "_ubdinit" },
891590Srgrimes#endif
901590Srgrimes#ifdef tahoe
911590Srgrimes#define	X_VBDINIT	(X_CP_TIME+1)
921590Srgrimes	{ "_vbdinit" },
931590Srgrimes#endif
941590Srgrimes	{ "" },
951590Srgrimes};
961590Srgrimes
9739230Sgibbsstruct statinfo cur, last;
981590Srgrimes
991590Srgrimesstatic  int linesperregion;
1001590Srgrimesstatic  double etime;
1011590Srgrimesstatic  int numbers = 0;		/* default display bar graphs */
10239230Sgibbsstatic  int kbpt = 0;			/* default ms/seek shown */
1031590Srgrimes
1041590Srgrimesstatic int barlabels __P((int));
10539230Sgibbsstatic void histogram __P((long double, int, double));
1061590Srgrimesstatic int numlabels __P((int));
10739230Sgibbsstatic int devstats __P((int, int, int));
1081590Srgrimesstatic void stat1 __P((int, int));
1091590Srgrimes
1101590SrgrimesWINDOW *
1111590Srgrimesopeniostat()
1121590Srgrimes{
1131590Srgrimes	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
1141590Srgrimes}
1151590Srgrimes
1161590Srgrimesvoid
1171590Srgrimescloseiostat(w)
1181590Srgrimes	WINDOW *w;
1191590Srgrimes{
1201590Srgrimes	if (w == NULL)
1211590Srgrimes		return;
1221590Srgrimes	wclear(w);
1231590Srgrimes	wrefresh(w);
1241590Srgrimes	delwin(w);
1251590Srgrimes}
1261590Srgrimes
1271590Srgrimesint
1281590Srgrimesinitiostat()
1291590Srgrimes{
13039230Sgibbs	if (num_devices = getnumdevs() < 0)
1311590Srgrimes		return(0);
13239230Sgibbs
13339230Sgibbs	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
13439230Sgibbs	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
13539230Sgibbs	bzero(cur.dinfo, sizeof(struct devinfo));
13639230Sgibbs	bzero(last.dinfo, sizeof(struct devinfo));
13739230Sgibbs
13839230Sgibbs	/*
13939230Sgibbs	 * This value for maxshowdevs (100) is bogus.  I'm not sure exactly
14039230Sgibbs	 * how to calculate it, though.
14139230Sgibbs	 */
14239230Sgibbs	if (dsinit(100, &cur, &last, NULL) != 1)
14339230Sgibbs		return(0);
14439230Sgibbs
14539230Sgibbs	if (kvm_nlist(kd, namelist)) {
14639230Sgibbs       		nlisterr(namelist);
14739230Sgibbs		return(0);
1481590Srgrimes	}
14939230Sgibbs
1501590Srgrimes	return(1);
1511590Srgrimes}
1521590Srgrimes
1531590Srgrimesvoid
1541590Srgrimesfetchiostat()
1551590Srgrimes{
15639230Sgibbs	struct devinfo *tmp_dinfo;
15739230Sgibbs
15839230Sgibbs	NREAD(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
15939230Sgibbs	tmp_dinfo = last.dinfo;
16039230Sgibbs	last.dinfo = cur.dinfo;
16139230Sgibbs	cur.dinfo = tmp_dinfo;
16239230Sgibbs
16339230Sgibbs	last.busy_time = cur.busy_time;
16439230Sgibbs
16539230Sgibbs	/*
16639230Sgibbs	 * Here what we want to do is refresh our device stats.
16739230Sgibbs	 * getdevs() returns 1 when the device list has changed.
16839230Sgibbs	 * If the device list has changed, we want to go through
16939230Sgibbs	 * the selection process again, in case a device that we
17039230Sgibbs	 * were previously displaying has gone away.
17139230Sgibbs	 */
17239230Sgibbs	switch (getdevs(&cur)) {
17339230Sgibbs	case -1:
17439230Sgibbs		errx(1, "%s", devstat_errbuf);
17539230Sgibbs		break;
17639230Sgibbs	case 1:
17739230Sgibbs		cmdiostat("refresh", NULL);
17839230Sgibbs		break;
17939230Sgibbs	default:
18039230Sgibbs		break;
18139230Sgibbs	}
18239230Sgibbs	num_devices = cur.dinfo->numdevs;
18339230Sgibbs	generation = cur.dinfo->generation;
18439230Sgibbs
1851590Srgrimes}
1861590Srgrimes
1871590Srgrimes#define	INSET	10
1881590Srgrimes
1891590Srgrimesvoid
1901590Srgrimeslabeliostat()
1911590Srgrimes{
1921590Srgrimes	int row;
1931590Srgrimes
1941590Srgrimes	row = 0;
1951590Srgrimes	wmove(wnd, row, 0); wclrtobot(wnd);
1961590Srgrimes	mvwaddstr(wnd, row++, INSET,
1971590Srgrimes	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
1981590Srgrimes	mvwaddstr(wnd, row++, 0, "cpu  user|");
1991590Srgrimes	mvwaddstr(wnd, row++, 0, "     nice|");
2001590Srgrimes	mvwaddstr(wnd, row++, 0, "   system|");
2014930Sbde	mvwaddstr(wnd, row++, 0, "interrupt|");
2021590Srgrimes	mvwaddstr(wnd, row++, 0, "     idle|");
2031590Srgrimes	if (numbers)
2041590Srgrimes		row = numlabels(row + 1);
2051590Srgrimes	else
2061590Srgrimes		row = barlabels(row + 1);
2071590Srgrimes}
2081590Srgrimes
2091590Srgrimesstatic int
2101590Srgrimesnumlabels(row)
2111590Srgrimes	int row;
2121590Srgrimes{
2131590Srgrimes	int i, col, regions, ndrives;
21439230Sgibbs	char tmpstr[10];
2151590Srgrimes
21639230Sgibbs#define COLWIDTH	17
21750635Speter#define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
21839230Sgibbs	for (ndrives = 0, i = 0; i < num_devices; i++)
21939230Sgibbs		if (dev_select[i].selected)
2201590Srgrimes			ndrives++;
2211590Srgrimes	regions = howmany(ndrives, DRIVESPERLINE);
2221590Srgrimes	/*
2231590Srgrimes	 * Deduct -regions for blank line after each scrolling region.
2241590Srgrimes	 */
22550635Speter	linesperregion = (wnd->_maxy - row - regions) / regions;
2261590Srgrimes	/*
2271590Srgrimes	 * Minimum region contains space for two
2281590Srgrimes	 * label lines and one line of statistics.
2291590Srgrimes	 */
2301590Srgrimes	if (linesperregion < 3)
2311590Srgrimes		linesperregion = 3;
23216301Sjkh	col = INSET;
23339230Sgibbs	for (i = 0; i < num_devices; i++)
23439230Sgibbs		if (dev_select[i].selected) {
23550635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
23616301Sjkh				col = INSET, row += linesperregion + 1;
23750635Speter				if (row > wnd->_maxy - (linesperregion + 1))
2381590Srgrimes					break;
2391590Srgrimes			}
24039230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
24139230Sgibbs				dev_select[i].unit_number);
24239230Sgibbs			mvwaddstr(wnd, row, col + 4, tmpstr);
24339230Sgibbs			mvwaddstr(wnd, row + 1, col, "  KB/t tps  MB/s ");
2441590Srgrimes			col += COLWIDTH;
2451590Srgrimes		}
2461590Srgrimes	if (col)
2471590Srgrimes		row += linesperregion + 1;
2481590Srgrimes	return (row);
2491590Srgrimes}
2501590Srgrimes
2511590Srgrimesstatic int
2521590Srgrimesbarlabels(row)
2531590Srgrimes	int row;
2541590Srgrimes{
2551590Srgrimes	int i;
25639230Sgibbs	char tmpstr[10];
2571590Srgrimes
2581590Srgrimes	mvwaddstr(wnd, row++, INSET,
2591590Srgrimes	    "/0   /5   /10  /15  /20  /25  /30  /35  /40  /45  /50");
26039230Sgibbs	linesperregion = 2 + kbpt;
26139230Sgibbs	for (i = 0; i < num_devices; i++)
26239230Sgibbs		if (dev_select[i].selected) {
26350635Speter			if (row > wnd->_maxy - linesperregion)
2641590Srgrimes				break;
26539230Sgibbs			sprintf(tmpstr, "%s%d", dev_select[i].device_name,
26639230Sgibbs				dev_select[i].unit_number);
26739230Sgibbs			mvwprintw(wnd, row++, 0, "%-5.5s MB/s|",
26839230Sgibbs				  tmpstr);
2691590Srgrimes			mvwaddstr(wnd, row++, 0, "      tps|");
27039230Sgibbs			if (kbpt)
27139230Sgibbs				mvwaddstr(wnd, row++, 0, "     KB/t|");
2721590Srgrimes		}
2731590Srgrimes	return (row);
2741590Srgrimes}
2751590Srgrimes
2761590Srgrimes
2771590Srgrimesvoid
2781590Srgrimesshowiostat()
2791590Srgrimes{
2801590Srgrimes	register long t;
2811590Srgrimes	register int i, row, col;
2821590Srgrimes
28339230Sgibbs#define X(fld)	t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t
2841590Srgrimes	etime = 0;
2851590Srgrimes	for(i = 0; i < CPUSTATES; i++) {
2861590Srgrimes		X(cp_time);
28739230Sgibbs		etime += cur.cp_time[i];
2881590Srgrimes	}
2891590Srgrimes	if (etime == 0.0)
2901590Srgrimes		etime = 1.0;
2914930Sbde	etime /= hertz;
2921590Srgrimes	row = 1;
2934930Sbde	for (i = 0; i < CPUSTATES; i++)
2941590Srgrimes		stat1(row++, i);
2951590Srgrimes	if (!numbers) {
2961590Srgrimes		row += 2;
29739230Sgibbs		for (i = 0; i < num_devices; i++)
29839230Sgibbs			if (dev_select[i].selected) {
29950635Speter				if (row > wnd->_maxy - linesperregion)
3001590Srgrimes					break;
30139230Sgibbs				row = devstats(row, INSET, i);
3021590Srgrimes			}
3031590Srgrimes		return;
3041590Srgrimes	}
30516301Sjkh	col = INSET;
3061590Srgrimes	wmove(wnd, row + linesperregion, 0);
3071590Srgrimes	wdeleteln(wnd);
3081590Srgrimes	wmove(wnd, row + 3, 0);
3091590Srgrimes	winsertln(wnd);
31039230Sgibbs	for (i = 0; i < num_devices; i++)
31139230Sgibbs		if (dev_select[i].selected) {
31250635Speter			if (col + COLWIDTH >= wnd->_maxx - INSET) {
31316301Sjkh				col = INSET, row += linesperregion + 1;
31450635Speter				if (row > wnd->_maxy - (linesperregion + 1))
3151590Srgrimes					break;
3161590Srgrimes				wmove(wnd, row + linesperregion, 0);
3171590Srgrimes				wdeleteln(wnd);
3181590Srgrimes				wmove(wnd, row + 3, 0);
3191590Srgrimes				winsertln(wnd);
3201590Srgrimes			}
32139230Sgibbs			(void) devstats(row + 3, col, i);
3221590Srgrimes			col += COLWIDTH;
3231590Srgrimes		}
3241590Srgrimes}
3251590Srgrimes
3261590Srgrimesstatic int
32739230Sgibbsdevstats(row, col, dn)
3281590Srgrimes	int row, col, dn;
3291590Srgrimes{
33039230Sgibbs	long double transfers_per_second;
33139230Sgibbs	long double kb_per_transfer, mb_per_second;
33239230Sgibbs	long double busy_seconds;
33339230Sgibbs	int di;
33439230Sgibbs
33539230Sgibbs	di = dev_select[dn].position;
3361590Srgrimes
33739230Sgibbs	busy_seconds = compute_etime(cur.busy_time, last.busy_time);
33839230Sgibbs
33939230Sgibbs	if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di],
34039230Sgibbs			  busy_seconds, NULL, NULL, NULL,
34139230Sgibbs			  &kb_per_transfer, &transfers_per_second,
34239230Sgibbs			  &mb_per_second, NULL, NULL) != 0)
34339230Sgibbs		errx(1, "%s", devstat_errbuf);
34439230Sgibbs
3451590Srgrimes	if (numbers) {
34639230Sgibbs		mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ",
34739230Sgibbs			 kb_per_transfer, transfers_per_second,
34839230Sgibbs			 mb_per_second);
34939230Sgibbs		return(row);
3501590Srgrimes	}
3511590Srgrimes	wmove(wnd, row++, col);
35239230Sgibbs	histogram(mb_per_second, 50, 1.0);
3531590Srgrimes	wmove(wnd, row++, col);
35439230Sgibbs	histogram(transfers_per_second, 50, 1.0);
35539230Sgibbs	if (kbpt) {
3561590Srgrimes		wmove(wnd, row++, col);
35739230Sgibbs		histogram(kb_per_transfer, 50, 1.0);
3581590Srgrimes	}
35939230Sgibbs
36039230Sgibbs	return(row);
36139230Sgibbs
3621590Srgrimes}
3631590Srgrimes
3641590Srgrimesstatic void
3651590Srgrimesstat1(row, o)
3661590Srgrimes	int row, o;
3671590Srgrimes{
3681590Srgrimes	register int i;
3691590Srgrimes	double time;
3701590Srgrimes
3711590Srgrimes	time = 0;
3721590Srgrimes	for (i = 0; i < CPUSTATES; i++)
37339230Sgibbs		time += cur.cp_time[i];
3741590Srgrimes	if (time == 0.0)
3751590Srgrimes		time = 1.0;
3761590Srgrimes	wmove(wnd, row, INSET);
3771590Srgrimes#define CPUSCALE	0.5
37839230Sgibbs	histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE);
3791590Srgrimes}
3801590Srgrimes
3811590Srgrimesstatic void
3821590Srgrimeshistogram(val, colwidth, scale)
38339230Sgibbs	long double val;
3841590Srgrimes	int colwidth;
3851590Srgrimes	double scale;
3861590Srgrimes{
3871590Srgrimes	char buf[10];
3881590Srgrimes	register int k;
3891590Srgrimes	register int v = (int)(val * scale) + 0.5;
3901590Srgrimes
3911590Srgrimes	k = MIN(v, colwidth);
3921590Srgrimes	if (v > colwidth) {
39339230Sgibbs		snprintf(buf, sizeof(buf), "%5.2Lf", val);
3941590Srgrimes		k -= strlen(buf);
3951590Srgrimes		while (k--)
3961590Srgrimes			waddch(wnd, 'X');
3971590Srgrimes		waddstr(wnd, buf);
3981590Srgrimes		return;
3991590Srgrimes	}
4001590Srgrimes	while (k--)
4011590Srgrimes		waddch(wnd, 'X');
4021590Srgrimes	wclrtoeol(wnd);
4031590Srgrimes}
4041590Srgrimes
4051590Srgrimesint
4061590Srgrimescmdiostat(cmd, args)
4071590Srgrimes	char *cmd, *args;
4081590Srgrimes{
4091590Srgrimes
41039230Sgibbs	if (prefix(cmd, "kbpt"))
41139230Sgibbs		kbpt = !kbpt;
4121590Srgrimes	else if (prefix(cmd, "numbers"))
4131590Srgrimes		numbers = 1;
4141590Srgrimes	else if (prefix(cmd, "bars"))
4151590Srgrimes		numbers = 0;
41639230Sgibbs	else if (!dscmd(cmd, args, 100, &cur))
4171590Srgrimes		return (0);
4181590Srgrimes	wclear(wnd);
4191590Srgrimes	labeliostat();
4201590Srgrimes	refresh();
4211590Srgrimes	return (1);
4221590Srgrimes}
423