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