1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)vmstat.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/device.h>
14 #include <sys/disklabel.h>
15 #include <sys/disk.h>
16 #include <sys/time.h>
17 #include <sys/dkstat.h>
18 #include <sys/ioctl.h>
19 #include <sys/sysctl.h>
20 #include <vm/vm.h>
21 
22 #include <errno.h>
23 #include <kvm.h>
24 #include <limits.h>
25 #include <nlist.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 
33 #include "extern.h"
34 
35 int	hz, hdrcnt, winlines;
36 
37 struct {
38 	long	old[CPUSTATES];		/* previous cp_time from kernel */
39 	long	delta[CPUSTATES];	/* delta between current & prev */
40 	long	total;			/* sum of deltas */
41 } cputime;
42 
43 void	cpustats __P((void));
44 void	dkstats __P((void));
45 void	getcputime __P(());
46 int	getwinsize __P((void));
47 void	needhdr __P((int));
48 void	printhdr __P((void));
49 
50 static struct nlist nl[] = {
51 	{ "_cnt" },
52 #define	X_CNT		0
53 	{ "_cp_time" },
54 #define	X_CPTIME	1
55 	0
56 };
57 
58 void
59 dovmstat(interval, reps)
60 	u_int interval;
61 	int reps;
62 {
63 	int mib[2], size;
64 	time_t uptime, halfuptime;
65 	struct clockinfo ci;
66 	struct vmtotal total;
67 	struct vmmeter cnt, ocnt;
68 
69 	knlist(nl);
70 	winlines = getwinsize();
71 	uptime = getuptime();
72 	halfuptime = uptime / 2;
73 	(void)signal(SIGCONT, needhdr);
74 	size = sizeof(ci);
75 	mib[0] = CTL_KERN;
76 	mib[1] = KERN_CLOCKRATE;
77 	if (sysctl(mib, 2, &ci, &size, NULL, 0) < 0)
78 		errexit("sysctl(KERN_CLOCKRATE): %s\n", strerror(errno));
79 	hz = ci.stathz ? ci.stathz : ci.hz;
80 	for (hdrcnt = 1;;) {
81 		if (--hdrcnt == 0)
82 			printhdr();
83 		kread(nl[X_CNT].n_value, &cnt, sizeof cnt, "cnt");
84 		getcputime();
85 		size = sizeof(total);
86 		mib[0] = CTL_VM;
87 		mib[1] = VM_METER;
88 		if (sysctl(mib, 2, &total, &size, NULL, 0) < 0)
89 			errexit("sysctl(VM_METER): %s\n", strerror(errno));
90 		(void)printf("%2d%2d%2d",
91 		    total.t_rq, total.t_dw + total.t_pw, total.t_sw);
92 #define pgtok(a) ((a) * cnt.v_page_size >> 10)
93 #define	rate(x)	(((x) + halfuptime) / uptime)	/* round */
94 		(void)printf("%6ld%6ld ",
95 		    pgtok(total.t_avm), pgtok(total.t_free));
96 		(void)printf("%4lu ", rate(cnt.v_faults - ocnt.v_faults));
97 		(void)printf("%3lu ",
98 		    rate(cnt.v_reactivated - ocnt.v_reactivated));
99 		(void)printf("%3lu ", rate(cnt.v_pageins - ocnt.v_pageins));
100 		(void)printf("%3lu %3lu ",
101 		    rate(cnt.v_pageouts - ocnt.v_pageouts), 0);
102 		(void)printf("%3lu ", rate(cnt.v_scan - ocnt.v_scan));
103 		dkstats();
104 		(void)printf("%4lu %4lu %3lu ",
105 		    rate(cnt.v_intr - ocnt.v_intr),
106 		    rate(cnt.v_syscall - ocnt.v_syscall),
107 		    rate(cnt.v_swtch - ocnt.v_swtch));
108 		cpustats();
109 		(void)printf("\n");
110 		(void)fflush(stdout);
111 		if (reps >= 0 && --reps <= 0)
112 			break;
113 		ocnt = cnt;
114 		uptime = interval;
115 		/*
116 		 * We round upward to avoid losing low-frequency events
117 		 * (i.e., >= 1 per interval but < 1 per second).
118 		 */
119 		halfuptime = (uptime + 1) / 2;
120 		(void)sleep(interval);
121 	}
122 }
123 
124 int
125 getwinsize()
126 {
127 	struct winsize winsize;
128 
129 	winsize.ws_row = 0;
130 	(void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
131 	return (winsize.ws_row > 0 ? winsize.ws_row : 20);
132 }
133 
134 /*
135  * Get cpu times for dkstats and cpustats().
136  */
137 void
138 getcputime()
139 {
140 	register int state;
141 	long t, sum, cp_time[CPUSTATES];
142 
143 	kread(nl[X_CPTIME].n_value, cp_time, sizeof cp_time, "cp_time");
144 	for (sum = 0, state = 0; state < CPUSTATES; ++state) {
145 		t = cp_time[state] - cputime.old[state];
146 		cputime.old[state] = cp_time[state];
147 		cputime.delta[state] = t;
148 		sum += t;
149 	}
150 	cputime.total = sum;
151 }
152 
153 /*
154  * Print disk statistics for dovmstat().
155  */
156 void
157 dkstats()
158 {
159 	register struct dkinfo *dk;
160 	double etime;
161 #ifdef notyet
162 	long xfer;
163 
164 	for (dk = dkinfo; dk != NULL; dk = dk->dk_next) {
165 		kread(addr + offsetof(struct dkdevice, dk_xfer),
166 		    &xfer, sizeof xfer, dk->dk_name);
167 		dk->dk_dxfer = xfer - dk->dk_oxfer;
168 		dk->dk_oxfer = xfer;
169 	}
170 #endif
171 	etime = (cputime.total ? (double)cputime.total : 1.0) / hz;
172 	for (dk = dkinfo; dk != NULL; dk = dk->dk_next)
173 		if (dk->dk_sel)
174 			(void)printf("%2.0f ", dk->dk_dxfer / etime);
175 }
176 
177 /*
178  * Print cpu statistics for dovmstat().
179  */
180 void
181 cpustats()
182 {
183 	double pct;
184 
185 	if (cputime.total)
186 		pct = 100.0 / cputime.total;
187 	else
188 		pct = 0.0;
189 	(void)printf("%2.0f %2.0f %2.0f",
190 	    (cputime.delta[CP_USER] + cputime.delta[CP_NICE]) * pct,
191 	    (cputime.delta[CP_SYS] + cputime.delta[CP_INTR]) * pct,
192 	    cputime.delta[CP_IDLE] * pct);
193 }
194 
195 void
196 printhdr()
197 {
198 	register struct dkinfo *dk;
199 
200 	(void)printf(" procs   memory     page%*s", 20, "");
201 	if (ndrives > 1)
202 		(void)printf("disks %*s  faults      cpu\n",
203 		   ndrives * 3 - 6, "");
204 	else
205 		(void)printf("%*s  faults      cpu\n", ndrives * 3, "");
206 	(void)printf(" r b w   avm   fre  flt  re  pi  po  fr  sr ");
207 	for (dk = dkinfo; dk != NULL; dk = dk->dk_next)
208 		if (dk->dk_sel)
209 			(void)printf("%s ", dk->dk_2c);
210 	(void)printf("  in   sy  cs us sy id\n");
211 	hdrcnt = winlines - 2;
212 }
213 
214 /*
215  * Force a header to be prepended to the next output.
216  */
217 void
218 needhdr(sig)
219 	int sig;
220 {
221 
222 	hdrcnt = 1;
223 }
224