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