1 /* $OpenBSD: iostat.c,v 1.22 2005/04/01 03:32:47 deraadt Exp $ */
2 /* $NetBSD: iostat.c,v 1.10 1996/10/25 18:21:58 scottr Exp $ */
3
4 /*
5 * Copyright (c) 1996 John M. Vinopal
6 * 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 for the NetBSD Project
19 * by John M. Vinopal.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 /*-
37 * Copyright (c) 1986, 1991, 1993
38 * The Regents of the University of California. All rights reserved.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 */
64
65 #ifndef lint
66 static char copyright[] =
67 "@(#) Copyright (c) 1986, 1991, 1993\n\
68 The Regents of the University of California. All rights reserved.\n";
69 #endif /* not lint */
70
71 #ifndef lint
72 #if 0
73 static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94";
74 #else
75 static char *rcsid = "$NetBSD: iostat.c,v 1.10 1996/10/25 18:21:58 scottr Exp $"
76 ;
77 #endif
78 #endif /* not lint */
79
80 #include <sys/dkstat.h>
81 #include <sys/time.h>
82
83 #include <err.h>
84 #include <ctype.h>
85 #include <signal.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 #include <kvm.h>
91
92 #include "dkstats.h"
93
94 /* Defined in dkstats.c */
95 extern struct _disk cur, last;
96 extern int dk_ndrive;
97
98 /* Namelist and memory files. */
99 kvm_t *kd;
100 char *nlistf, *memf;
101
102 int hz, reps, interval;
103 static int todo = 0;
104
105 volatile sig_atomic_t wantheader;
106
107 #define ISSET(x, a) ((x) & (a))
108 #define SHOW_CPU 0x0001
109 #define SHOW_TTY 0x0002
110 #define SHOW_STATS_1 0x0004
111 #define SHOW_STATS_2 0x0008
112 #define SHOW_TOTALS 0x0080
113
114 static void cpustats(void);
115 static void disk_stats(double);
116 static void disk_stats2(double);
117 static void sigheader(int);
118 static void header(void);
119 static void usage(void);
120 static void display(void);
121 static void selectdrives(int, char **);
122
123 void dkswap(void);
124 void dkreadstats(void);
125 int dkinit(int);
126
127 int
main(int argc,char * argv[])128 main(int argc, char *argv[])
129 {
130 int ch, hdrcnt;
131 struct timeval tv;
132
133 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:")) != -1)
134 switch(ch) {
135 case 'c':
136 if ((reps = atoi(optarg)) <= 0)
137 errx(1, "repetition count <= 0.");
138 break;
139 case 'C':
140 todo |= SHOW_CPU;
141 break;
142 case 'd':
143 todo |= SHOW_STATS_1;
144 break;
145 case 'D':
146 todo |= SHOW_STATS_2;
147 break;
148 case 'I':
149 todo |= SHOW_TOTALS;
150 break;
151 case 'M':
152 memf = optarg;
153 break;
154 case 'N':
155 nlistf = optarg;
156 break;
157 case 'T':
158 todo |= SHOW_TTY;
159 break;
160 case 'w':
161 if ((interval = atoi(optarg)) <= 0)
162 errx(1, "interval <= 0.");
163 break;
164 case '?':
165 default:
166 usage();
167 }
168 argc -= optind;
169 argv += optind;
170
171 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_1 | SHOW_STATS_2))
172 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
173
174 dkinit(0);
175 dkreadstats();
176 selectdrives(argc, argv);
177
178 tv.tv_sec = interval;
179 tv.tv_usec = 0;
180
181 /* print a new header on sigcont */
182 (void)signal(SIGCONT, sigheader);
183
184 for (hdrcnt = 1;;) {
185 if (!--hdrcnt || wantheader) {
186 header();
187 hdrcnt = 20;
188 wantheader = 0;
189 }
190
191 if (!ISSET(todo, SHOW_TOTALS))
192 dkswap();
193 display();
194
195 if (reps >= 0 && --reps <= 0)
196 break;
197 select(0, NULL, NULL, NULL, &tv);
198 dkreadstats();
199 if (last.dk_ndrive != cur.dk_ndrive)
200 wantheader = 1;
201 }
202 exit(0);
203 }
204
205 static void
sigheader(int signo)206 sigheader(int signo)
207 {
208 wantheader = 1;
209 }
210
211 static void
header(void)212 header(void)
213 {
214 int i;
215
216 /* Main Headers. */
217 if (ISSET(todo, SHOW_TTY))
218 (void)printf(" tty");
219
220 if (ISSET(todo, SHOW_STATS_1))
221 for (i = 0; i < dk_ndrive; i++)
222 if (cur.dk_select[i])
223 (void)printf(" %14.14s ", cur.dk_name[i]);
224
225 if (ISSET(todo, SHOW_STATS_2))
226 for (i = 0; i < dk_ndrive; i++)
227 if (cur.dk_select[i])
228 (void)printf(" %13.13s ", cur.dk_name[i]);
229
230 if (ISSET(todo, SHOW_CPU))
231 (void)printf(" cpu");
232 printf("\n");
233
234 /* Sub-Headers. */
235 if (ISSET(todo, SHOW_TTY))
236 printf(" tin tout");
237
238 if (ISSET(todo, SHOW_STATS_1))
239 for (i = 0; i < dk_ndrive; i++)
240 if (cur.dk_select[i])
241 if (ISSET(todo, SHOW_TOTALS))
242 (void)printf(" KB/t xfr MB ");
243 else
244 (void)printf(" KB/t t/s MB/s ");
245
246 if (ISSET(todo, SHOW_STATS_2))
247 for (i = 0; i < dk_ndrive; i++)
248 if (cur.dk_select[i])
249 (void)printf(" KB xfr time ");
250
251 if (ISSET(todo, SHOW_CPU))
252 (void)printf(" us ni sy in id");
253 printf("\n");
254 }
255
256 static void
disk_stats(double etime)257 disk_stats(double etime)
258 {
259 int dn;
260 double atime, mbps;
261
262 for (dn = 0; dn < dk_ndrive; ++dn) {
263 if (!cur.dk_select[dn])
264 continue;
265
266 /* average Kbytes per transfer. */
267 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
268 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
269 (1024.0)) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
270 else
271 mbps = 0.0;
272
273 (void)printf(" %5.2f", mbps);
274
275 /* average transfers per second. */
276 (void)printf(" %3.0f",
277 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
278
279 /* time busy in disk activity */
280 atime = (double)cur.dk_time[dn].tv_sec +
281 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
282
283 /* Megabytes per second. */
284 if (atime != 0.0)
285 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
286 (double)(1024 * 1024);
287 else
288 mbps = 0;
289 (void)printf(" %4.2f ", mbps / etime);
290 }
291 }
292
293 static void
disk_stats2(double etime)294 disk_stats2(double etime)
295 {
296 int dn;
297 double atime;
298
299 for (dn = 0; dn < dk_ndrive; ++dn) {
300 if (!cur.dk_select[dn])
301 continue;
302
303 /* average kbytes per second. */
304 (void)printf(" %4.0f",
305 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / (1024.0) / etime);
306
307 /* average transfers per second. */
308 (void)printf(" %3.0f",
309 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
310
311 /* average time busy in disk activity. */
312 atime = (double)cur.dk_time[dn].tv_sec +
313 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
314 (void)printf(" %4.2f ", atime / etime);
315 }
316 }
317
318 static void
cpustats(void)319 cpustats(void)
320 {
321 int state;
322 double time;
323
324 time = 0;
325 for (state = 0; state < CPUSTATES; ++state)
326 time += cur.cp_time[state];
327 if (!time)
328 time = 1.0;
329 /* States are generally never 100% and can use %3.0f. */
330 for (state = 0; state < CPUSTATES; ++state)
331 printf("%3.0f", 100. * cur.cp_time[state] / time);
332 }
333
334 static void
usage(void)335 usage(void)
336 {
337 (void)fprintf(stderr,
338 "usage: iostat [-CdDIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n");
339 exit(1);
340 }
341
342 static void
display(void)343 display(void)
344 {
345 int i;
346 double etime;
347
348 /* Sum up the elapsed ticks. */
349 etime = 0.0;
350 for (i = 0; i < CPUSTATES; i++) {
351 etime += cur.cp_time[i];
352 }
353 if (etime == 0.0)
354 etime = 1.0;
355 /* Convert to seconds. */
356 etime /= (float)hz;
357
358 /* If we're showing totals only, then don't divide by the
359 * system time.
360 */
361 if (ISSET(todo, SHOW_TOTALS))
362 etime = 1.0;
363
364 if (ISSET(todo, SHOW_TTY))
365 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
366
367 if (ISSET(todo, SHOW_STATS_1))
368 disk_stats(etime);
369
370 if (ISSET(todo, SHOW_STATS_2))
371 disk_stats2(etime);
372
373 if (ISSET(todo, SHOW_CPU))
374 cpustats();
375
376 (void)printf("\n");
377 (void)fflush(stdout);
378 }
379
380 static void
selectdrives(int argc,char * argv[])381 selectdrives(int argc, char *argv[])
382 {
383 int i, ndrives;
384
385 /*
386 * Choose drives to be displayed. Priority goes to (in order) drives
387 * supplied as arguments and default drives. If everything isn't
388 * filled in and there are drives not taken care of, display the first
389 * few that fit.
390 *
391 * The backward compatibility #ifdefs permit the syntax:
392 * iostat [ drives ] [ interval [ count ] ]
393 */
394 #define BACKWARD_COMPATIBILITY
395 for (ndrives = 0; *argv; ++argv) {
396 #ifdef BACKWARD_COMPATIBILITY
397 if (isdigit(**argv))
398 break;
399 #endif
400 for (i = 0; i < dk_ndrive; i++) {
401 if (strcmp(cur.dk_name[i], *argv))
402 continue;
403 cur.dk_select[i] = 1;
404 ++ndrives;
405 }
406 }
407 #ifdef BACKWARD_COMPATIBILITY
408 if (*argv) {
409 interval = atoi(*argv);
410 if (*++argv)
411 reps = atoi(*argv);
412 }
413 #endif
414
415 if (interval) {
416 if (!reps)
417 reps = -1;
418 } else
419 if (reps)
420 interval = 1;
421
422 /* Pick up to 4 drives if none specified. */
423 if (ndrives == 0)
424 for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
425 if (cur.dk_select[i])
426 continue;
427 cur.dk_select[i] = 1;
428 ++ndrives;
429 }
430 }
431