iostat.c revision 1.23
1/*	$OpenBSD: iostat.c,v 1.23 2004/02/15 22:56:12 tedu Exp $	*/
2/*	$NetBSD: iostat.c,v 1.5 1996/05/10 23:16:35 thorpej Exp $	*/
3
4/*
5 * Copyright (c) 1980, 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)iostat.c	8.1 (Berkeley) 6/6/93";
36#endif
37static char rcsid[] = "$OpenBSD: iostat.c,v 1.23 2004/02/15 22:56:12 tedu Exp $";
38#endif /* not lint */
39
40#include <sys/param.h>
41#include <sys/dkstat.h>
42#include <sys/buf.h>
43#include <sys/time.h>
44
45#include <string.h>
46#include <stdlib.h>
47#include <paths.h>
48#include "systat.h"
49#include "extern.h"
50
51#include "dkstats.h"
52extern struct _disk	cur, last;
53
54static  int linesperregion;
55static  double etime;
56static  int numbers = 0;		/* default display bar graphs */
57static  int secs = 0;			/* default seconds shown */
58static	int split = 0;			/* whether to split r/w stats */
59
60static int barlabels(int);
61static void histogram(double, int, double);
62static int numlabels(int);
63static int stats(int, int, int);
64static void stat1(int, int);
65
66
67WINDOW *
68openiostat(void)
69{
70	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
71}
72
73void
74closeiostat(WINDOW *w)
75{
76	if (w == NULL)
77		return;
78	wclear(w);
79	wrefresh(w);
80	delwin(w);
81}
82
83int
84initiostat(void)
85{
86	dkinit(1);
87	dkreadstats();
88	return (1);
89}
90
91void
92fetchiostat(void)
93{
94	if (cur.dk_ndrive == 0)
95		return;
96	dkreadstats();
97}
98
99#define	INSET	10
100
101void
102labeliostat(void)
103{
104	int row;
105
106	row = 0;
107	wmove(wnd, row, 0); wclrtobot(wnd);
108	mvwaddstr(wnd, row++, INSET,
109	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
110	mvwaddstr(wnd, row++, 0, "cpu  user|");
111	mvwaddstr(wnd, row++, 0, "     nice|");
112	mvwaddstr(wnd, row++, 0, "   system|");
113	mvwaddstr(wnd, row++, 0, "interrupt|");
114	mvwaddstr(wnd, row++, 0, "     idle|");
115	if (numbers)
116		row = numlabels(row + 1);
117	else
118		row = barlabels(row + 1);
119}
120
121static int
122numlabels(int row)
123{
124	int i, col, regions, ndrives;
125
126	if (cur.dk_ndrive == 0) {
127		mvwaddstr(wnd, row++, INSET, "No drives attached.");
128		return (row);
129	}
130#define COLWIDTH	(split ? 30 : 17)
131#define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
132	for (ndrives = 0, i = 0; i < cur.dk_ndrive; i++)
133		if (cur.dk_select[i])
134			ndrives++;
135	regions = howmany(ndrives, DRIVESPERLINE);
136	/*
137	 * Deduct -regions for blank line after each scrolling region.
138	 */
139	linesperregion = (wnd->_maxy - row - regions) / regions;
140	/*
141	 * Minimum region contains space for two
142	 * label lines and one line of statistics.
143	 */
144	if (linesperregion < 3)
145		linesperregion = 3;
146	col = INSET;
147	for (i = 0; i < cur.dk_ndrive; i++)
148		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
149			if (col + COLWIDTH >= wnd->_maxx) {
150				col = INSET, row += linesperregion + 1;
151				if (row > wnd->_maxy - (linesperregion + 1))
152					break;
153			}
154			mvwaddstr(wnd, row, col + 4, cur.dk_name[i]);
155			if (split)
156				mvwaddstr(wnd, row + 1, col, " rKBps wKBps "
157				    "rtps wtps  sec");
158			else
159				mvwaddstr(wnd, row + 1, col, " KBps tps  sec");
160			col += COLWIDTH;
161		}
162	if (col)
163		row += linesperregion + 1;
164	return (row);
165}
166
167static int
168barlabels(int row)
169{
170	int i;
171
172	if (cur.dk_ndrive == 0) {
173		mvwaddstr(wnd, row++, INSET, "No drives attached.");
174		return (row);
175	}
176	mvwaddstr(wnd, row++, INSET,
177	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
178	linesperregion = 2 + secs;
179	for (i = 0; i < dk_ndrive; i++)
180		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
181			if (row > wnd->_maxy - linesperregion)
182				break;
183			if (split) {
184				mvwprintw(wnd, row++, 0, "%4.4s rKps|",
185				    cur.dk_name[i]);
186				mvwaddstr(wnd, row++, 0, "     wKps|");
187				mvwaddstr(wnd, row++, 0, "     rtps|");
188				mvwaddstr(wnd, row++, 0, "     wtps|");
189			} else {
190				mvwprintw(wnd, row++, 0, "%4.4s  Kps|",
191				    cur.dk_name[i]);
192				mvwaddstr(wnd, row++, 0, "      tps|");
193			}
194			if (secs)
195				mvwaddstr(wnd, row++, 0, "     msec|");
196		}
197	return (row);
198}
199
200
201void
202showiostat(void)
203{
204	int i, row, col;
205
206	dkswap();
207
208	etime = 0;
209	for (i = 0; i < CPUSTATES; i++) {
210		etime += cur.cp_time[i];
211	}
212	if (etime == 0.0)
213		etime = 1.0;
214	etime /= (float) hz;
215	row = 1;
216
217	/*
218	 * Interrupt CPU state not calculated yet.
219	 */
220	for (i = 0; i < CPUSTATES; i++)
221		stat1(row++, i);
222
223	if (last.dk_ndrive != cur.dk_ndrive)
224		labeliostat();
225
226	if (cur.dk_ndrive == 0)
227		return;
228
229	if (!numbers) {
230		row += 2;
231		for (i = 0; i < cur.dk_ndrive; i++)
232			if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
233				if (row > wnd->_maxy - linesperregion)
234					break;
235				row = stats(row, INSET, i);
236			}
237		return;
238	}
239	col = INSET;
240	wmove(wnd, row + linesperregion, 0);
241	wdeleteln(wnd);
242	wmove(wnd, row + 3, 0);
243	winsertln(wnd);
244	for (i = 0; i < cur.dk_ndrive; i++)
245		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
246			if (col + COLWIDTH >= wnd->_maxx) {
247				col = INSET, row += linesperregion + 1;
248				if (row > wnd->_maxy - (linesperregion + 1))
249					break;
250				wmove(wnd, row + linesperregion, 0);
251				wdeleteln(wnd);
252				wmove(wnd, row + 3, 0);
253				winsertln(wnd);
254			}
255			(void) stats(row + 3, col, i);
256			col += COLWIDTH;
257		}
258}
259
260static int
261stats(int row, int col, int dn)
262{
263	double atime, rwords, wwords;
264
265	/* time busy in disk activity */
266	atime = (double)cur.dk_time[dn].tv_sec +
267		((double)cur.dk_time[dn].tv_usec / (double)1000000);
268
269	rwords = cur.dk_rbytes[dn] / 1024.0;	/* # of K read */
270	wwords = cur.dk_wbytes[dn] / 1024.0;	/* # of K written */
271	if (numbers) {
272		if (split)
273			mvwprintw(wnd, row, col, "%6.0f%6.0f%5.0f%5.0f%5.1f",
274			    rwords / etime, wwords / etime, cur.dk_rxfer[dn] /
275			    etime, cur.dk_wxfer[dn] / etime, atime / etime);
276		else
277			mvwprintw(wnd, row, col, "%5.0f%4.0f%5.1f",
278			    (rwords + wwords) / etime,
279			    (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime,
280			    atime / etime);
281		return (row);
282	}
283	if (split) {
284		wmove(wnd, row++, col);
285		histogram(rwords / etime, 50, 0.5);
286		wmove(wnd, row++, col);
287		histogram(wwords / etime, 50, 0.5);
288		wmove(wnd, row++, col);
289		histogram(cur.dk_rxfer[dn] / etime, 50, 0.5);
290		wmove(wnd, row++, col);
291		histogram(cur.dk_wxfer[dn] / etime, 50, 0.5);
292	} else {
293		wmove(wnd, row++, col);
294		histogram((rwords + wwords) / etime, 50, 0.5);
295		wmove(wnd, row++, col);
296		histogram((cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime, 50,
297		    0.5);
298	}
299	if (secs) {
300		wmove(wnd, row++, col);
301		atime *= 1000;	/* In milliseconds */
302		histogram(atime / etime, 50, 0.5);
303	}
304	return (row);
305}
306
307static void
308stat1(int row, int o)
309{
310	int i;
311	double time;
312
313	time = 0;
314	for (i = 0; i < CPUSTATES; i++)
315		time += cur.cp_time[i];
316	if (time == 0.0)
317		time = 1.0;
318	wmove(wnd, row, INSET);
319#define CPUSCALE	0.5
320	histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE);
321}
322
323static void
324histogram(double val, int colwidth, double scale)
325{
326	int v = (int)(val * scale + 0.5);
327	int factor = 1;
328	int y, x;
329
330	while (v > colwidth) {
331		v = (v + 5) / 10;
332		factor *= 10;
333	}
334	getyx(wnd, y, x);
335	wclrtoeol(wnd);
336	whline(wnd, 'X', v);
337	if (factor != 1)
338		mvwprintw(wnd, y, x + colwidth + 1, "* %d ", factor);
339}
340
341int
342cmdiostat(char *cmd, char *args)
343{
344
345	if (prefix(cmd, "secs"))
346		secs = !secs;
347	else if (prefix(cmd, "numbers"))
348		numbers = 1;
349	else if (prefix(cmd, "bars"))
350		numbers = 0;
351	else if (prefix(cmd, "split"))
352		split = ~split;
353	else if (!dkcmd(cmd, args))
354		return (0);
355	wclear(wnd);
356	labeliostat();
357	refresh();
358	return (1);
359}
360