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