xref: /original-bsd/usr.bin/vmstat/vmstat.c (revision 53787e02)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)vmstat.c	5.1 (Berkeley) 05/31/85";
15 #endif not lint
16 
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <nlist.h>
20 
21 #include <sys/param.h>
22 #include <sys/file.h>
23 #include <sys/vm.h>
24 #include <sys/dk.h>
25 #include <sys/buf.h>
26 #include <sys/dir.h>
27 #include <sys/inode.h>
28 #include <sys/namei.h>
29 
30 struct nlist nl[] = {
31 #define	X_CPTIME	0
32 	{ "_cp_time" },
33 #define	X_RATE		1
34 	{ "_rate" },
35 #define X_TOTAL		2
36 	{ "_total" },
37 #define	X_DEFICIT	3
38 	{ "_deficit" },
39 #define	X_FORKSTAT	4
40 	{ "_forkstat" },
41 #define X_SUM		5
42 	{ "_sum" },
43 #define	X_FIRSTFREE	6
44 	{ "_firstfree" },
45 #define	X_MAXFREE	7
46 	{ "_maxfree" },
47 #define	X_BOOTTIME	8
48 	{ "_boottime" },
49 #define	X_DKXFER	9
50 	{ "_dk_xfer" },
51 #define X_REC		10
52 	{ "_rectime" },
53 #define X_PGIN		11
54 	{ "_pgintime" },
55 #define X_HZ		12
56 	{ "_hz" },
57 #define X_PHZ		13
58 	{ "_phz" },
59 #define X_NCHSTATS	14
60 	{ "_nchstats" },
61 #define	X_INTRNAMES	15
62 	{ "_intrnames" },
63 #define	X_EINTRNAMES	16
64 	{ "_eintrnames" },
65 #define	X_INTRCNT	17
66 	{ "_intrcnt" },
67 #define	X_EINTRCNT	18
68 	{ "_eintrcnt" },
69 #define	X_DK_NDRIVE	19
70 	{ "_dk_ndrive" },
71 #ifdef vax
72 #define X_MBDINIT	20
73 	{ "_mbdinit" },
74 #define X_UBDINIT	21
75 	{ "_ubdinit" },
76 #endif
77 	{ "" },
78 };
79 
80 char	**dr_name;
81 int	*dr_select;
82 int	dk_ndrive;
83 int	ndrives = 0;
84 #ifdef vax
85 char	*defdrives[] = { "hp0", "hp1", "hp2",  0 };
86 #else
87 char	*defdrives[] = { 0 };
88 #endif
89 double	stat1();
90 int	firstfree, maxfree;
91 int	hz;
92 int	phz;
93 int	HZ;
94 
95 #ifdef vax
96 #define	INTS(x)	((x) - (hz + phz))
97 #endif
98 
99 struct {
100 	int	busy;
101 	long	time[CPUSTATES];
102 	long	*xfer;
103 	struct	vmmeter Rate;
104 	struct	vmtotal	Total;
105 	struct	vmmeter Sum;
106 	struct	forkstat Forkstat;
107 	unsigned rectime;
108 	unsigned pgintime;
109 } s, s1, z;
110 #define	rate		s.Rate
111 #define	total		s.Total
112 #define	sum		s.Sum
113 #define	forkstat	s.Forkstat
114 
115 struct	vmmeter osum;
116 int	zero;
117 int	deficit;
118 double	etime;
119 int 	mf;
120 time_t	now, boottime;
121 int	printhdr();
122 int	lines = 1;
123 
124 main(argc, argv)
125 	int argc;
126 	char **argv;
127 {
128 	extern char *ctime();
129 	register i,j;
130 	int iter, nintv, iflag = 0;
131 	double f1, f2;
132 	long t;
133 	char *arg, **cp, name[6], buf[BUFSIZ];
134 
135 	nlist("/vmunix", nl);
136 	if(nl[0].n_type == 0) {
137 		printf("no /vmunix namelist\n");
138 		exit(1);
139 	}
140 	mf = open("/dev/kmem", 0);
141 	if(mf < 0) {
142 		printf("cannot open /dev/kmem\n");
143 		exit(1);
144 	}
145 	iter = 0;
146 	argc--, argv++;
147 	while (argc>0 && argv[0][0]=='-') {
148 		char *cp = *argv++;
149 		argc--;
150 		while (*++cp) switch (*cp) {
151 
152 		case 't':
153 			dotimes();
154 			exit(0);
155 
156 		case 'z':
157 			close(mf);
158 			mf = open("/dev/kmem", 2);
159 			lseek(mf, (long)nl[X_SUM].n_value, L_SET);
160 			write(mf, &z.Sum, sizeof z.Sum);
161 			exit(0);
162 
163 		case 'f':
164 			doforkst();
165 			exit(0);
166 
167 		case 's':
168 			dosum();
169 			exit(0);
170 
171 		case 'i':
172 			iflag++;
173 			break;
174 
175 		default:
176 			fprintf(stderr,
177 			    "usage: vmstat [ -fsi ] [ interval ] [ count]\n");
178 			exit(1);
179 		}
180 	}
181 	lseek(mf, (long)nl[X_FIRSTFREE].n_value, L_SET);
182 	read(mf, &firstfree, sizeof firstfree);
183 	lseek(mf, (long)nl[X_MAXFREE].n_value, L_SET);
184 	read(mf, &maxfree, sizeof maxfree);
185 	lseek(mf, (long)nl[X_BOOTTIME].n_value, L_SET);
186 	read(mf, &boottime, sizeof boottime);
187 	lseek(mf, (long)nl[X_HZ].n_value, L_SET);
188 	read(mf, &hz, sizeof hz);
189 	if (nl[X_PHZ].n_value != 0) {
190 		lseek(mf, (long)nl[X_PHZ].n_value, L_SET);
191 		read(mf, &phz, sizeof phz);
192 	}
193 	HZ = phz ? phz : hz;
194 	if (nl[DK_NDRIVE].n_value == 0) {
195 		printf("dk_ndrive undefined in system\n");
196 		exit(1);
197 	}
198 	lseek(mf, nl[X_DK_NDRIVE].n_value, L_SET);
199 	read(mf, &dk_ndrive, sizeof (dk_ndrive));
200 	if (dk_ndrive <= 0) {
201 		printf("dk_ndrive %d\n", dk_ndrive);
202 		exit(1);
203 	}
204 	dr_select = (int *)calloc(dk_ndrive, sizeof (int));
205 	dr_name = (char **)calloc(dk_ndrive, sizeof (char *));
206 #define	allocate(e, t) \
207     s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
208     s1./**/e = (t *)calloc(dk_ndrive, sizeof (t));
209 	allocate(xfer, long);
210 	for (arg = buf, i = 0; i < dk_ndrive; i++) {
211 		dr_name[i] = arg;
212 		sprintf(dr_name[i], "dk%d", i);
213 		arg += strlen(dr_name[i]) + 1;
214 	}
215 	read_names();
216 	time(&now);
217 	nintv = now - boottime;
218 	if (nintv <= 0 || nintv > 60*60*24*365*10) {
219 		printf("Time makes no sense... namelist must be wrong.\n");
220 		exit(1);
221 	}
222 	if (iflag) {
223 		dointr(nintv);
224 		exit(0);
225 	}
226 	/*
227 	 * Choose drives to be displayed.  Priority
228 	 * goes to (in order) drives supplied as arguments,
229 	 * default drives.  If everything isn't filled
230 	 * in and there are drives not taken care of,
231 	 * display the first few that fit.
232 	 */
233 	ndrives = 0;
234 	while (argc > 0 && !isdigit(argv[0][0])) {
235 		for (i = 0; i < dk_ndrive; i++) {
236 			if (strcmp(dr_name[i], argv[0]))
237 				continue;
238 			dr_select[i] = 1;
239 			ndrives++;
240 		}
241 		argc--, argv++;
242 	}
243 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
244 		if (dr_select[i])
245 			continue;
246 		for (cp = defdrives; *cp; cp++)
247 			if (strcmp(dr_name[i], *cp) == 0) {
248 				dr_select[i] = 1;
249 				ndrives++;
250 				break;
251 			}
252 	}
253 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
254 		if (dr_select[i])
255 			continue;
256 		dr_select[i] = 1;
257 		ndrives++;
258 	}
259 	if (argc > 1)
260 		iter = atoi(argv[1]);
261 	signal(SIGCONT, printhdr);
262 loop:
263 	if (--lines == 0)
264 		printhdr();
265 	lseek(mf, (long)nl[X_CPTIME].n_value, L_SET);
266  	read(mf, s.time, sizeof s.time);
267 	lseek(mf, (long)nl[X_DKXFER].n_value, L_SET);
268 	read(mf, s.xfer, dk_ndrive * sizeof (long));
269 	if (nintv != 1)
270 		lseek(mf, (long)nl[X_SUM].n_value, L_SET);
271 	else
272 		lseek(mf, (long)nl[X_RATE].n_value, L_SET);
273 	read(mf, &rate, sizeof rate);
274 	lseek(mf, (long)nl[X_TOTAL].n_value, L_SET);
275 	read(mf, &total, sizeof total);
276 	osum = sum;
277 	lseek(mf, (long)nl[X_SUM].n_value, L_SET);
278 	read(mf, &sum, sizeof sum);
279 	lseek(mf, (long)nl[X_DEFICIT].n_value, L_SET);
280 	read(mf, &deficit, sizeof deficit);
281 	etime = 0;
282 	for (i=0; i < dk_ndrive; i++) {
283 		t = s.xfer[i];
284 		s.xfer[i] -= s1.xfer[i];
285 		s1.xfer[i] = t;
286 	}
287 	for (i=0; i < CPUSTATES; i++) {
288 		t = s.time[i];
289 		s.time[i] -= s1.time[i];
290 		s1.time[i] = t;
291 		etime += s.time[i];
292 	}
293 	if(etime == 0.)
294 		etime = 1.;
295 	printf("%2d%2d%2d", total.t_rq, total.t_dw+total.t_pw, total.t_sw);
296 #define pgtok(a) ((a)*NBPG/1024)
297 	printf("%6d%5d", pgtok(total.t_avm), pgtok(total.t_free));
298 	printf("%4d%3d", (rate.v_pgrec - (rate.v_xsfrec+rate.v_xifrec))/nintv,
299 	    (rate.v_xsfrec+rate.v_xifrec)/nintv);
300 	printf("%4d", pgtok(rate.v_pgpgin)/nintv);
301 	printf("%4d%4d%4d%4d", pgtok(rate.v_pgpgout)/nintv,
302 	    pgtok(rate.v_dfree)/nintv, pgtok(deficit), rate.v_scan/nintv);
303 	etime /= (float)HZ;
304 	for (i = 0; i < dk_ndrive; i++)
305 		if (dr_select[i])
306 			stats(i);
307 	printf("%4d%4d%4d", INTS(rate.v_intr/nintv), rate.v_syscall/nintv,
308 	    rate.v_swtch/nintv);
309 	for(i=0; i<CPUSTATES; i++) {
310 		float f = stat1(i);
311 		if (i == 0) {		/* US+NI */
312 			i++;
313 			f += stat1(i);
314 		}
315 		printf("%3.0f", f);
316 	}
317 	printf("\n");
318 	fflush(stdout);
319 contin:
320 	nintv = 1;
321 	if (--iter &&argc > 0) {
322 		sleep(atoi(argv[0]));
323 		goto loop;
324 	}
325 }
326 
327 printhdr()
328 {
329 	register int i, j;
330 
331 	printf(" procs    memory              page           ");
332 	i = (ndrives * 3 - 6) / 2;
333 	if (i < 0)
334 		i = 0;
335 	for (j = 0; j < i; j++)
336 		putchar(' ');
337 	printf("faults");
338 	i = ndrives * 3 - 6 - i;
339 	for (j = 0; j < i; j++)
340 		putchar(' ');
341 	printf("               cpu\n");
342 	printf(" r b w   avm  fre  re at  pi  po  fr  de  sr ");
343 	for (i = 0; i < dk_ndrive; i++)
344 		if (dr_select[i])
345 			printf("%c%c ", dr_name[i][0], dr_name[i][2]);
346 	printf(" in  sy  cs us sy id\n");
347 	lines = 19;
348 }
349 
350 dotimes()
351 {
352 
353 	lseek(mf, (long)nl[X_REC].n_value, L_SET);
354 	read(mf, &s.rectime, sizeof s.rectime);
355 	lseek(mf, (long)nl[X_PGIN].n_value, L_SET);
356 	read(mf, &s.pgintime, sizeof s.pgintime);
357 	lseek(mf, (long)nl[X_SUM].n_value, L_SET);
358 	read(mf, &sum, sizeof sum);
359 	printf("%d reclaims, %d total time (usec)\n", sum.v_pgrec, s.rectime);
360 	printf("average: %d usec / reclaim\n", s.rectime/sum.v_pgrec);
361 	printf("\n");
362 	printf("%d page ins, %d total time (msec)\n",sum.v_pgin, s.pgintime/10);
363 	printf("average: %8.1f msec / page in\n", s.pgintime/(sum.v_pgin*10.0));
364 }
365 
366 dosum()
367 {
368 	struct nchstats nchstats;
369 	long nchtotal;
370 
371 	lseek(mf, (long)nl[X_SUM].n_value, L_SET);
372 	read(mf, &sum, sizeof sum);
373 	printf("%9d swap ins\n", sum.v_swpin);
374 	printf("%9d swap outs\n", sum.v_swpout);
375 	printf("%9d pages swapped in\n", sum.v_pswpin / CLSIZE);
376 	printf("%9d pages swapped out\n", sum.v_pswpout / CLSIZE);
377 	printf("%9d total address trans. faults taken\n", sum.v_faults);
378 	printf("%9d page ins\n", sum.v_pgin);
379 	printf("%9d page outs\n", sum.v_pgout);
380 	printf("%9d pages paged in\n", sum.v_pgpgin);
381 	printf("%9d pages paged out\n", sum.v_pgpgout);
382 	printf("%9d sequential process pages freed\n", sum.v_seqfree);
383 	printf("%9d total reclaims (%d%% fast)\n", sum.v_pgrec,
384 	    (sum.v_fastpgrec * 100) / (sum.v_pgrec == 0 ? 1 : sum.v_pgrec));
385 	printf("%9d reclaims from free list\n", sum.v_pgfrec);
386 	printf("%9d intransit blocking page faults\n", sum.v_intrans);
387 	printf("%9d zero fill pages created\n", sum.v_nzfod / CLSIZE);
388 	printf("%9d zero fill page faults\n", sum.v_zfod / CLSIZE);
389 	printf("%9d executable fill pages created\n", sum.v_nexfod / CLSIZE);
390 	printf("%9d executable fill page faults\n", sum.v_exfod / CLSIZE);
391 	printf("%9d swap text pages found in free list\n", sum.v_xsfrec);
392 	printf("%9d inode text pages found in free list\n", sum.v_xifrec);
393 	printf("%9d file fill pages created\n", sum.v_nvrfod / CLSIZE);
394 	printf("%9d file fill page faults\n", sum.v_vrfod / CLSIZE);
395 	printf("%9d pages examined by the clock daemon\n", sum.v_scan);
396 	printf("%9d revolutions of the clock hand\n", sum.v_rev);
397 	printf("%9d pages freed by the clock daemon\n", sum.v_dfree / CLSIZE);
398 	printf("%9d cpu context switches\n", sum.v_swtch);
399 	printf("%9d device interrupts\n", sum.v_intr);
400 	printf("%9d software interrupts\n", sum.v_soft);
401 #ifdef vax
402 	printf("%9d pseduo-dma dz interrupts\n", sum.v_pdma);
403 #endif
404 	printf("%9d traps\n", sum.v_trap);
405 	printf("%9d system calls\n", sum.v_syscall);
406 	lseek(mf, (long)nl[X_NCHSTATS].n_value, 0);
407 	read(mf, &nchstats, sizeof nchstats);
408 	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_badhits +
409 	    nchstats.ncs_falsehits + nchstats.ncs_miss + nchstats.ncs_long;
410 	printf("%9d total name lookups", nchtotal);
411 #define	nz(x)	((x) ? (x) : 1)
412 	printf(" (cache hits %d%% system %d%% per-process)\n",
413 	    nchstats.ncs_goodhits * 100 / nz(nchtotal),
414 	    nchstats.ncs_pass2 * 100 / nz(nchtotal));
415 	printf("%9s badhits %d, falsehits %d, toolong %d\n", "",
416 	    nchstats.ncs_badhits, nchstats.ncs_falsehits, nchstats.ncs_long);
417 }
418 
419 doforkst()
420 {
421 
422 	lseek(mf, (long)nl[X_FORKSTAT].n_value, L_SET);
423 	read(mf, &forkstat, sizeof forkstat);
424 	printf("%d forks, %d pages, average=%.2f\n",
425 		forkstat.cntfork, forkstat.sizfork,
426 		(float) forkstat.sizfork / forkstat.cntfork);
427 	printf("%d vforks, %d pages, average=%.2f\n",
428 		forkstat.cntvfork, forkstat.sizvfork,
429 		(float)forkstat.sizvfork / forkstat.cntvfork);
430 }
431 
432 stats(dn)
433 {
434 
435 	if (dn >= dk_ndrive) {
436 		printf("  0");
437 		return;
438 	}
439 	printf("%3.0f", s.xfer[dn]/etime);
440 }
441 
442 double
443 stat1(row)
444 {
445 	double t;
446 	register i;
447 
448 	t = 0;
449 	for(i=0; i<CPUSTATES; i++)
450 		t += s.time[i];
451 	if(t == 0.)
452 		t = 1.;
453 	return(s.time[row]*100./t);
454 }
455 
456 pct(top, bot)
457 {
458 
459 	if (bot == 0)
460 		return (0);
461 	return ((top * 100) / bot);
462 }
463 
464 dointr(nintv)
465 {
466 	int nintr, inttotal;
467 	long *intrcnt;
468 	char *intrname, *malloc();
469 
470 	nintr = (nl[X_EINTRCNT].n_value - nl[X_INTRCNT].n_value) / sizeof(long);
471 	intrcnt = (long *) malloc(nl[X_EINTRCNT].n_value -
472 		nl[X_INTRCNT].n_value);
473 	intrname = malloc(nl[X_EINTRNAMES].n_value - nl[X_INTRNAMES].n_value);
474 	if (intrcnt == NULL || intrname == NULL) {
475 		fprintf(stderr, "vmstat: out of memory\n");
476 		exit(9);
477 	}
478 	lseek(mf, (long)nl[X_INTRCNT].n_value, L_SET);
479 	read(mf, intrcnt, nintr * sizeof (long));
480 	lseek(mf, (long)nl[X_INTRNAMES].n_value, L_SET);
481 	read(mf, intrname, nl[X_EINTRNAMES].n_value - nl[X_INTRNAMES].n_value);
482 	printf("interrupt      total      rate\n");
483 	inttotal = 0;
484 	while (nintr--) {
485 		if (*intrcnt)
486 			printf("%-12s %8ld %8ld\n", intrname,
487 			    *intrcnt, *intrcnt / nintv);
488 		intrname += strlen(intrname) + 1;
489 		inttotal += *intrcnt++;
490 	}
491 	printf("Total        %8ld %8ld\n", inttotal, inttotal / nintv);
492 }
493 
494 #define steal(where, var) \
495 	lseek(mf, where, L_SET); read(mf, &var, sizeof var);
496 /*
497  * Read the drive names out of kmem.
498  */
499 #ifdef vax
500 #include <vaxuba/ubavar.h>
501 #include <vaxmba/mbavar.h>
502 
503 read_names()
504 {
505 	struct mba_device mdev;
506 	register struct mba_device *mp;
507 	struct mba_driver mdrv;
508 	short two_char;
509 	char *cp = (char *) &two_char;
510 	struct uba_device udev, *up;
511 	struct uba_driver udrv;
512 
513 	mp = (struct mba_device *) nl[X_MBDINIT].n_value;
514 	up = (struct uba_device *) nl[X_UBDINIT].n_value;
515 	if (up == 0) {
516 		fprintf(stderr, "vmstat: Disk init info not in namelist\n");
517 		exit(1);
518 	}
519 	if (mp) for (;;) {
520 		steal(mp++, mdev);
521 		if (mdev.mi_driver == 0)
522 			break;
523 		if (mdev.mi_dk < 0 || mdev.mi_alive == 0)
524 			continue;
525 		steal(mdev.mi_driver, mdrv);
526 		steal(mdrv.md_dname, two_char);
527 		sprintf(dr_name[mdev.mi_dk], "%c%c%d",
528 		    cp[0], cp[1], mdev.mi_unit);
529 	}
530 	for (;;) {
531 		steal(up++, udev);
532 		if (udev.ui_driver == 0)
533 			break;
534 		if (udev.ui_dk < 0 || udev.ui_alive == 0)
535 			continue;
536 		steal(udev.ui_driver, udrv);
537 		steal(udrv.ud_dname, two_char);
538 		sprintf(dr_name[udev.ui_dk], "%c%c%d",
539 		    cp[0], cp[1], udev.ui_unit);
540 	}
541 }
542 #endif
543