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
35 static char sccsid[] = "@(#)iostat.c	8.1 (Berkeley) 6/6/93";
36 #endif
37 static 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"
52 extern struct _disk	cur, last;
53 
54 static  int linesperregion;
55 static  double etime;
56 static  int numbers = 0;		/* default display bar graphs */
57 static  int secs = 0;			/* default seconds shown */
58 static	int split = 0;			/* whether to split r/w stats */
59 
60 static int barlabels(int);
61 static void histogram(double, int, double);
62 static int numlabels(int);
63 static int stats(int, int, int);
64 static void stat1(int, int);
65 
66 
67 WINDOW *
openiostat(void)68 openiostat(void)
69 {
70 	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
71 }
72 
73 void
closeiostat(WINDOW * w)74 closeiostat(WINDOW *w)
75 {
76 	if (w == NULL)
77 		return;
78 	wclear(w);
79 	wrefresh(w);
80 	delwin(w);
81 }
82 
83 int
initiostat(void)84 initiostat(void)
85 {
86 	dkinit(1);
87 	dkreadstats();
88 	return (1);
89 }
90 
91 void
fetchiostat(void)92 fetchiostat(void)
93 {
94 	if (cur.dk_ndrive == 0)
95 		return;
96 	dkreadstats();
97 }
98 
99 #define	INSET	10
100 
101 void
labeliostat(void)102 labeliostat(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 
121 static int
numlabels(int row)122 numlabels(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 
167 static int
barlabels(int row)168 barlabels(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 
201 void
showiostat(void)202 showiostat(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 
260 static int
stats(int row,int col,int dn)261 stats(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 
307 static void
stat1(int row,int o)308 stat1(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 
323 static void
histogram(double val,int colwidth,double scale)324 histogram(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 
341 int
cmdiostat(char * cmd,char * args)342 cmdiostat(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