xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision 811e6386f8c5e4a3521c7003da29ec8673e344fa)
1 /*-
2  * Copyright (c) 1986, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1986, 1991 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)iostat.c	5.9 (Berkeley) 6/27/91";
42 #endif /* not lint */
43 
44 #include <sys/param.h>
45 #include <sys/buf.h>
46 #include <sys/dkstat.h>
47 #include <signal.h>
48 #include <fcntl.h>
49 #include <nlist.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <paths.h>
56 #include <kvm.h>
57 
58 struct nlist nl[] = {
59 #define	X_DK_TIME	0
60 	{ "_dk_time" },
61 #define	X_DK_XFER	1
62 	{ "_dk_xfer" },
63 #define	X_DK_WDS	2
64 	{ "_dk_wds" },
65 #define	X_TK_NIN	3
66 	{ "_tk_nin" },
67 #define	X_TK_NOUT	4
68 	{ "_tk_nout" },
69 #define	X_DK_SEEK	5
70 	{ "_dk_seek" },
71 #define	X_CP_TIME	6
72 	{ "_cp_time" },
73 #define	X_DK_WPMS	7
74 	{ "_dk_wpms" },
75 #define	X_HZ		8
76 	{ "_hz" },
77 #define	X_PHZ		9
78 	{ "_phz" },
79 #define	X_DK_NDRIVE	10
80 	{ "_dk_ndrive" },
81 #define	X_END		10
82 #ifdef hp300
83 #define	X_HPDINIT	(X_END+1)
84 	{ "_hp_dinit" },
85 #endif
86 #ifdef tahoe
87 #define	X_VBDINIT	(X_END+1)
88 	{ "_vbdinit" },
89 #endif
90 #ifdef vax
91 	{ "_mbdinit" },
92 #define X_MBDINIT	(X_END+1)
93 	{ "_ubdinit" },
94 #define X_UBDINIT	(X_END+2)
95 #endif
96 #ifdef __386BSD__
97 #define	X_ISA_BIO	(X_END+1)
98 	{ "_isa_devtab_bio" },
99 #endif /* __386BSD__ */
100 	{ NULL },
101 };
102 
103 struct _disk {
104 	long	cp_time[CPUSTATES];
105 	long	*dk_time;
106 	long	*dk_wds;
107 	long	*dk_seek;
108 	long	*dk_xfer;
109 	long	tk_nin;
110 	long	tk_nout;
111 } cur, last;
112 
113 double etime;
114 long *dk_wpms;
115 int dk_ndrive, *dr_select, hz, kmemfd, ndrives;
116 char **dr_name;
117 
118 #define nlread(x, v) \
119 	kvm_read((void *)nl[x].n_value, (void *)&(v), sizeof(v))
120 
121 #include "names.c"				/* XXX */
122 
123 static void cpustats __P((void)), dkstats __P((void)), phdr __P((int));
124 static void usage __P((void)), err __P((const char *, ...));
125 
126 main(argc, argv)
127 	int argc;
128 	char **argv;
129 {
130 	register int i;
131 	long tmp;
132 	int ch, hdrcnt, reps, interval, phz, ndrives;
133 	char **cp, *memfile, *namelist, buf[30];
134 
135 	interval = reps = 0;
136 	namelist = memfile = NULL;
137 	while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF)
138 		switch(ch) {
139 		case 'c':
140 			reps = atoi(optarg);
141 			break;
142 		case 'M':
143 			memfile = optarg;
144 			break;
145 		case 'N':
146 			namelist = optarg;
147 			break;
148 		case 'w':
149 			interval = atoi(optarg);
150 			break;
151 		case '?':
152 		default:
153 			usage();
154 		}
155 	argc -= optind;
156 	argv += optind;
157 
158 	if (kvm_openfiles(namelist, memfile, NULL) == -1)
159 		err("kvm_openfiles: %s", kvm_geterr());
160 	if (kvm_nlist(nl) == -1)
161 		err("kvm_nlist: %s", kvm_geterr());
162 	if (nl[X_DK_NDRIVE].n_type == 0)
163 		err("dk_ndrive not found in namelist");
164 	(void)nlread(X_DK_NDRIVE, dk_ndrive);
165 	if (dk_ndrive <= 0)
166 		err("invalid dk_ndrive %d\n", dk_ndrive);
167 
168 	cur.dk_time = calloc(dk_ndrive, sizeof(long));
169 	cur.dk_wds = calloc(dk_ndrive, sizeof(long));
170 	cur.dk_seek = calloc(dk_ndrive, sizeof(long));
171 	cur.dk_xfer = calloc(dk_ndrive, sizeof(long));
172 	last.dk_time = calloc(dk_ndrive, sizeof(long));
173 	last.dk_wds = calloc(dk_ndrive, sizeof(long));
174 	last.dk_seek = calloc(dk_ndrive, sizeof(long));
175 	last.dk_xfer = calloc(dk_ndrive, sizeof(long));
176 	dr_select = calloc(dk_ndrive, sizeof(int));
177 	dr_name = calloc(dk_ndrive, sizeof(char *));
178 	dk_wpms = calloc(dk_ndrive, sizeof(long));
179 
180 	for (i = 0; i < dk_ndrive; i++) {
181 		(void)sprintf(buf, "dk%d", i);
182 		dr_name[i] = strdup(buf);
183 	}
184 	read_names();
185 	(void)nlread(X_HZ, hz);
186 	(void)nlread(X_PHZ, phz);
187 	if (phz)
188 		hz = phz;
189 	(void)kvm_read((void *)nl[X_DK_WPMS].n_value, dk_wpms,
190 		dk_ndrive * sizeof(dk_wpms));
191 
192 	/*
193 	 * Choose drives to be displayed.  Priority goes to (in order) drives
194 	 * supplied as arguments and default drives.  If everything isn't
195 	 * filled in and there are drives not taken care of, display the first
196 	 * few that fit.
197 	 *
198 	 * The backward compatibility #ifdefs permit the syntax:
199 	 *	iostat [ drives ] [ interval [ count ] ]
200 	 */
201 #define	BACKWARD_COMPATIBILITY
202 	for (ndrives = 0; *argv; ++argv) {
203 #ifdef	BACKWARD_COMPATIBILITY
204 		if (isdigit(**argv))
205 			break;
206 #endif
207 		for (i = 0; i < dk_ndrive; i++) {
208 			if (strcmp(dr_name[i], *argv))
209 				continue;
210 			dr_select[i] = 1;
211 			++ndrives;
212 		}
213 	}
214 #ifdef	BACKWARD_COMPATIBILITY
215 	if (*argv) {
216 		interval = atoi(*argv);
217 		if (*++argv)
218 			reps = atoi(*argv);
219 	}
220 #endif
221 
222 	if (interval) {
223 		if (!reps)
224 			reps = -1;
225 	} else
226 		if (reps)
227 			interval = 1;
228 
229 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
230 		if (dr_select[i] || dk_wpms[i] == 0)
231 			continue;
232 		for (cp = defdrives; *cp; cp++)
233 			if (strcmp(dr_name[i], *cp) == 0) {
234 				dr_select[i] = 1;
235 				++ndrives;
236 				break;
237 			}
238 	}
239 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
240 		if (dr_select[i])
241 			continue;
242 		dr_select[i] = 1;
243 		++ndrives;
244 	}
245 
246 	(void)signal(SIGCONT, phdr);
247 
248 	for (hdrcnt = 1;;) {
249 		if (!--hdrcnt) {
250 			phdr(0);
251 			hdrcnt = 20;
252 		}
253 		(void)kvm_read((void *)nl[X_DK_TIME].n_value,
254 		    cur.dk_time, dk_ndrive * sizeof(long));
255 		(void)kvm_read((void *)nl[X_DK_XFER].n_value,
256 		    cur.dk_xfer, dk_ndrive * sizeof(long));
257 		(void)kvm_read((void *)nl[X_DK_WDS].n_value,
258 		    cur.dk_wds, dk_ndrive * sizeof(long));
259 		(void)kvm_read((void *)nl[X_DK_SEEK].n_value,
260 		    cur.dk_seek, dk_ndrive * sizeof(long));
261 		(void)kvm_read((void *)nl[X_TK_NIN].n_value,
262 		    &cur.tk_nin, sizeof(cur.tk_nin));
263 		(void)kvm_read((void *)nl[X_TK_NOUT].n_value,
264 		    &cur.tk_nout, sizeof(cur.tk_nout));
265 		(void)kvm_read((void *)nl[X_CP_TIME].n_value,
266 		    cur.cp_time, sizeof(cur.cp_time));
267 		for (i = 0; i < dk_ndrive; i++) {
268 			if (!dr_select[i])
269 				continue;
270 #define X(fld)	tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
271 			X(dk_xfer);
272 			X(dk_seek);
273 			X(dk_wds);
274 			X(dk_time);
275 		}
276 		tmp = cur.tk_nin;
277 		cur.tk_nin -= last.tk_nin;
278 		last.tk_nin = tmp;
279 		tmp = cur.tk_nout;
280 		cur.tk_nout -= last.tk_nout;
281 		last.tk_nout = tmp;
282 		etime = 0;
283 		for (i = 0; i < CPUSTATES; i++) {
284 			X(cp_time);
285 			etime += cur.cp_time[i];
286 		}
287 		if (etime == 0.0)
288 			etime = 1.0;
289 		etime /= (float)hz;
290 		(void)printf("%4.0f%5.0f",
291 		    cur.tk_nin / etime, cur.tk_nout / etime);
292 		dkstats();
293 		cpustats();
294 		(void)printf("\n");
295 		(void)fflush(stdout);
296 
297 		if (reps >= 0 && --reps <= 0)
298 			break;
299 		(void)sleep(interval);
300 	}
301 	exit(0);
302 }
303 
304 /* ARGUSED */
305 void
306 phdr(notused)
307 	int notused;
308 {
309 	register int i;
310 
311 	(void)printf("      tty");
312 	for (i = 0; i < dk_ndrive; i++)
313 		if (dr_select[i])
314 			(void)printf("          %3.3s ", dr_name[i]);
315 	(void)printf("         cpu\n tin tout");
316 	for (i = 0; i < dk_ndrive; i++)
317 		if (dr_select[i])
318 			(void)printf(" sps tps msps ");
319 	(void)printf(" us ni sy id\n");
320 }
321 
322 void
323 dkstats()
324 {
325 	register int dn;
326 	double atime, itime, msps, words, xtime;
327 
328 	for (dn = 0; dn < dk_ndrive; ++dn) {
329 		if (!dr_select[dn])
330 			continue;
331 		words = cur.dk_wds[dn] * 32;		/* words xfer'd */
332 		(void)printf("%4.0f",			/* sectors */
333 		    words / (DEV_BSIZE / 2) / etime);
334 
335 		(void)printf("%4.0f", cur.dk_xfer[dn] / etime);
336 
337 		if (dk_wpms[dn] && cur.dk_xfer[dn]) {
338 			atime = cur.dk_time[dn];	/* ticks disk busy */
339 			atime /= (float)hz;		/* ticks to seconds */
340 			xtime = words / dk_wpms[dn];	/* transfer time */
341 			itime = atime - xtime;		/* time not xfer'ing */
342 			if (itime < 0)
343 				msps = 0;
344 			else
345 				msps = itime * 1000 / cur.dk_xfer[dn];
346 		} else
347 			msps = 0;
348 		(void)printf("%5.1f ", msps);
349 	}
350 }
351 
352 void
353 cpustats()
354 {
355 	register int state;
356 	double time;
357 
358 	time = 0;
359 	for (state = 0; state < CPUSTATES; ++state)
360 		time += cur.cp_time[state];
361 	for (state = 0; state < CPUSTATES; ++state)
362 		(void)printf("%3.0f",
363 		    100. * cur.cp_time[state] / (time ? time : 1));
364 }
365 
366 void
367 usage()
368 {
369 	(void)fprintf(stderr,
370 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n");
371 	exit(1);
372 }
373 
374 #if __STDC__
375 #include <stdarg.h>
376 #else
377 #include <varargs.h>
378 #endif
379 
380 void
381 #if __STDC__
382 err(const char *fmt, ...)
383 #else
384 err(fmt, va_alist)
385 	char *fmt;
386         va_dcl
387 #endif
388 {
389 	va_list ap;
390 #if __STDC__
391 	va_start(ap, fmt);
392 #else
393 	va_start(ap);
394 #endif
395 	(void)fprintf(stderr, "iostat: ");
396 	(void)vfprintf(stderr, fmt, ap);
397 	va_end(ap);
398 	(void)fprintf(stderr, "\n");
399 	exit(1);
400 	/* NOTREACHED */
401 }
402