xref: /dragonfly/usr.bin/systat/vmmeter.c (revision 7d84b73d)
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, (WideMode ? 24 : 16),
163 				  "%*.*s", "");
164 			if (WideMode)
165 				DRAW_ROW2(n, CPU_START + i, 17, "%*.*s", "");
166 		} else {
167 			DRAW_ROW2(n, CPU_START + i, (WideMode ? 24 : 16),
168 				  "%*.*s",
169 				  vmm_cur[i].v_lock_name);
170 			if (WideMode) {
171 				DRAW_ROWX(n, CPU_START + i, 17,
172 					  "%016lx",
173 					  (uintmax_t)(uintptr_t)
174 						vmm_cur[i].v_lock_addr);
175 			}
176 		}
177 
178 		if (WideMode && vmm_cptime_cur[i].cp_sample_pc) {
179 			void *rip;
180 
181 			rip = (void *)(intptr_t)CPUC(i, sample_pc);
182 			DRAW_ROW2(n, CPU_START + i, 30, "%*.*s",
183 				  address_to_symbol(rip, &symctx));
184 		}
185 	}
186 
187 	/*
188 	 * Top row totals and averages
189 	 */
190 	if (cp_total_all == 0)
191 		cp_total_all = 1;
192 
193 	n = X_START + CPU_LABEL_W;
194 	DRAW_ROW(n, TOT_START, 6, "%*u", DTOT(v_timer));	/* timer */
195 	DRAW_ROW(n, TOT_START, 8, "%*u", DTOT(v_ipi));		/* ipi	*/
196 	DRAW_ROW(n, TOT_START, 8, "%*u", DTOT(v_intr));		/* extint */
197 
198 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(user) +	/* user */
199 					   CPUVTOT(nice));
200 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(sys));	/* sys */
201 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(intr));	/* intr */
202 	DRAW_ROW(n, TOT_START, 6, "%*.1f", CPUVTOT(idle));	/* idle */
203 
204 	DRAW_ROWX(n, TOT_START, 8, "%7u", DTOT(v_lock_colls));
205 	DRAW_ROWX(n, TOT_START, 0, " (%5.2f%% coltot)",
206 		(double)DTOT(v_lock_colls) / 1000000.0);
207 }
208 
209 void
210 fetchvmm(void)
211 {
212 	vmm_fetched = 1;
213 
214 	memcpy(vmm_prev, vmm_cur, sizeof(struct vmmeter) * vmm_ncpus);
215 	memcpy(vmm_cptime_prev, vmm_cptime_cur,
216 	       sizeof(struct kinfo_cputime) * vmm_ncpus);
217 	vmm_totprev = vmm_totcur;
218 	getvmm();
219 }
220 
221 void
222 labelvmm(void)
223 {
224 	int i, n;
225 
226 	clear();
227 
228 	n = X_START + CPU_LABEL_W;
229 
230 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "timer");
231 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "ipi");
232 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "extint");
233 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "user%");
234 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "sys%");
235 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "intr%");
236 	DRAW_ROW(n, TOT_START - 1, 6, "%*s", "idle%");
237 	DRAW_ROW(n, TOT_START - 1, 8, "%*s", "smpcol");
238 	DRAW_ROW(n, TOT_START - 1, (WideMode ? 24 : 16), "%*s", "contention");
239 	if (WideMode) {
240 		DRAW_ROW(n, TOT_START - 1, 17, "%*s", "lockaddr");
241 	}
242 	if (WideMode && getuid() == 0) {
243 		DRAW_ROW(n, TOT_START - 1, 30, "%*s", "sample_pc");
244 	}
245 
246 	n = X_START + CPU_LABEL_W;
247 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
248 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
249 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
250 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
251 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
252 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
253 	DRAW_ROW(n, TOT_START + 1, 6, "%*s", "-----");
254 	DRAW_ROW(n, TOT_START + 1, 8, "%*s", "-------");
255 	DRAW_ROW(n, TOT_START + 1, (WideMode ? 24 : 16),
256 		 "%*s",
257 		 (WideMode ? "-----------------------" : "---------------"));
258 	if (WideMode)
259 		DRAW_ROW(n, TOT_START + 1, 17, "%*s", "---------------");
260 	if (WideMode && getuid() == 0) {
261 		DRAW_ROW(n, TOT_START + 1, 30,
262 			 "%*s", "-----------------------------");
263 	}
264 
265 	mvprintw(TOT_START, X_START, "total");
266 	for (i = 0; i < vmm_ncpus; ++i)
267 		mvprintw(CPU_START + i, X_START, "cpu%d", i);
268 
269 #if 0
270 	n = X_START + CPU_LABEL_W;
271 	DRAW_ROW(n, TOT_STARTX - 1, 15, "%-*s", "contention");
272 	DRAW_ROW(n, TOT_STARTX - 1, 35, "%-*s", "function");
273 
274 	for (i = 0; i < vmm_ncpus; ++i)
275 		mvprintw(CPU_STARTX + i, X_START, "cpu%d", i);
276 #endif
277 }
278 
279 WINDOW *
280 openvmm(void)
281 {
282 	if (symbols_read == 0) {
283 		symbols_read = 1;
284 		read_symbols(NULL);
285 	}
286 
287 	if (kinfo_get_cpus(&vmm_ncpus))
288 		err(1, "kinfo_get_cpus");
289 
290 	vmm_cur = calloc(vmm_ncpus, sizeof(*vmm_cur));
291 	if (vmm_cur == NULL)
292 		err(1, "calloc vmm_cur");
293 
294 	vmm_prev = calloc(vmm_ncpus, sizeof(*vmm_prev));
295 	if (vmm_prev == NULL)
296 		err(1, "calloc vmm_prev");
297 
298 	vmm_cptime_cur = calloc(vmm_ncpus, sizeof(*vmm_cptime_cur));
299 	if (vmm_cptime_cur == NULL)
300 		err(1, "calloc vmm_cptime_cur");
301 
302 	vmm_cptime_prev = calloc(vmm_ncpus, sizeof(*vmm_cptime_prev));
303 	if (vmm_cptime_prev == NULL)
304 		err(1, "calloc vmm_cptime_prev");
305 
306 	getvmm();
307 
308 	return (stdscr);
309 }
310 
311 void
312 closevmm(WINDOW *w)
313 {
314 	if (vmm_cur != NULL)
315 		free(vmm_cur);
316 	if (vmm_prev != NULL)
317 		free(vmm_prev);
318 
319 	if (vmm_cptime_cur != NULL)
320 		free(vmm_cptime_cur);
321 	if (vmm_cptime_prev != NULL)
322 		free(vmm_cptime_prev);
323 
324 	vmm_fetched = 0;
325 
326 	if (w == NULL)
327 		return;
328 	wclear(w);
329 	wrefresh(w);
330 }
331