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
44typedef 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
52typedef 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
62const char *g_cmd = "kvmstat";
63
64static void
65fatal(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
82int
83kstat_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
101int
102kstat_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
120void
121kstat_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
219void
220kstat_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
289uint64_t
290kstat_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
299void
300kstat_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
339boolean_t
340interested(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 */
359char *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
380void
381usage()
382{
383	(void) fprintf(stderr, "%s", g_usage);
384	exit(EXIT_FAILURE);
385}
386
387/*ARGSUSED*/
388void
389intr(int sig)
390{}
391
392/*ARGSUSED*/
393int
394main(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