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