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