xref: /dragonfly/usr.bin/systat/vmmeter.c (revision dda92f98)
1 #define _KERNEL_STRUCTURES
2 #include <sys/param.h>
3 #include <sys/sysctl.h>
4 #include <sys/vmmeter.h>
5 #include "symbols.h"
6 
7 #include <err.h>
8 #include <kinfo.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <devstat.h>
14 
15 #include "systat.h"
16 #include "extern.h"
17 
18 #define X_START		1
19 #define TOT_START	1
20 #define CPU_START	3
21 #define CPU_STARTX	(CPU_START + 1 + vmm_ncpus)
22 #define CPU_LABEL_W	7
23 
24 #define DRAW_ROW(n, y, w, fmt, args...)		\
25 do {						\
26 	mvprintw(y, n, fmt, w - 1, args);	\
27 	n += w;					\
28 } while (0)
29 
30 #define DRAW_ROW2(n, y, w, fmt, args...)	\
31 do {						\
32 	mvprintw(y, n, fmt, w - 1, w - 1, args);\
33 	n += w; \
34 } while (0)
35 
36 #define DRAW_ROWX(n, y, w, fmt, args...)	\
37 do {						\
38 	mvprintw(y, n, fmt, args);		\
39 	n += w;					\
40 } while (0)
41 
42 static int vmm_ncpus;
43 static int vmm_fetched;
44 static struct vmmeter vmm_totcur, vmm_totprev;
45 static struct vmmeter *vmm_cur, *vmm_prev;
46 static struct kinfo_cputime *vmm_cptime_cur, *vmm_cptime_prev;
47 static struct save_ctx symctx;
48 static int symbols_read;
49 
50 static void
51 getvmm(void)
52 {
53 	size_t sz;
54 	int i;
55 
56 	vmm_totcur.v_timer = 0;
57 	vmm_totcur.v_ipi = 0;
58 	vmm_totcur.v_intr = 0;
59 	vmm_totcur.v_lock_colls = 0;
60 
61 	for (i = 0; i < vmm_ncpus; ++i) {
62 		struct vmmeter *vmm = &vmm_cur[i];
63 		char buf[64];
64 
65 		sz = sizeof(*vmm);
66 		snprintf(buf, sizeof(buf), "vm.cpu%d.vmmeter", i);
67 		if (sysctlbyname(buf, vmm, &sz, NULL, 0))
68 			err(1, "sysctlbyname(cpu%d)", i);
69 
70 		vmm->v_intr -= (vmm->v_timer + vmm->v_ipi);
71 
72 		vmm_totcur.v_timer += vmm->v_timer;
73 		vmm_totcur.v_ipi += vmm->v_ipi;
74 		vmm_totcur.v_intr += vmm->v_intr;
75 		vmm_totcur.v_lock_colls += vmm->v_lock_colls;
76 	}
77 
78 	sz = vmm_ncpus * sizeof(struct kinfo_cputime);
79 	if (sysctlbyname("kern.cputime", vmm_cptime_cur, &sz, NULL, 0))
80 		err(1, "kern.cputime");
81 }
82 
83 int
84 initvmm(void)
85 {
86 	return 1;
87 }
88 
89 #define D(idx, field)						\
90 	(u_int)((vmm_cur[idx].field - vmm_prev[idx].field) / naptime)
91 
92 #define CPUD(dif, idx, field)					\
93 do {								\
94 	dif.cp_##field = vmm_cptime_cur[idx].cp_##field -	\
95 			 vmm_cptime_prev[idx].cp_##field;	\
96 	dtot.cp_##field += vmm_cptime_cur[idx].cp_##field -	\
97 			 vmm_cptime_prev[idx].cp_##field;	\
98 	cp_total += dif.cp_##field;				\
99 	cp_total_all += dif.cp_##field;				\
100 } while (0)
101 
102 #define CPUV(dif, field)					\
103 	(dif.cp_##field * 100.0) / cp_total
104 
105 #define CPUC(idx, field) vmm_cptime_cur[idx].cp_##field
106 
107 #define CPUVTOT(field)						\
108 	(dtot.cp_##field * 100.0) / cp_total_all
109 
110 #define DTOT(field)						\
111 	(u_int)((vmm_totcur.field - vmm_totprev.field) / naptime)
112 
113 
114 void
115 showvmm(void)
116 {
117 	struct kinfo_cputime dtot;
118 	uint64_t cp_total_all = 0;
119 	int i, n;
120 
121 	if (!vmm_fetched)
122 		return;
123 
124 	bzero(&dtot, sizeof(dtot));
125 
126 	for (i = 0; i < vmm_ncpus; ++i) {
127 		struct kinfo_cputime d;
128 		uint64_t cp_total = 0;
129 
130 		n = X_START + CPU_LABEL_W;
131 
132 		DRAW_ROW(n, CPU_START + i, 6, "%*u", D(i, v_timer));
133 		DRAW_ROW(n, CPU_START + i, 8, "%*u", D(i, v_ipi));
134 		DRAW_ROW(n, CPU_START + i, 8, "%*u", D(i, v_intr));
135 
136 		CPUD(d, i, user);
137 		CPUD(d, i, idle);
138 		CPUD(d, i, intr);
139 		CPUD(d, i, nice);
140 		CPUD(d, i, sys);
141 
142 		if (cp_total == 0)
143 			cp_total = 1;
144 
145 		DRAW_ROW(n, CPU_START + i, 6, "%*.1f",
146 			 CPUV(d, user) + CPUV(d, nice));
147 		DRAW_ROW(n, CPU_START + i, 6, "%*.1f", CPUV(d, sys));
148 		DRAW_ROW(n, CPU_START + i, 6, "%*.1f", CPUV(d, intr));
149 		DRAW_ROW(n, CPU_START + i, 6, "%*.1f", CPUV(d, idle));
150 
151 		/*
152 		 * Display token collision count and the last-colliding
153 		 * token name.
154 		 */
155 		if (D(i, v_lock_colls) > 9999999)
156 			DRAW_ROW(n, CPU_START + i, 8, "%*u", 9999999);
157 		else
158 			DRAW_ROW(n, CPU_START + i, 8, "%*u",
159 				 D(i, v_lock_colls));
160 
161 		if (D(i, v_lock_colls) == 0) {
162 			DRAW_ROW2(n, CPU_START + i, 18, "%*.*s", "");
163 		} else {
164 			DRAW_ROW2(n, CPU_START + i, 18, "%*.*s",
165 				  vmm_cur[i].v_lock_name);
166 		}
167 
168 		if (vmm_cptime_cur[i].cp_sample_pc) {
169 			void *rip;
170 
171 			rip = (void *)(intptr_t)CPUC(i, sample_pc);
172 			DRAW_ROW2(n, CPU_START + i, 30, "%*.*s",
173 				  address_to_symbol(rip, &symctx));
174 		}
175 	}
176 
177 	/*
178 	 * Top row totals and averages
179 	 */
180 	if (cp_total_all == 0)
181 		cp_total_all = 1;
182 
183 	n = X_START + CPU_LABEL_W;
184 	DRAW_ROW(n, TOT_START, 6, "%*u", DTOT(v_timer));	/* timer */
185 	DRAW_ROW(n, TOT_START, 8, "%*u", DTOT(v_ipi));		/* ipi	*/
186 	DRAW_ROW(n, TOT_START, 8, "%*u", DTOT(v_intr));		/* extint */
187 
188 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(user) +	/* user */
189 					   CPUVTOT(nice));
190 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(sys));	/* sys */
191 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(intr));	/* intr */
192 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(idle));	/* idle */
193 
194 	DRAW_ROWX(n, TOT_START, 8, "%7u", DTOT(v_lock_colls));
195 	DRAW_ROWX(n, TOT_START, 0, " (%5.2f%% coltot)",
196 		(double)DTOT(v_lock_colls) / 1000000.0);
197 }
198 
199 void
200 fetchvmm(void)
201 {
202 	vmm_fetched = 1;
203 
204 	memcpy(vmm_prev, vmm_cur, sizeof(struct vmmeter) * vmm_ncpus);
205 	memcpy(vmm_cptime_prev, vmm_cptime_cur,
206 	       sizeof(struct kinfo_cputime) * vmm_ncpus);
207 	vmm_totprev = vmm_totcur;
208 	getvmm();
209 }
210 
211 void
212 labelvmm(void)
213 {
214 	int i, n;
215 
216 	clear();
217 
218 	n = X_START + CPU_LABEL_W;
219 
220 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "timer");
221 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "ipi");
222 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "extint");
223 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "user%");
224 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "sys%");
225 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "intr%");
226 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "idle%");
227 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "smpcol");
228 	DRAW_ROW(n, TOT_START - 1, 18, "%*s", "label");
229 	if (getuid() == 0) {
230 		DRAW_ROW(n, TOT_START - 1, 30, "%*s", "sample_pc");
231 	}
232 
233 	n = X_START + CPU_LABEL_W;
234 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
235 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
236 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
237 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
238 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
239 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
240 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
241 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
242 	DRAW_ROW(n, TOT_START + 1, 18, "%*s", "-----------------");
243 	if (getuid() == 0) {
244 		DRAW_ROW(n, TOT_START - 1, 30, "%*s", "---------");
245 	}
246 
247 	mvprintw(TOT_START, X_START, "total");
248 	for (i = 0; i < vmm_ncpus; ++i)
249 		mvprintw(CPU_START + i, X_START, "cpu%d", i);
250 
251 #if 0
252 	n = X_START + CPU_LABEL_W;
253 	DRAW_ROW(n, TOT_STARTX - 1, 15, "%-*s", "contention");
254 	DRAW_ROW(n, TOT_STARTX - 1, 35, "%-*s", "function");
255 
256 	for (i = 0; i < vmm_ncpus; ++i)
257 		mvprintw(CPU_STARTX + i, X_START, "cpu%d", i);
258 #endif
259 }
260 
261 WINDOW *
262 openvmm(void)
263 {
264 	if (symbols_read == 0) {
265 		symbols_read = 1;
266 		read_symbols(NULL);
267 	}
268 
269 	if (kinfo_get_cpus(&vmm_ncpus))
270 		err(1, "kinfo_get_cpus");
271 
272 	vmm_cur = calloc(vmm_ncpus, sizeof(*vmm_cur));
273 	if (vmm_cur == NULL)
274 		err(1, "calloc vmm_cur");
275 
276 	vmm_prev = calloc(vmm_ncpus, sizeof(*vmm_prev));
277 	if (vmm_prev == NULL)
278 		err(1, "calloc vmm_prev");
279 
280 	vmm_cptime_cur = calloc(vmm_ncpus, sizeof(*vmm_cptime_cur));
281 	if (vmm_cptime_cur == NULL)
282 		err(1, "calloc vmm_cptime_cur");
283 
284 	vmm_cptime_prev = calloc(vmm_ncpus, sizeof(*vmm_cptime_prev));
285 	if (vmm_cptime_prev == NULL)
286 		err(1, "calloc vmm_cptime_prev");
287 
288 	getvmm();
289 
290 	return (stdscr);
291 }
292 
293 void
294 closevmm(WINDOW *w)
295 {
296 	if (vmm_cur != NULL)
297 		free(vmm_cur);
298 	if (vmm_prev != NULL)
299 		free(vmm_prev);
300 
301 	if (vmm_cptime_cur != NULL)
302 		free(vmm_cptime_cur);
303 	if (vmm_cptime_prev != NULL)
304 		free(vmm_cptime_prev);
305 
306 	vmm_fetched = 0;
307 
308 	if (w == NULL)
309 		return;
310 	wclear(w);
311 	wrefresh(w);
312 }
313