xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision 5f7096188587a2c7c95fa3c69b78e1ec9c7923d0)
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[] = "from: @(#)iostat.c	5.9 (Berkeley) 6/27/91";*/
42 static char rcsid[] = "$Id: iostat.c,v 1.3 1993/08/01 17:59:28 mycroft Exp $";
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/buf.h>
47 #include <sys/dkstat.h>
48 #include <signal.h>
49 #include <fcntl.h>
50 #include <nlist.h>
51 #include <unistd.h>
52 #include <stdio.h>
53 #include <ctype.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <paths.h>
57 #include <kvm.h>
58 
59 struct nlist nl[] = {
60 #define	X_DK_TIME	0
61 	{ "_dk_time" },
62 #define	X_DK_XFER	1
63 	{ "_dk_xfer" },
64 #define	X_DK_WDS	2
65 	{ "_dk_wds" },
66 #define	X_TK_NIN	3
67 	{ "_tk_nin" },
68 #define	X_TK_NOUT	4
69 	{ "_tk_nout" },
70 #define	X_DK_SEEK	5
71 	{ "_dk_seek" },
72 #define	X_CP_TIME	6
73 	{ "_cp_time" },
74 #define	X_DK_WPMS	7
75 	{ "_dk_wpms" },
76 #define	X_HZ		8
77 	{ "_hz" },
78 #define	X_PHZ		9
79 	{ "_phz" },
80 #define	X_DK_NDRIVE	10
81 	{ "_dk_ndrive" },
82 #define	X_END		10
83 #ifdef hp300
84 #define	X_HPDINIT	(X_END+1)
85 	{ "_hp_dinit" },
86 #endif
87 #ifdef tahoe
88 #define	X_VBDINIT	(X_END+1)
89 	{ "_vbdinit" },
90 #endif
91 #ifdef vax
92 	{ "_mbdinit" },
93 #define X_MBDINIT	(X_END+1)
94 	{ "_ubdinit" },
95 #define X_UBDINIT	(X_END+2)
96 #endif
97 #ifdef __386BSD__
98 #define	X_ISA_BIO	(X_END+1)
99 	{ "_isa_subdev" },
100 #endif /* __386BSD__ */
101 	{ NULL },
102 };
103 
104 struct _disk {
105 	long	cp_time[CPUSTATES];
106 	long	*dk_time;
107 	long	*dk_wds;
108 	long	*dk_seek;
109 	long	*dk_xfer;
110 	long	tk_nin;
111 	long	tk_nout;
112 } cur, last;
113 
114 double etime;
115 long *dk_wpms;
116 int dk_ndrive, *dr_select, hz, kmemfd, ndrives;
117 char **dr_name;
118 
119 #define nlread(x, v) \
120 	kvm_read((void *)nl[x].n_value, (void *)&(v), sizeof(v))
121 
122 #include "names.c"				/* XXX */
123 
124 static void cpustats __P((void)), dkstats __P((void)), phdr __P((int));
125 static void usage __P((void)), err __P((const char *, ...));
126 
127 main(argc, argv)
128 	int argc;
129 	char **argv;
130 {
131 	register int i;
132 	long tmp;
133 	int ch, hdrcnt, reps, interval, phz, ndrives;
134 	char **cp, *memfile, *namelist, buf[30];
135 
136 	interval = reps = 0;
137 	namelist = memfile = NULL;
138 	while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF)
139 		switch(ch) {
140 		case 'c':
141 			reps = atoi(optarg);
142 			break;
143 		case 'M':
144 			memfile = optarg;
145 			break;
146 		case 'N':
147 			namelist = optarg;
148 			break;
149 		case 'w':
150 			interval = atoi(optarg);
151 			break;
152 		case '?':
153 		default:
154 			usage();
155 		}
156 	argc -= optind;
157 	argv += optind;
158 
159 	if (kvm_openfiles(namelist, memfile, NULL) == -1)
160 		err("kvm_openfiles: %s", kvm_geterr());
161 	if (kvm_nlist(nl) == -1)
162 		err("kvm_nlist: %s", kvm_geterr());
163 	if (nl[X_DK_NDRIVE].n_type == 0)
164 		err("dk_ndrive not found in namelist");
165 	(void)nlread(X_DK_NDRIVE, dk_ndrive);
166 	if (dk_ndrive <= 0)
167 		err("invalid dk_ndrive %d\n", dk_ndrive);
168 
169 	cur.dk_time = calloc(dk_ndrive, sizeof(long));
170 	cur.dk_wds = calloc(dk_ndrive, sizeof(long));
171 	cur.dk_seek = calloc(dk_ndrive, sizeof(long));
172 	cur.dk_xfer = calloc(dk_ndrive, sizeof(long));
173 	last.dk_time = calloc(dk_ndrive, sizeof(long));
174 	last.dk_wds = calloc(dk_ndrive, sizeof(long));
175 	last.dk_seek = calloc(dk_ndrive, sizeof(long));
176 	last.dk_xfer = calloc(dk_ndrive, sizeof(long));
177 	dr_select = calloc(dk_ndrive, sizeof(int));
178 	dr_name = calloc(dk_ndrive, sizeof(char *));
179 	dk_wpms = calloc(dk_ndrive, sizeof(long));
180 
181 	for (i = 0; i < dk_ndrive; i++) {
182 		(void)sprintf(buf, "dk%d", i);
183 		dr_name[i] = strdup(buf);
184 	}
185 	read_names();
186 	(void)nlread(X_HZ, hz);
187 	(void)nlread(X_PHZ, phz);
188 	if (phz)
189 		hz = phz;
190 	(void)kvm_read((void *)nl[X_DK_WPMS].n_value, dk_wpms,
191 		dk_ndrive * sizeof(dk_wpms));
192 
193 	/*
194 	 * Choose drives to be displayed.  Priority goes to (in order) drives
195 	 * supplied as arguments and default drives.  If everything isn't
196 	 * filled in and there are drives not taken care of, display the first
197 	 * few that fit.
198 	 *
199 	 * The backward compatibility #ifdefs permit the syntax:
200 	 *	iostat [ drives ] [ interval [ count ] ]
201 	 */
202 #define	BACKWARD_COMPATIBILITY
203 	for (ndrives = 0; *argv; ++argv) {
204 #ifdef	BACKWARD_COMPATIBILITY
205 		if (isdigit(**argv))
206 			break;
207 #endif
208 		for (i = 0; i < dk_ndrive; i++) {
209 			if (strcmp(dr_name[i], *argv))
210 				continue;
211 			dr_select[i] = 1;
212 			++ndrives;
213 		}
214 	}
215 #ifdef	BACKWARD_COMPATIBILITY
216 	if (*argv) {
217 		interval = atoi(*argv);
218 		if (*++argv)
219 			reps = atoi(*argv);
220 	}
221 #endif
222 
223 	if (interval) {
224 		if (!reps)
225 			reps = -1;
226 	} else
227 		if (reps)
228 			interval = 1;
229 
230 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
231 		if (dr_select[i] || dk_wpms[i] == 0)
232 			continue;
233 		for (cp = defdrives; *cp; cp++)
234 			if (strcmp(dr_name[i], *cp) == 0) {
235 				dr_select[i] = 1;
236 				++ndrives;
237 				break;
238 			}
239 	}
240 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
241 		if (dr_select[i])
242 			continue;
243 		dr_select[i] = 1;
244 		++ndrives;
245 	}
246 
247 	(void)signal(SIGCONT, phdr);
248 
249 	for (hdrcnt = 1;;) {
250 		if (!--hdrcnt) {
251 			phdr(0);
252 			hdrcnt = 20;
253 		}
254 		(void)kvm_read((void *)nl[X_DK_TIME].n_value,
255 		    cur.dk_time, dk_ndrive * sizeof(long));
256 		(void)kvm_read((void *)nl[X_DK_XFER].n_value,
257 		    cur.dk_xfer, dk_ndrive * sizeof(long));
258 		(void)kvm_read((void *)nl[X_DK_WDS].n_value,
259 		    cur.dk_wds, dk_ndrive * sizeof(long));
260 		(void)kvm_read((void *)nl[X_DK_SEEK].n_value,
261 		    cur.dk_seek, dk_ndrive * sizeof(long));
262 		(void)kvm_read((void *)nl[X_TK_NIN].n_value,
263 		    &cur.tk_nin, sizeof(cur.tk_nin));
264 		(void)kvm_read((void *)nl[X_TK_NOUT].n_value,
265 		    &cur.tk_nout, sizeof(cur.tk_nout));
266 		(void)kvm_read((void *)nl[X_CP_TIME].n_value,
267 		    cur.cp_time, sizeof(cur.cp_time));
268 		for (i = 0; i < dk_ndrive; i++) {
269 			if (!dr_select[i])
270 				continue;
271 #define X(fld)	tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
272 			X(dk_xfer);
273 			X(dk_seek);
274 			X(dk_wds);
275 			X(dk_time);
276 		}
277 		tmp = cur.tk_nin;
278 		cur.tk_nin -= last.tk_nin;
279 		last.tk_nin = tmp;
280 		tmp = cur.tk_nout;
281 		cur.tk_nout -= last.tk_nout;
282 		last.tk_nout = tmp;
283 		etime = 0;
284 		for (i = 0; i < CPUSTATES; i++) {
285 			X(cp_time);
286 			etime += cur.cp_time[i];
287 		}
288 		if (etime == 0.0)
289 			etime = 1.0;
290 		etime /= (float)hz;
291 		(void)printf("%4.0f%5.0f",
292 		    cur.tk_nin / etime, cur.tk_nout / etime);
293 		dkstats();
294 		cpustats();
295 		(void)printf("\n");
296 		(void)fflush(stdout);
297 
298 		if (reps >= 0 && --reps <= 0)
299 			break;
300 		(void)sleep(interval);
301 	}
302 	exit(0);
303 }
304 
305 /* ARGUSED */
306 void
307 phdr(notused)
308 	int notused;
309 {
310 	register int i;
311 
312 	(void)printf("      tty");
313 	for (i = 0; i < dk_ndrive; i++)
314 		if (dr_select[i])
315 			(void)printf("        %5.5s ", dr_name[i]);
316 	(void)printf("         cpu\n tin tout");
317 	for (i = 0; i < dk_ndrive; i++)
318 		if (dr_select[i])
319 			(void)printf(" sps tps msps ");
320 	(void)printf(" us ni sy id\n");
321 }
322 
323 void
324 dkstats()
325 {
326 	register int dn;
327 	double atime, itime, msps, words, xtime;
328 
329 	for (dn = 0; dn < dk_ndrive; ++dn) {
330 		if (!dr_select[dn])
331 			continue;
332 		words = cur.dk_wds[dn] * 32;		/* words xfer'd */
333 		(void)printf("%4.0f",			/* sectors */
334 		    words / (DEV_BSIZE / 2) / etime);
335 
336 		(void)printf("%4.0f", cur.dk_xfer[dn] / etime);
337 
338 		if (dk_wpms[dn] && cur.dk_xfer[dn]) {
339 			atime = cur.dk_time[dn];	/* ticks disk busy */
340 			atime /= (float)hz;		/* ticks to seconds */
341 			xtime = words / dk_wpms[dn];	/* transfer time */
342 			itime = atime - xtime;		/* time not xfer'ing */
343 			if (itime < 0)
344 				msps = 0;
345 			else
346 				msps = itime * 1000 / cur.dk_xfer[dn];
347 		} else
348 			msps = 0;
349 		(void)printf("%5.1f ", msps);
350 	}
351 }
352 
353 void
354 cpustats()
355 {
356 	register int state;
357 	double time;
358 
359 	time = 0;
360 	for (state = 0; state < CPUSTATES; ++state)
361 		time += cur.cp_time[state];
362 	for (state = 0; state < CPUSTATES; ++state)
363 		(void)printf("%3.0f",
364 		    100. * cur.cp_time[state] / (time ? time : 1));
365 }
366 
367 void
368 usage()
369 {
370 	(void)fprintf(stderr,
371 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n");
372 	exit(1);
373 }
374 
375 #if __STDC__
376 #include <stdarg.h>
377 #else
378 #include <varargs.h>
379 #endif
380 
381 void
382 #if __STDC__
383 err(const char *fmt, ...)
384 #else
385 err(fmt, va_alist)
386 	char *fmt;
387         va_dcl
388 #endif
389 {
390 	va_list ap;
391 #if __STDC__
392 	va_start(ap, fmt);
393 #else
394 	va_start(ap);
395 #endif
396 	(void)fprintf(stderr, "iostat: ");
397 	(void)vfprintf(stderr, fmt, ap);
398 	va_end(ap);
399 	(void)fprintf(stderr, "\n");
400 	exit(1);
401 	/* NOTREACHED */
402 }
403