xref: /original-bsd/usr.sbin/iostat/iostat.c (revision e59fb703)
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.11 (Berkeley) 11/13/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		10
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 void cpustats __P((void));
94 void dkstats __P((void));
95 void err __P((const char *, ...));
96 void phdr __P((int));
97 void usage __P((void));
98 
99 main(argc, argv)
100 	int argc;
101 	char **argv;
102 {
103 	register int i;
104 	long tmp;
105 	int ch, hdrcnt, reps, interval, phz, ndrives;
106 	char **cp, *memfile, *namelist, buf[30];
107 
108 	interval = reps = 0;
109 	namelist = memfile = NULL;
110 	while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF)
111 		switch(ch) {
112 		case 'c':
113 			reps = atoi(optarg);
114 			break;
115 		case 'M':
116 			memfile = optarg;
117 			break;
118 		case 'N':
119 			namelist = optarg;
120 			break;
121 		case 'w':
122 			interval = atoi(optarg);
123 			break;
124 		case '?':
125 		default:
126 			usage();
127 		}
128 	argc -= optind;
129 	argv += optind;
130 
131 	if (kvm_openfiles(namelist, memfile, NULL) == -1)
132 		err("kvm_openfiles: %s", kvm_geterr());
133 	if (kvm_nlist(nl) == -1)
134 		err("kvm_nlist: %s", kvm_geterr());
135 	if (nl[X_DK_NDRIVE].n_type == 0)
136 		err("dk_ndrive not found in namelist");
137 	(void)nlread(X_DK_NDRIVE, dk_ndrive);
138 	if (dk_ndrive <= 0)
139 		err("invalid dk_ndrive %d\n", dk_ndrive);
140 
141 	cur.dk_time = calloc(dk_ndrive, sizeof(long));
142 	cur.dk_wds = calloc(dk_ndrive, sizeof(long));
143 	cur.dk_seek = calloc(dk_ndrive, sizeof(long));
144 	cur.dk_xfer = calloc(dk_ndrive, sizeof(long));
145 	last.dk_time = calloc(dk_ndrive, sizeof(long));
146 	last.dk_wds = calloc(dk_ndrive, sizeof(long));
147 	last.dk_seek = calloc(dk_ndrive, sizeof(long));
148 	last.dk_xfer = calloc(dk_ndrive, sizeof(long));
149 	dr_select = calloc(dk_ndrive, sizeof(int));
150 	dr_name = calloc(dk_ndrive, sizeof(char *));
151 	dk_wpms = calloc(dk_ndrive, sizeof(long));
152 
153 	for (i = 0; i < dk_ndrive; i++) {
154 		(void)sprintf(buf, "dk%d", i);
155 		dr_name[i] = strdup(buf);
156 	}
157 	read_names();
158 	(void)nlread(X_HZ, hz);
159 	(void)nlread(X_PHZ, phz);
160 	if (phz)
161 		hz = phz;
162 	(void)kvm_read((void *)nl[X_DK_WPMS].n_value, dk_wpms,
163 		dk_ndrive * sizeof(dk_wpms));
164 
165 	/*
166 	 * Choose drives to be displayed.  Priority goes to (in order) drives
167 	 * supplied as arguments and default drives.  If everything isn't
168 	 * filled in and there are drives not taken care of, display the first
169 	 * few that fit.
170 	 *
171 	 * The backward compatibility #ifdefs permit the syntax:
172 	 *	iostat [ drives ] [ interval [ count ] ]
173 	 */
174 #define	BACKWARD_COMPATIBILITY
175 	for (ndrives = 0; *argv; ++argv) {
176 #ifdef	BACKWARD_COMPATIBILITY
177 		if (isdigit(**argv))
178 			break;
179 #endif
180 		for (i = 0; i < dk_ndrive; i++) {
181 			if (strcmp(dr_name[i], *argv))
182 				continue;
183 			dr_select[i] = 1;
184 			++ndrives;
185 		}
186 	}
187 #ifdef	BACKWARD_COMPATIBILITY
188 	if (*argv) {
189 		interval = atoi(*argv);
190 		if (*++argv)
191 			reps = atoi(*argv);
192 	}
193 #endif
194 
195 	if (interval) {
196 		if (!reps)
197 			reps = -1;
198 	} else
199 		if (reps)
200 			interval = 1;
201 
202 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
203 		if (dr_select[i] || dk_wpms[i] == 0)
204 			continue;
205 		for (cp = defdrives; *cp; cp++)
206 			if (strcmp(dr_name[i], *cp) == 0) {
207 				dr_select[i] = 1;
208 				++ndrives;
209 				break;
210 			}
211 	}
212 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
213 		if (dr_select[i])
214 			continue;
215 		dr_select[i] = 1;
216 		++ndrives;
217 	}
218 
219 	(void)signal(SIGCONT, phdr);
220 
221 	for (hdrcnt = 1;;) {
222 		if (!--hdrcnt) {
223 			phdr(0);
224 			hdrcnt = 20;
225 		}
226 		(void)kvm_read((void *)nl[X_DK_TIME].n_value,
227 		    cur.dk_time, dk_ndrive * sizeof(long));
228 		(void)kvm_read((void *)nl[X_DK_XFER].n_value,
229 		    cur.dk_xfer, dk_ndrive * sizeof(long));
230 		(void)kvm_read((void *)nl[X_DK_WDS].n_value,
231 		    cur.dk_wds, dk_ndrive * sizeof(long));
232 		(void)kvm_read((void *)nl[X_DK_SEEK].n_value,
233 		    cur.dk_seek, dk_ndrive * sizeof(long));
234 		(void)kvm_read((void *)nl[X_TK_NIN].n_value,
235 		    &cur.tk_nin, sizeof(cur.tk_nin));
236 		(void)kvm_read((void *)nl[X_TK_NOUT].n_value,
237 		    &cur.tk_nout, sizeof(cur.tk_nout));
238 		(void)kvm_read((void *)nl[X_CP_TIME].n_value,
239 		    cur.cp_time, sizeof(cur.cp_time));
240 		for (i = 0; i < dk_ndrive; i++) {
241 			if (!dr_select[i])
242 				continue;
243 #define X(fld)	tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
244 			X(dk_xfer);
245 			X(dk_seek);
246 			X(dk_wds);
247 			X(dk_time);
248 		}
249 		tmp = cur.tk_nin;
250 		cur.tk_nin -= last.tk_nin;
251 		last.tk_nin = tmp;
252 		tmp = cur.tk_nout;
253 		cur.tk_nout -= last.tk_nout;
254 		last.tk_nout = tmp;
255 		etime = 0;
256 		for (i = 0; i < CPUSTATES; i++) {
257 			X(cp_time);
258 			etime += cur.cp_time[i];
259 		}
260 		if (etime == 0.0)
261 			etime = 1.0;
262 		etime /= (float)hz;
263 		(void)printf("%4.0f%5.0f",
264 		    cur.tk_nin / etime, cur.tk_nout / etime);
265 		dkstats();
266 		cpustats();
267 		(void)printf("\n");
268 		(void)fflush(stdout);
269 
270 		if (reps >= 0 && --reps <= 0)
271 			break;
272 		(void)sleep(interval);
273 	}
274 	exit(0);
275 }
276 
277 /* ARGUSED */
278 void
279 phdr(notused)
280 	int notused;
281 {
282 	register int i;
283 
284 	(void)printf("      tty");
285 	for (i = 0; i < dk_ndrive; i++)
286 		if (dr_select[i])
287 			(void)printf("          %3.3s ", dr_name[i]);
288 	(void)printf("         cpu\n tin tout");
289 	for (i = 0; i < dk_ndrive; i++)
290 		if (dr_select[i])
291 			(void)printf(" sps tps msps ");
292 	(void)printf(" us ni sy id\n");
293 }
294 
295 void
296 dkstats()
297 {
298 	register int dn;
299 	double atime, itime, msps, words, xtime;
300 
301 	for (dn = 0; dn < dk_ndrive; ++dn) {
302 		if (!dr_select[dn])
303 			continue;
304 		words = cur.dk_wds[dn] * 32;		/* words xfer'd */
305 		(void)printf("%4.0f",			/* sectors */
306 		    words / (DEV_BSIZE / 2) / etime);
307 
308 		(void)printf("%4.0f", cur.dk_xfer[dn] / etime);
309 
310 		if (dk_wpms[dn] && cur.dk_xfer[dn]) {
311 			atime = cur.dk_time[dn];	/* ticks disk busy */
312 			atime /= (float)hz;		/* ticks to seconds */
313 			xtime = words / dk_wpms[dn];	/* transfer time */
314 			itime = atime - xtime;		/* time not xfer'ing */
315 			if (itime < 0)
316 				msps = 0;
317 			else
318 				msps = itime * 1000 / cur.dk_xfer[dn];
319 		} else
320 			msps = 0;
321 		(void)printf("%5.1f ", msps);
322 	}
323 }
324 
325 void
326 cpustats()
327 {
328 	register int state;
329 	double time;
330 
331 	time = 0;
332 	for (state = 0; state < CPUSTATES; ++state)
333 		time += cur.cp_time[state];
334 	for (state = 0; state < CPUSTATES; ++state)
335 		(void)printf("%3.0f",
336 		    100. * cur.cp_time[state] / (time ? time : 1));
337 }
338 
339 void
340 usage()
341 {
342 	(void)fprintf(stderr,
343 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n");
344 	exit(1);
345 }
346 
347 #if __STDC__
348 #include <stdarg.h>
349 #else
350 #include <varargs.h>
351 #endif
352 
353 void
354 #if __STDC__
355 err(const char *fmt, ...)
356 #else
357 err(fmt, va_alist)
358 	char *fmt;
359         va_dcl
360 #endif
361 {
362 	va_list ap;
363 #if __STDC__
364 	va_start(ap, fmt);
365 #else
366 	va_start(ap);
367 #endif
368 	(void)fprintf(stderr, "iostat: ");
369 	(void)vfprintf(stderr, fmt, ap);
370 	va_end(ap);
371 	(void)fprintf(stderr, "\n");
372 	exit(1);
373 	/* NOTREACHED */
374 }
375