xref: /openbsd-src/usr.bin/systat/iostat.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: iostat.c,v 1.21 2003/06/03 02:56:17 millert 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.21 2003/06/03 02:56:17 millert 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 
59 static int barlabels(int);
60 static void histogram(double, int, double);
61 static int numlabels(int);
62 static int stats(int, int, int);
63 static void stat1(int, int);
64 
65 
66 WINDOW *
67 openiostat(void)
68 {
69 	return (subwin(stdscr, LINES-1-5, 0, 5, 0));
70 }
71 
72 void
73 closeiostat(WINDOW *w)
74 {
75 	if (w == NULL)
76 		return;
77 	wclear(w);
78 	wrefresh(w);
79 	delwin(w);
80 }
81 
82 int
83 initiostat(void)
84 {
85 	dkinit(1);
86 	dkreadstats();
87 	return (1);
88 }
89 
90 void
91 fetchiostat(void)
92 {
93 	if (cur.dk_ndrive == 0)
94 		return;
95 	dkreadstats();
96 }
97 
98 #define	INSET	10
99 
100 void
101 labeliostat(void)
102 {
103 	int row;
104 
105 	row = 0;
106 	wmove(wnd, row, 0); wclrtobot(wnd);
107 	mvwaddstr(wnd, row++, INSET,
108 	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
109 	mvwaddstr(wnd, row++, 0, "cpu  user|");
110 	mvwaddstr(wnd, row++, 0, "     nice|");
111 	mvwaddstr(wnd, row++, 0, "   system|");
112 	mvwaddstr(wnd, row++, 0, "interrupt|");
113 	mvwaddstr(wnd, row++, 0, "     idle|");
114 	if (numbers)
115 		row = numlabels(row + 1);
116 	else
117 		row = barlabels(row + 1);
118 }
119 
120 static int
121 numlabels(int row)
122 {
123 	int i, col, regions, ndrives;
124 
125 	if (cur.dk_ndrive == 0) {
126 		mvwaddstr(wnd, row++, INSET, "No drives attached.");
127 		return (row);
128 	}
129 #define COLWIDTH	17
130 #define DRIVESPERLINE	((wnd->_maxx - INSET) / COLWIDTH)
131 	for (ndrives = 0, i = 0; i < cur.dk_ndrive; i++)
132 		if (cur.dk_select[i])
133 			ndrives++;
134 	regions = howmany(ndrives, DRIVESPERLINE);
135 	/*
136 	 * Deduct -regions for blank line after each scrolling region.
137 	 */
138 	linesperregion = (wnd->_maxy - row - regions) / regions;
139 	/*
140 	 * Minimum region contains space for two
141 	 * label lines and one line of statistics.
142 	 */
143 	if (linesperregion < 3)
144 		linesperregion = 3;
145 	col = INSET;
146 	for (i = 0; i < cur.dk_ndrive; i++)
147 		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
148 			if (col + COLWIDTH >= wnd->_maxx) {
149 				col = INSET, row += linesperregion + 1;
150 				if (row > wnd->_maxy - (linesperregion + 1))
151 					break;
152 			}
153 			mvwaddstr(wnd, row, col + 4, cur.dk_name[i]);
154 			mvwaddstr(wnd, row + 1, col, " KBps tps  sec");
155 			col += COLWIDTH;
156 		}
157 	if (col)
158 		row += linesperregion + 1;
159 	return (row);
160 }
161 
162 static int
163 barlabels(int row)
164 {
165 	int i;
166 
167 	if (cur.dk_ndrive == 0) {
168 		mvwaddstr(wnd, row++, INSET, "No drives attached.");
169 		return (row);
170 	}
171 	mvwaddstr(wnd, row++, INSET,
172 	    "/0   /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
173 	linesperregion = 2 + secs;
174 	for (i = 0; i < dk_ndrive; i++)
175 		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
176 			if (row > wnd->_maxy - linesperregion)
177 				break;
178 			mvwprintw(wnd, row++, 0, "%4.4s  Kps|", cur.dk_name[i]);
179 			mvwaddstr(wnd, row++, 0, "      tps|");
180 			if (secs)
181 				mvwaddstr(wnd, row++, 0, "     msec|");
182 		}
183 	return (row);
184 }
185 
186 
187 void
188 showiostat(void)
189 {
190 	int i, row, col;
191 
192 	dkswap();
193 
194 	etime = 0;
195 	for (i = 0; i < CPUSTATES; i++) {
196 		etime += cur.cp_time[i];
197 	}
198 	if (etime == 0.0)
199 		etime = 1.0;
200 	etime /= (float) hz;
201 	row = 1;
202 
203 	/*
204 	 * Interrupt CPU state not calculated yet.
205 	 */
206 	for (i = 0; i < CPUSTATES; i++)
207 		stat1(row++, i);
208 
209 	if (last.dk_ndrive != cur.dk_ndrive)
210 		labeliostat();
211 
212 	if (cur.dk_ndrive == 0)
213 		return;
214 
215 	if (!numbers) {
216 		row += 2;
217 		for (i = 0; i < cur.dk_ndrive; i++)
218 			if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
219 				if (row > wnd->_maxy - linesperregion)
220 					break;
221 				row = stats(row, INSET, i);
222 			}
223 		return;
224 	}
225 	col = INSET;
226 	wmove(wnd, row + linesperregion, 0);
227 	wdeleteln(wnd);
228 	wmove(wnd, row + 3, 0);
229 	winsertln(wnd);
230 	for (i = 0; i < cur.dk_ndrive; i++)
231 		if (cur.dk_select[i] /*&& cur.dk_bytes[i] != 0.0*/) {
232 			if (col + COLWIDTH >= wnd->_maxx) {
233 				col = INSET, row += linesperregion + 1;
234 				if (row > wnd->_maxy - (linesperregion + 1))
235 					break;
236 				wmove(wnd, row + linesperregion, 0);
237 				wdeleteln(wnd);
238 				wmove(wnd, row + 3, 0);
239 				winsertln(wnd);
240 			}
241 			(void) stats(row + 3, col, i);
242 			col += COLWIDTH;
243 		}
244 }
245 
246 static int
247 stats(int row, int col, int dn)
248 {
249 	double atime, words;
250 
251 	/* time busy in disk activity */
252 	atime = (double)cur.dk_time[dn].tv_sec +
253 		((double)cur.dk_time[dn].tv_usec / (double)1000000);
254 
255 	words = cur.dk_bytes[dn] / 1024.0;	/* # of K transferred */
256 	if (numbers) {
257 		mvwprintw(wnd, row, col, "%5.0f%4.0f%5.1f",
258 		    words / etime, cur.dk_xfer[dn] / etime, atime / etime);
259 		return (row);
260 	}
261 	wmove(wnd, row++, col);
262 	histogram(words / etime, 50, 0.5);
263 	wmove(wnd, row++, col);
264 	histogram(cur.dk_xfer[dn] / etime, 50, 0.5);
265 	if (secs) {
266 		wmove(wnd, row++, col);
267 		atime *= 1000;	/* In milliseconds */
268 		histogram(atime / etime, 50, 0.5);
269 	}
270 	return (row);
271 }
272 
273 static void
274 stat1(int row, int o)
275 {
276 	int i;
277 	double time;
278 
279 	time = 0;
280 	for (i = 0; i < CPUSTATES; i++)
281 		time += cur.cp_time[i];
282 	if (time == 0.0)
283 		time = 1.0;
284 	wmove(wnd, row, INSET);
285 #define CPUSCALE	0.5
286 	histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE);
287 }
288 
289 static void
290 histogram(double val, int colwidth, double scale)
291 {
292 	int v = (int)(val * scale + 0.5);
293 	int factor = 1;
294 	int y, x;
295 
296 	while (v > colwidth) {
297 		v = (v + 5) / 10;
298 		factor *= 10;
299 	}
300 	getyx(wnd, y, x);
301 	wclrtoeol(wnd);
302 	whline(wnd, 'X', v);
303 	if (factor != 1)
304 		mvwprintw(wnd, y, x + colwidth + 1, "* %d ", factor);
305 }
306 
307 int
308 cmdiostat(char *cmd, char *args)
309 {
310 
311 	if (prefix(cmd, "secs"))
312 		secs = !secs;
313 	else if (prefix(cmd, "numbers"))
314 		numbers = 1;
315 	else if (prefix(cmd, "bars"))
316 		numbers = 0;
317 	else if (!dkcmd(cmd, args))
318 		return (0);
319 	wclear(wnd);
320 	labeliostat();
321 	refresh();
322 	return (1);
323 }
324