1 /* $OpenBSD: iostat.c,v 1.47 2023/03/08 04:43:13 guenther 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 #include <sys/limits.h>
66 #include <sys/time.h>
67 #include <sys/sched.h>
68
69 #include <err.h>
70 #include <ctype.h>
71 #include <limits.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <time.h>
77 #include <unistd.h>
78 #include <kvm.h>
79
80 #include "dkstats.h"
81
82 /* Defined in dkstats.c */
83 extern struct _disk cur, last;
84 extern int dk_ndrive;
85
86 /* Namelist and memory files. */
87 kvm_t *kd;
88 char *nlistf, *memf;
89
90 int hz, reps;
91 time_t interval;
92 static int todo = 0;
93
94 volatile sig_atomic_t wantheader;
95
96 #define ISSET(x, a) ((x) & (a))
97 #define SHOW_CPU 0x0001
98 #define SHOW_TTY 0x0002
99 #define SHOW_STATS_1 0x0004
100 #define SHOW_STATS_2 0x0008
101 #define SHOW_TOTALS 0x0080
102
103 static void cpustats(void);
104 static void disk_stats(double);
105 static void disk_stats2(double);
106 static void sigalarm(int);
107 static void sigheader(int);
108 static void header(void);
109 static void usage(void);
110 static void display(void);
111 static void selectdrives(char **);
112
113 void dkswap(void);
114 void dkreadstats(void);
115 int dkinit(int);
116
117 int
main(int argc,char * argv[])118 main(int argc, char *argv[])
119 {
120 struct itimerval itv;
121 const char *errstr;
122 sigset_t empty;
123 int ch, hdrcnt;
124
125 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:")) != -1)
126 switch(ch) {
127 case 'c':
128 reps = strtonum(optarg, 1, INT_MAX, &errstr);
129 if (errstr)
130 errx(1, "repetition count is %s", errstr);
131 break;
132 case 'C':
133 todo |= SHOW_CPU;
134 break;
135 case 'd':
136 todo |= SHOW_STATS_1;
137 break;
138 case 'D':
139 todo |= SHOW_STATS_2;
140 break;
141 case 'I':
142 todo |= SHOW_TOTALS;
143 break;
144 case 'M':
145 memf = optarg;
146 break;
147 case 'N':
148 nlistf = optarg;
149 break;
150 case 'T':
151 todo |= SHOW_TTY;
152 break;
153 case 'w':
154 interval = strtonum(optarg, 1, UINT_MAX, &errstr);
155 if (errstr)
156 errx(1, "wait is %s", errstr);
157 break;
158 default:
159 usage();
160 }
161 argc -= optind;
162 argv += optind;
163
164 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_1 | SHOW_STATS_2))
165 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
166
167 dkinit(0);
168
169 if (unveil("/", "") == -1)
170 err(1, "unveil /");
171 if (unveil(NULL, NULL) == -1)
172 err(1, "unveil");
173
174 dkreadstats();
175 selectdrives(argv);
176
177 /* print a new header on sigcont */
178 signal(SIGCONT, sigheader);
179
180 if (interval != 0) {
181 if (signal(SIGALRM, sigalarm) == SIG_ERR)
182 err(1, "signal");
183 sigemptyset(&empty);
184 itv.it_value.tv_sec = interval;
185 itv.it_value.tv_usec = 0;
186 itv.it_interval = itv.it_value;
187 if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
188 err(1, "setitimer");
189 }
190
191 for (hdrcnt = 1;;) {
192 if (!--hdrcnt || wantheader) {
193 header();
194 hdrcnt = 20;
195 wantheader = 0;
196 }
197
198 if (!ISSET(todo, SHOW_TOTALS))
199 dkswap();
200 display();
201
202 if (reps >= 0 && --reps <= 0)
203 break;
204 sigsuspend(&empty);
205 dkreadstats();
206 if (last.dk_ndrive != cur.dk_ndrive)
207 wantheader = 1;
208 }
209 exit(0);
210 }
211
212 static void
sigalarm(int signo)213 sigalarm(int signo)
214 {
215 }
216
217 static void
sigheader(int signo)218 sigheader(int signo)
219 {
220 wantheader = 1;
221 }
222
223 static void
header(void)224 header(void)
225 {
226 int i;
227 static int printedheader = 0;
228
229 if (printedheader && !isatty(STDOUT_FILENO))
230 return;
231
232 /* Main Headers. */
233 if (ISSET(todo, SHOW_TTY)) {
234 if (ISSET(todo, SHOW_TOTALS))
235 printf(" tty");
236 else
237 printf(" tty");
238 }
239
240 if (ISSET(todo, SHOW_STATS_1))
241 for (i = 0; i < dk_ndrive; i++)
242 if (cur.dk_select[i]) {
243 printf(" %18.18s ", cur.dk_name[i]);
244 }
245 if (ISSET(todo, SHOW_STATS_2))
246 for (i = 0; i < dk_ndrive; i++)
247 if (cur.dk_select[i])
248 printf(" %17.17s ", cur.dk_name[i]);
249
250 if (ISSET(todo, SHOW_CPU))
251 printf(" cpu");
252 printf("\n");
253
254 /* Sub-Headers. */
255 if (ISSET(todo, SHOW_TTY)) {
256 if (ISSET(todo, SHOW_TOTALS))
257 printf(" tin tout");
258 else
259 printf(" tin tout");
260 }
261
262 if (ISSET(todo, SHOW_STATS_1))
263 for (i = 0; i < dk_ndrive; i++)
264 if (cur.dk_select[i]) {
265 if (ISSET(todo, SHOW_TOTALS))
266 printf(" KB/t xfr MB ");
267 else
268 printf(" KB/t t/s MB/s ");
269 }
270 if (ISSET(todo, SHOW_STATS_2))
271 for (i = 0; i < dk_ndrive; i++)
272 if (cur.dk_select[i])
273 printf(" KB xfr time ");
274
275 if (ISSET(todo, SHOW_CPU))
276 printf(" us ni sy sp in id");
277 printf("\n");
278 }
279
280 static void
disk_stats(double etime)281 disk_stats(double etime)
282 {
283 int dn;
284 double atime, mbps;
285
286 for (dn = 0; dn < dk_ndrive; ++dn) {
287 if (!cur.dk_select[dn])
288 continue;
289
290 /* average Kbytes per transfer. */
291 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
292 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
293 (1024.0)) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
294 else
295 mbps = 0.0;
296
297 printf(" %5.2f", mbps);
298
299 /* average transfers per second. */
300 if (ISSET(todo, SHOW_TOTALS))
301 printf(" %5.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
302 else
303 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
304
305 /* time busy in disk activity */
306 atime = (double)cur.dk_time[dn].tv_sec +
307 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
308
309 /* Megabytes per second. */
310 if (atime != 0.0)
311 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
312 (double)(1024 * 1024);
313 else
314 mbps = 0;
315 if (ISSET(todo, SHOW_TOTALS))
316 printf(" %6.2f ", mbps / etime);
317 else
318 printf(" %7.2f ", mbps / etime);
319 }
320 }
321
322 static void
disk_stats2(double etime)323 disk_stats2(double etime)
324 {
325 int dn;
326 double atime;
327
328 for (dn = 0; dn < dk_ndrive; ++dn) {
329 if (!cur.dk_select[dn])
330 continue;
331
332 /* average kbytes per second. */
333 printf(" %7.0f",
334 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / (1024.0) / etime);
335
336 /* average transfers per second. */
337 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
338
339 /* average time busy in disk activity. */
340 atime = (double)cur.dk_time[dn].tv_sec +
341 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
342 printf(" %4.2f ", atime / etime);
343 }
344 }
345
346 static void
cpustats(void)347 cpustats(void)
348 {
349 int state;
350 double t = 0;
351
352 for (state = 0; state < CPUSTATES; ++state)
353 t += cur.cp_time[state];
354 if (!t)
355 t = 1.0;
356 /* States are generally never 100% and can use %3.0f. */
357 for (state = 0; state < CPUSTATES; ++state)
358 printf("%3.0f", 100. * cur.cp_time[state] / t);
359 }
360
361 static void
usage(void)362 usage(void)
363 {
364 fprintf(stderr,
365 "usage: iostat [-CDdIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n");
366 exit(1);
367 }
368
369 static void
display(void)370 display(void)
371 {
372 int i;
373 double etime;
374
375 /* Sum up the elapsed ticks. */
376 etime = 0.0;
377 for (i = 0; i < CPUSTATES; i++)
378 etime += cur.cp_time[i];
379 if (etime == 0.0)
380 etime = 1.0;
381 /* Convert to seconds. */
382 etime /= (float)hz;
383
384 /* If we're showing totals only, then don't divide by the
385 * system time.
386 */
387 if (ISSET(todo, SHOW_TOTALS))
388 etime = 1.0;
389
390 if (ISSET(todo, SHOW_TTY)) {
391 if (ISSET(todo, SHOW_TOTALS))
392 printf("%6.0f %8.0f", cur.tk_nin / etime,
393 cur.tk_nout / etime);
394 else
395 printf("%4.0f %4.0f", cur.tk_nin / etime,
396 cur.tk_nout / etime);
397 }
398
399 if (ISSET(todo, SHOW_STATS_1))
400 disk_stats(etime);
401
402 if (ISSET(todo, SHOW_STATS_2))
403 disk_stats2(etime);
404
405 if (ISSET(todo, SHOW_CPU))
406 cpustats();
407
408 printf("\n");
409 fflush(stdout);
410 }
411
412 static void
selectdrives(char * argv[])413 selectdrives(char *argv[])
414 {
415 const char *errstr;
416 int i, ndrives;
417
418 /*
419 * Choose drives to be displayed. Priority goes to (in order) drives
420 * supplied as arguments and default drives. If everything isn't
421 * filled in and there are drives not taken care of, display the first
422 * few that fit.
423 *
424 * The backward compatibility syntax is:
425 * iostat [ drives ] [ interval [ count ] ]
426 */
427 for (ndrives = 0; *argv; ++argv) {
428 if (isdigit((unsigned char)**argv))
429 break;
430 for (i = 0; i < dk_ndrive; i++) {
431 if (strcmp(cur.dk_name[i], *argv))
432 continue;
433 cur.dk_select[i] = 1;
434 ++ndrives;
435 break;
436 }
437 if (i == dk_ndrive)
438 errx(1, "invalid interval or drive name: %s", *argv);
439 }
440 if (*argv) {
441 interval = strtonum(*argv, 1, UINT_MAX, &errstr);
442 if (errstr)
443 errx(1, "interval is %s", errstr);
444 if (*++argv) {
445 reps = strtonum(*argv, 1, INT_MAX, &errstr);
446 if (errstr)
447 errx(1, "repetition count is %s", errstr);
448 ++argv;
449 }
450 }
451 if (*argv)
452 errx(1, "too many arguments");
453
454 if (interval) {
455 if (!reps)
456 reps = -1;
457 } else
458 if (reps)
459 interval = 1;
460
461 /* Pick up to 4 drives if none specified. */
462 if (ndrives == 0)
463 for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
464 if (cur.dk_select[i])
465 continue;
466 cur.dk_select[i] = 1;
467 ++ndrives;
468 }
469 }
470