xref: /illumos-gate/usr/src/cmd/kvmstat/kvmstat.c (revision 7aa76ffc)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
24  */
25 
26 #include <sys/kstat.h>
27 #include <kstat.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <alloca.h>
36 #include <signal.h>
37 #include <sys/varargs.h>
38 #include <sys/int_limits.h>
39 
40 #define	KSTAT_FIELD_USEINSTANCE		0x01
41 #define	KSTAT_FIELD_NODELTA		0x02
42 #define	KSTAT_FIELD_FILLER		0x04
43 
44 typedef struct kstat_field {
45 	char *ksf_header;		/* header for field */
46 	char *ksf_name;			/* name of stat, if any */
47 	int ksf_width;			/* width for field in output line */
48 	uint32_t ksf_flags;		/* flags for this field, if any */
49 	int ksf_hint;			/* index hint for field in kstat */
50 } kstat_field_t;
51 
52 typedef struct kstat_instance {
53 	char ksi_name[KSTAT_STRLEN];	/* name of the underlying kstat */
54 	int ksi_instance;		/* instance identifer of this kstat */
55 	kstat_t *ksi_ksp;		/* pointer to the kstat */
56 	uint64_t *ksi_data[2];		/* pointer to two generations of data */
57 	hrtime_t ksi_snaptime[2];	/* hrtime for data generations */
58 	int ksi_gen;			/* current generation */
59 	struct kstat_instance *ksi_next; /* next in instance list */
60 } kstat_instance_t;
61 
62 const char *g_cmd = "kvmstat";
63 
64 static void
fatal(char * fmt,...)65 fatal(char *fmt, ...)
66 {
67 	va_list ap;
68 	int error = errno;
69 
70 	va_start(ap, fmt);
71 
72 	(void) fprintf(stderr, "%s: ", g_cmd);
73 	/*LINTED*/
74 	(void) vfprintf(stderr, fmt, ap);
75 
76 	if (fmt[strlen(fmt) - 1] != '\n')
77 		(void) fprintf(stderr, ": %s\n", strerror(error));
78 
79 	exit(EXIT_FAILURE);
80 }
81 
82 int
kstat_field_hint(kstat_t * ksp,kstat_field_t * field)83 kstat_field_hint(kstat_t *ksp, kstat_field_t *field)
84 {
85 	kstat_named_t *nm = KSTAT_NAMED_PTR(ksp);
86 	int i;
87 
88 	assert(ksp->ks_type == KSTAT_TYPE_NAMED);
89 
90 	for (i = 0; i < ksp->ks_ndata; i++) {
91 		if (strcmp(field->ksf_name, nm[i].name) == 0)
92 			return (field->ksf_hint = i);
93 	}
94 
95 	fatal("could not find field '%s' in %s:%d\n",
96 	    field->ksf_name, ksp->ks_name, ksp->ks_instance);
97 
98 	return (0);
99 }
100 
101 int
kstat_instances_compare(const void * lhs,const void * rhs)102 kstat_instances_compare(const void *lhs, const void *rhs)
103 {
104 	kstat_instance_t *l = *((kstat_instance_t **)lhs);
105 	kstat_instance_t *r = *((kstat_instance_t **)rhs);
106 	int rval;
107 
108 	if ((rval = strcmp(l->ksi_name, r->ksi_name)) != 0)
109 		return (rval);
110 
111 	if (l->ksi_instance < r->ksi_instance)
112 		return (-1);
113 
114 	if (l->ksi_instance > r->ksi_instance)
115 		return (1);
116 
117 	return (0);
118 }
119 
120 void
kstat_instances_update(kstat_ctl_t * kcp,kstat_instance_t ** head,boolean_t (* interested)(kstat_t *))121 kstat_instances_update(kstat_ctl_t *kcp, kstat_instance_t **head,
122     boolean_t (*interested)(kstat_t *))
123 {
124 	int ninstances = 0, i;
125 	kstat_instance_t **sorted, *ksi, *next;
126 	kstat_t *ksp;
127 	kid_t kid;
128 
129 	if ((kid = kstat_chain_update(kcp)) == 0 && *head != NULL)
130 		return;
131 
132 	if (kid == -1)
133 		fatal("failed to update kstat chain");
134 
135 	for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next)
136 		ksi->ksi_ksp = NULL;
137 
138 	for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
139 		kstat_instance_t *last = NULL;
140 
141 		if (!interested(ksp))
142 			continue;
143 
144 		/*
145 		 * Now look to see if we have this instance and name.  (Yes,
146 		 * this is a linear search; we're assuming that this list is
147 		 * modest in size.)
148 		 */
149 		for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next) {
150 			last = ksi;
151 
152 			if (ksi->ksi_instance != ksp->ks_instance)
153 				continue;
154 
155 			if (strcmp(ksi->ksi_name, ksp->ks_name) != 0)
156 				continue;
157 
158 			ksi->ksi_ksp = ksp;
159 			ninstances++;
160 			break;
161 		}
162 
163 		if (ksi != NULL)
164 			continue;
165 
166 		if ((ksi = malloc(sizeof (kstat_instance_t))) == NULL)
167 			fatal("could not allocate memory for stat instance");
168 
169 		bzero(ksi, sizeof (kstat_instance_t));
170 		(void) strlcpy(ksi->ksi_name, ksp->ks_name, KSTAT_STRLEN);
171 		ksi->ksi_instance = ksp->ks_instance;
172 		ksi->ksi_ksp = ksp;
173 		ksi->ksi_next = NULL;
174 
175 		if (last == NULL) {
176 			assert(*head == NULL);
177 			*head = ksi;
178 		} else {
179 			last->ksi_next = ksi;
180 		}
181 
182 		ninstances++;
183 	}
184 
185 	/*
186 	 * Now we know how many instances we have; iterate back over them,
187 	 * pruning the stale ones and adding the active ones to a holding
188 	 * array in which to sort them.
189 	 */
190 	sorted = (void *)alloca(ninstances * sizeof (kstat_instance_t *));
191 	ninstances = 0;
192 
193 	for (ksi = *head; ksi != NULL; ksi = next) {
194 		next = ksi->ksi_next;
195 
196 		if (ksi->ksi_ksp == NULL) {
197 			free(ksi);
198 		} else {
199 			sorted[ninstances++] = ksi;
200 		}
201 	}
202 
203 	if (ninstances == 0) {
204 		*head = NULL;
205 		return;
206 	}
207 
208 	qsort(sorted, ninstances, sizeof (kstat_instance_t *),
209 	    kstat_instances_compare);
210 
211 	*head = sorted[0];
212 
213 	for (i = 0; i < ninstances; i++) {
214 		ksi = sorted[i];
215 		ksi->ksi_next = i < ninstances - 1 ? sorted[i + 1] : NULL;
216 	}
217 }
218 
219 void
kstat_instances_read(kstat_ctl_t * kcp,kstat_instance_t * instances,kstat_field_t * fields)220 kstat_instances_read(kstat_ctl_t *kcp, kstat_instance_t *instances,
221     kstat_field_t *fields)
222 {
223 	kstat_instance_t *ksi;
224 	int i, nfields;
225 
226 	for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++)
227 		continue;
228 
229 	for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) {
230 		kstat_t *ksp = ksi->ksi_ksp;
231 
232 		if (ksp == NULL)
233 			continue;
234 
235 		if (kstat_read(kcp, ksp, NULL) == -1) {
236 			if (errno == ENXIO) {
237 				/*
238 				 * Our kstat has been removed since the update;
239 				 * NULL it out to prevent us from trying to read
240 				 * it again (and to indicate that it should not
241 				 * be displayed) and drive on.
242 				 */
243 				ksi->ksi_ksp = NULL;
244 				continue;
245 			}
246 
247 			fatal("failed to read kstat %s:%d",
248 			    ksi->ksi_name, ksi->ksi_instance);
249 		}
250 
251 		if (ksp->ks_type != KSTAT_TYPE_NAMED) {
252 			fatal("%s:%d is not a named kstat", ksi->ksi_name,
253 			    ksi->ksi_instance);
254 		}
255 
256 		if (ksi->ksi_data[0] == NULL) {
257 			size_t size = nfields * sizeof (uint64_t) * 2;
258 			uint64_t *data;
259 
260 			if ((data = malloc(size)) == NULL)
261 				fatal("could not allocate memory");
262 
263 			bzero(data, size);
264 			ksi->ksi_data[0] = data;
265 			ksi->ksi_data[1] = &data[nfields];
266 		}
267 
268 		for (i = 0; i < nfields; i++) {
269 			kstat_named_t *nm = KSTAT_NAMED_PTR(ksp);
270 			kstat_field_t *field = &fields[i];
271 			int hint = field->ksf_hint;
272 
273 			if (field->ksf_name == NULL)
274 				continue;
275 
276 			if (hint < 0 || hint >= ksp->ks_ndata ||
277 			    strcmp(field->ksf_name, nm[hint].name) != 0) {
278 				hint = kstat_field_hint(ksp, field);
279 			}
280 
281 			ksi->ksi_data[ksi->ksi_gen][i] = nm[hint].value.ui64;
282 		}
283 
284 		ksi->ksi_snaptime[ksi->ksi_gen] = ksp->ks_snaptime;
285 		ksi->ksi_gen ^= 1;
286 	}
287 }
288 
289 uint64_t
kstat_instances_delta(kstat_instance_t * ksi,int i)290 kstat_instances_delta(kstat_instance_t *ksi, int i)
291 {
292 	int gen = ksi->ksi_gen;
293 	uint64_t delta = ksi->ksi_data[gen ^ 1][i] - ksi->ksi_data[gen][i];
294 	uint64_t tdelta = ksi->ksi_snaptime[gen ^ 1] - ksi->ksi_snaptime[gen];
295 
296 	return (((delta * (uint64_t)NANOSEC) + (tdelta / 2)) / tdelta);
297 }
298 
299 void
kstat_instances_print(kstat_instance_t * instances,kstat_field_t * fields,boolean_t header)300 kstat_instances_print(kstat_instance_t *instances, kstat_field_t *fields,
301     boolean_t header)
302 {
303 	kstat_instance_t *ksi = instances;
304 	int i, nfields;
305 
306 	for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++)
307 		continue;
308 
309 	if (header) {
310 		for (i = 0; i < nfields; i++) {
311 			(void) printf("%*s%c", fields[i].ksf_width,
312 			    fields[i].ksf_header, i < nfields - 1 ? ' ' : '\n');
313 		}
314 	}
315 
316 	for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) {
317 		if (ksi->ksi_snaptime[1] == 0 || ksi->ksi_ksp == NULL)
318 			continue;
319 
320 		for (i = 0; i < nfields; i++) {
321 			char trailer = i < nfields - 1 ? ' ' : '\n';
322 
323 			if (fields[i].ksf_flags & KSTAT_FIELD_FILLER) {
324 				(void) printf("%*s%c", fields[i].ksf_width,
325 				    fields[i].ksf_header, trailer);
326 				continue;
327 			}
328 
329 			(void) printf("%*lld%c", fields[i].ksf_width,
330 			    fields[i].ksf_flags & KSTAT_FIELD_USEINSTANCE ?
331 			    ksi->ksi_instance :
332 			    fields[i].ksf_flags & KSTAT_FIELD_NODELTA ?
333 			    ksi->ksi_data[ksi->ksi_gen ^ 1][i] :
334 			    kstat_instances_delta(ksi, i), trailer);
335 		}
336 	}
337 }
338 
339 boolean_t
interested(kstat_t * ksp)340 interested(kstat_t *ksp)
341 {
342 	const char *module = "kvm";
343 	const char *class = "misc";
344 	const char *name = "vcpu-";
345 
346 	if (strcmp(ksp->ks_module, module) != 0)
347 		return (B_FALSE);
348 
349 	if (strcmp(ksp->ks_class, class) != 0)
350 		return (B_FALSE);
351 
352 	if (strstr(ksp->ks_name, name) != ksp->ks_name)
353 		return (B_FALSE);
354 
355 	return (B_TRUE);
356 }
357 
358 /* BEGIN CSTYLED */
359 char *g_usage = "Usage: kvmstat [interval [count]]\n"
360     "\n"
361     "  Displays statistics for running kernel virtual machines, with one line\n"
362     "  per virtual CPU.  All statistics are reported as per-second rates.\n"
363     "\n"
364     "  The columns are as follows:\n"
365     "\n"
366     "    pid    =>  identifier of process controlling the virtual CPU\n"
367     "    vcpu   =>  virtual CPU identifier relative to its virtual machine\n"
368     "    exits  =>  virtual machine exits for the virtual CPU\n"
369     "    haltx  =>  virtual machine exits due to the HLT instruction\n"
370     "    irqx   =>  virtual machine exits due to a pending external interrupt\n"
371     "    irqwx  =>  virtual machine exits due to an open interrupt window\n"
372     "    iox    =>  virtual machine exits due to an I/O instruction\n"
373     "    mmiox  =>  virtual machine exits due to memory mapped I/O \n"
374     "    irqs   =>  interrupts injected into the virtual CPU\n"
375     "    emul   =>  instructions emulated in the kernel\n"
376     "    eptv   =>  extended page table violations\n"
377     "\n";
378 /* END CSTYLED */
379 
380 void
usage()381 usage()
382 {
383 	(void) fprintf(stderr, "%s", g_usage);
384 	exit(EXIT_FAILURE);
385 }
386 
387 /*ARGSUSED*/
388 void
intr(int sig)389 intr(int sig)
390 {}
391 
392 /*ARGSUSED*/
393 int
main(int argc,char ** argv)394 main(int argc, char **argv)
395 {
396 	kstat_ctl_t *kcp;
397 	kstat_instance_t *instances = NULL;
398 	int i = 0;
399 	int interval = 1;
400 	int count = INT32_MAX;
401 	struct itimerval itimer;
402 	struct sigaction act;
403 	sigset_t set;
404 	char *endp;
405 
406 	kstat_field_t fields[] =  {
407 		{ "pid", "pid", 6, KSTAT_FIELD_NODELTA },
408 		{ "vcpu", NULL, 4, KSTAT_FIELD_USEINSTANCE },
409 		{ "|", NULL, 1, KSTAT_FIELD_FILLER },
410 		{ "exits", "exits", 6 },
411 		{ ":", NULL, 1, KSTAT_FIELD_FILLER },
412 		{ "haltx", "halt-exits", 6 },
413 		{ "irqx", "irq-exits", 6 },
414 		{ "irqwx", "irq-window-exits", 6 },
415 		{ "iox", "io-exits", 6 },
416 		{ "mmiox", "mmio-exits", 6 },
417 		{ "|", NULL, 1, KSTAT_FIELD_FILLER },
418 		{ "irqs", "irq-injections", 6 },
419 		{ "emul", "insn-emulation", 6 },
420 		{ "eptv", "pf-fixed", 6 },
421 		{ NULL }
422 	};
423 
424 	if (argc > 1) {
425 		interval = strtol(argv[1], &endp, 10);
426 
427 		if (*endp != '\0' || interval <= 0)
428 			usage();
429 	}
430 
431 	if (argc > 2) {
432 		count = strtol(argv[2], &endp, 10);
433 
434 		if (*endp != '\0' || count <= 0)
435 			usage();
436 	}
437 
438 	if ((kcp = kstat_open()) == NULL)
439 		fatal("could not open /dev/kstat");
440 
441 	(void) sigemptyset(&act.sa_mask);
442 	act.sa_flags = 0;
443 	act.sa_handler = intr;
444 	(void) sigaction(SIGALRM, &act, NULL);
445 
446 	(void) sigemptyset(&set);
447 	(void) sigaddset(&set, SIGALRM);
448 	(void) sigprocmask(SIG_BLOCK, &set, NULL);
449 
450 	bzero(&itimer, sizeof (itimer));
451 	itimer.it_value.tv_sec = interval;
452 	itimer.it_interval.tv_sec = interval;
453 
454 	if (setitimer(ITIMER_REAL, &itimer, NULL) != 0) {
455 		fatal("could not set timer to %d second%s", interval,
456 		    interval == 1 ? "" : "s");
457 	}
458 
459 	(void) sigemptyset(&set);
460 
461 	for (;;) {
462 		kstat_instances_update(kcp, &instances, interested);
463 		kstat_instances_read(kcp, instances, fields);
464 
465 		if (i++ > 0) {
466 			kstat_instances_print(instances, fields,
467 			    instances != NULL && instances->ksi_next == NULL ?
468 			    (((i - 2) % 20) == 0) : B_TRUE);
469 		}
470 
471 		if (i > count)
472 			break;
473 
474 		(void) sigsuspend(&set);
475 	}
476 
477 	/*NOTREACHED*/
478 	return (0);
479 }
480