Home | History | Annotate | Line # | Navigate | Download | only in kvmstat

      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
     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
     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
    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
    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
    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
    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
    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
    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
    381 usage()
    382 {
    383 	(void) fprintf(stderr, "%s", g_usage);
    384 	exit(EXIT_FAILURE);
    385 }
    386 
    387 /*ARGSUSED*/
    388 void
    389 intr(int sig)
    390 {}
    391 
    392 /*ARGSUSED*/
    393 int
    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