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.
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.
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]
23 * Copyright (c) 2011, Joyent, Inc. All rights reserved.
26 #include <sys/kstat.h>
37 #include <sys/varargs.h>
38 #include <sys/int_limits.h>
40 #define KSTAT_FIELD_USEINSTANCE 0x01
41 #define KSTAT_FIELD_NODELTA 0x02
42 #define KSTAT_FIELD_FILLER 0x04
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 */
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 */
62 const char *g_cmd
= "kvmstat";
72 (void) fprintf(stderr
, "%s: ", g_cmd
);
74 (void) vfprintf(stderr
, fmt
, ap
);
76 if (fmt
[strlen(fmt
) - 1] != '\n')
77 (void) fprintf(stderr
, ": %s\n", strerror(error
));
83 kstat_field_hint(kstat_t
*ksp
, kstat_field_t
*field
)
85 kstat_named_t
*nm
= KSTAT_NAMED_PTR(ksp
);
88 assert(ksp
->ks_type
== KSTAT_TYPE_NAMED
);
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
);
95 fatal("could not find field '%s' in %s:%d\n",
96 field
->ksf_name
, ksp
->ks_name
, ksp
->ks_instance
);
102 kstat_instances_compare(const void *lhs
, const void *rhs
)
104 kstat_instance_t
*l
= *((kstat_instance_t
**)lhs
);
105 kstat_instance_t
*r
= *((kstat_instance_t
**)rhs
);
108 if ((rval
= strcmp(l
->ksi_name
, r
->ksi_name
)) != 0)
111 if (l
->ksi_instance
< r
->ksi_instance
)
114 if (l
->ksi_instance
> r
->ksi_instance
)
121 kstat_instances_update(kstat_ctl_t
*kcp
, kstat_instance_t
**head
,
122 boolean_t (*interested
)(kstat_t
*))
124 int ninstances
= 0, i
;
125 kstat_instance_t
**sorted
, *ksi
, *next
;
129 if ((kid
= kstat_chain_update(kcp
)) == 0 && *head
!= NULL
)
133 fatal("failed to update kstat chain");
135 for (ksi
= *head
; ksi
!= NULL
; ksi
= ksi
->ksi_next
)
138 for (ksp
= kcp
->kc_chain
; ksp
!= NULL
; ksp
= ksp
->ks_next
) {
139 kstat_instance_t
*last
= NULL
;
141 if (!interested(ksp
))
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
149 for (ksi
= *head
; ksi
!= NULL
; ksi
= ksi
->ksi_next
) {
152 if (ksi
->ksi_instance
!= ksp
->ks_instance
)
155 if (strcmp(ksi
->ksi_name
, ksp
->ks_name
) != 0)
166 if ((ksi
= malloc(sizeof (kstat_instance_t
))) == NULL
)
167 fatal("could not allocate memory for stat instance");
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
;
173 ksi
->ksi_next
= NULL
;
176 assert(*head
== NULL
);
179 last
->ksi_next
= ksi
;
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.
190 sorted
= (void *)alloca(ninstances
* sizeof (kstat_instance_t
*));
193 for (ksi
= *head
; ksi
!= NULL
; ksi
= next
) {
194 next
= ksi
->ksi_next
;
196 if (ksi
->ksi_ksp
== NULL
) {
199 sorted
[ninstances
++] = ksi
;
203 if (ninstances
== 0) {
208 qsort(sorted
, ninstances
, sizeof (kstat_instance_t
*),
209 kstat_instances_compare
);
213 for (i
= 0; i
< ninstances
; i
++) {
215 ksi
->ksi_next
= i
< ninstances
- 1 ? sorted
[i
+ 1] : NULL
;
220 kstat_instances_read(kstat_ctl_t
*kcp
, kstat_instance_t
*instances
,
221 kstat_field_t
*fields
)
223 kstat_instance_t
*ksi
;
226 for (nfields
= 0; fields
[nfields
].ksf_header
!= NULL
; nfields
++)
229 for (ksi
= instances
; ksi
!= NULL
; ksi
= ksi
->ksi_next
) {
230 kstat_t
*ksp
= ksi
->ksi_ksp
;
235 if (kstat_read(kcp
, ksp
, NULL
) == -1) {
236 if (errno
== ENXIO
) {
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.
247 fatal("failed to read kstat %s:%d",
248 ksi
->ksi_name
, ksi
->ksi_instance
);
251 if (ksp
->ks_type
!= KSTAT_TYPE_NAMED
) {
252 fatal("%s:%d is not a named kstat", ksi
->ksi_name
,
256 if (ksi
->ksi_data
[0] == NULL
) {
257 size_t size
= nfields
* sizeof (uint64_t) * 2;
260 if ((data
= malloc(size
)) == NULL
)
261 fatal("could not allocate memory");
264 ksi
->ksi_data
[0] = data
;
265 ksi
->ksi_data
[1] = &data
[nfields
];
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
;
273 if (field
->ksf_name
== NULL
)
276 if (hint
< 0 || hint
>= ksp
->ks_ndata
||
277 strcmp(field
->ksf_name
, nm
[hint
].name
) != 0) {
278 hint
= kstat_field_hint(ksp
, field
);
281 ksi
->ksi_data
[ksi
->ksi_gen
][i
] = nm
[hint
].value
.ui64
;
284 ksi
->ksi_snaptime
[ksi
->ksi_gen
] = ksp
->ks_snaptime
;
290 kstat_instances_delta(kstat_instance_t
*ksi
, int i
)
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
];
296 return (((delta
* (uint64_t)NANOSEC
) + (tdelta
/ 2)) / tdelta
);
300 kstat_instances_print(kstat_instance_t
*instances
, kstat_field_t
*fields
,
303 kstat_instance_t
*ksi
= instances
;
306 for (nfields
= 0; fields
[nfields
].ksf_header
!= NULL
; nfields
++)
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');
316 for (ksi
= instances
; ksi
!= NULL
; ksi
= ksi
->ksi_next
) {
317 if (ksi
->ksi_snaptime
[1] == 0 || ksi
->ksi_ksp
== NULL
)
320 for (i
= 0; i
< nfields
; i
++) {
321 char trailer
= i
< nfields
- 1 ? ' ' : '\n';
323 if (fields
[i
].ksf_flags
& KSTAT_FIELD_FILLER
) {
324 (void) printf("%*s%c", fields
[i
].ksf_width
,
325 fields
[i
].ksf_header
, trailer
);
329 (void) printf("%*lld%c", fields
[i
].ksf_width
,
330 fields
[i
].ksf_flags
& KSTAT_FIELD_USEINSTANCE
?
332 fields
[i
].ksf_flags
& KSTAT_FIELD_NODELTA
?
333 ksi
->ksi_data
[ksi
->ksi_gen
^ 1][i
] :
334 kstat_instances_delta(ksi
, i
), trailer
);
340 interested(kstat_t
*ksp
)
342 const char *module
= "kvm";
343 const char *class = "misc";
344 const char *name
= "vcpu-";
346 if (strcmp(ksp
->ks_module
, module
) != 0)
349 if (strcmp(ksp
->ks_class
, class) != 0)
352 if (strstr(ksp
->ks_name
, name
) != ksp
->ks_name
)
359 char *g_usage
= "Usage: kvmstat [interval [count]]\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"
364 " The columns are as follows:\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"
383 (void) fprintf(stderr
, "%s", g_usage
);
394 main(int argc
, char **argv
)
397 kstat_instance_t
*instances
= NULL
;
400 int count
= INT32_MAX
;
401 struct itimerval itimer
;
402 struct sigaction act
;
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 },
425 interval
= strtol(argv
[1], &endp
, 10);
427 if (*endp
!= '\0' || interval
<= 0)
432 count
= strtol(argv
[2], &endp
, 10);
434 if (*endp
!= '\0' || count
<= 0)
438 if ((kcp
= kstat_open()) == NULL
)
439 fatal("could not open /dev/kstat");
441 (void) sigemptyset(&act
.sa_mask
);
443 act
.sa_handler
= intr
;
444 (void) sigaction(SIGALRM
, &act
, NULL
);
446 (void) sigemptyset(&set
);
447 (void) sigaddset(&set
, SIGALRM
);
448 (void) sigprocmask(SIG_BLOCK
, &set
, NULL
);
450 bzero(&itimer
, sizeof (itimer
));
451 itimer
.it_value
.tv_sec
= interval
;
452 itimer
.it_interval
.tv_sec
= interval
;
454 if (setitimer(ITIMER_REAL
, &itimer
, NULL
) != 0) {
455 fatal("could not set timer to %d second%s", interval
,
456 interval
== 1 ? "" : "s");
459 (void) sigemptyset(&set
);
462 kstat_instances_update(kcp
, &instances
, interested
);
463 kstat_instances_read(kcp
, instances
, fields
);
466 kstat_instances_print(instances
, fields
,
467 instances
!= NULL
&& instances
->ksi_next
== NULL
?
468 (((i
- 2) % 20) == 0) : B_TRUE
);
474 (void) sigsuspend(&set
);