3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
9 # Avi Kivity <avi@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2. See
12 # the COPYING file in the top-level directory.
15 import sys
, os
, time
, optparse
, ctypes
18 class DebugfsProvider(object):
20 self
.base
= '/sys/kernel/debug/kvm'
21 self
._fields
= os
.listdir(self
.base
)
24 def select(self
, fields
):
28 return int(file(self
.base
+ '/' + key
).read())
29 return dict([(key
, val(key
)) for key
in self
._fields
])
33 1: 'EXTERNAL_INTERRUPT',
35 7: 'PENDING_INTERRUPT',
59 36: 'MWAIT_INSTRUCTION',
60 39: 'MONITOR_INSTRUCTION',
61 40: 'PAUSE_INSTRUCTION',
62 41: 'MCE_DURING_VMENTRY',
63 43: 'TPR_BELOW_THRESHOLD',
104 0x065: 'CR0_SEL_WRITE',
128 0x07d: 'TASK_SWITCH',
129 0x07e: 'FERR_FREEZE',
148 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
149 aarch64_exit_reasons
= {
187 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
188 userspace_exit_reasons
= {
196 7: 'IRQ_WINDOW_OPEN',
206 17: 'INTERNAL_ERROR',
217 'vmx': vmx_exit_reasons
,
218 'svm': svm_exit_reasons
,
221 sc_perf_evt_open
= None
225 'SET_FILTER' : 0x40082406,
226 'ENABLE' : 0x00002400,
227 'DISABLE' : 0x00002401,
228 'RESET' : 0x00002403,
233 'sc_perf_evt_open' : 298,
234 'exit_reasons' : x86_exit_reasons
[flag
],
239 'sc_perf_evt_open' : 331
244 'sc_perf_evt_open' : 319,
246 'SET_FILTER' : 0x80002406 |
(ctypes
.sizeof(ctypes
.c_char_p
) << 16),
247 'ENABLE' : 0x20002400,
248 'DISABLE' : 0x20002401,
254 'sc_perf_evt_open' : 241,
255 'exit_reasons' : aarch64_exit_reasons
,
258 def detect_platform():
259 if os
.uname()[4].startswith('ppc'):
262 elif os
.uname()[4].startswith('aarch64'):
266 for line
in file('/proc/cpuinfo').readlines():
267 if line
.startswith('flags'):
268 for flag
in line
.split():
269 if flag
in x86_exit_reasons
:
272 elif line
.startswith('vendor_id'):
273 for flag
in line
.split():
274 if flag
== 'IBM/S390':
281 return dict((x
[1], x
[0]) for x
in d
.iteritems())
284 filters
['kvm_userspace_exit'] = ('reason', invert(userspace_exit_reasons
))
286 filters
['kvm_exit'] = ('exit_reason', invert(exit_reasons
))
290 libc
= ctypes
.CDLL('libc.so.6')
291 syscall
= libc
.syscall
292 get_errno
= libc
.__errno
_location
293 get_errno
.restype
= POINTER(c_int
)
295 class perf_event_attr(ctypes
.Structure
):
296 _fields_
= [('type', ctypes
.c_uint32
),
297 ('size', ctypes
.c_uint32
),
298 ('config', ctypes
.c_uint64
),
299 ('sample_freq', ctypes
.c_uint64
),
300 ('sample_type', ctypes
.c_uint64
),
301 ('read_format', ctypes
.c_uint64
),
302 ('flags', ctypes
.c_uint64
),
303 ('wakeup_events', ctypes
.c_uint32
),
304 ('bp_type', ctypes
.c_uint32
),
305 ('bp_addr', ctypes
.c_uint64
),
306 ('bp_len', ctypes
.c_uint64
),
308 def _perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
309 return syscall(sc_perf_evt_open
, ctypes
.pointer(attr
), ctypes
.c_int(pid
),
310 ctypes
.c_int(cpu
), ctypes
.c_int(group_fd
),
311 ctypes
.c_long(flags
))
313 PERF_TYPE_HARDWARE
= 0
314 PERF_TYPE_SOFTWARE
= 1
315 PERF_TYPE_TRACEPOINT
= 2
316 PERF_TYPE_HW_CACHE
= 3
318 PERF_TYPE_BREAKPOINT
= 5
320 PERF_SAMPLE_IP
= 1 << 0
321 PERF_SAMPLE_TID
= 1 << 1
322 PERF_SAMPLE_TIME
= 1 << 2
323 PERF_SAMPLE_ADDR
= 1 << 3
324 PERF_SAMPLE_READ
= 1 << 4
325 PERF_SAMPLE_CALLCHAIN
= 1 << 5
326 PERF_SAMPLE_ID
= 1 << 6
327 PERF_SAMPLE_CPU
= 1 << 7
328 PERF_SAMPLE_PERIOD
= 1 << 8
329 PERF_SAMPLE_STREAM_ID
= 1 << 9
330 PERF_SAMPLE_RAW
= 1 << 10
332 PERF_FORMAT_TOTAL_TIME_ENABLED
= 1 << 0
333 PERF_FORMAT_TOTAL_TIME_RUNNING
= 1 << 1
334 PERF_FORMAT_ID
= 1 << 2
335 PERF_FORMAT_GROUP
= 1 << 3
339 sys_tracing
= '/sys/kernel/debug/tracing'
342 def __init__(self
, cpu
):
344 self
.group_leader
= None
346 def add_event(self
, name
, event_set
, tracepoint
, filter = None):
347 self
.events
.append(Event(group
= self
,
348 name
= name
, event_set
= event_set
,
349 tracepoint
= tracepoint
, filter = filter))
350 if len(self
.events
) == 1:
351 self
.file = os
.fdopen(self
.events
[0].fd
)
353 bytes
= 8 * (1 + len(self
.events
))
354 fmt
= 'xxxxxxxx' + 'q' * len(self
.events
)
355 return dict(zip([event
.name
for event
in self
.events
],
356 struct
.unpack(fmt
, self
.file.read(bytes
))))
359 def __init__(self
, group
, name
, event_set
, tracepoint
, filter = None):
361 attr
= perf_event_attr()
362 attr
.type = PERF_TYPE_TRACEPOINT
363 attr
.size
= ctypes
.sizeof(attr
)
364 id_path
= os
.path
.join(sys_tracing
, 'events', event_set
,
366 id = int(file(id_path
).read())
368 attr
.sample_type
= (PERF_SAMPLE_RAW
371 attr
.sample_period
= 1
372 attr
.read_format
= PERF_FORMAT_GROUP
375 group_leader
= group
.events
[0].fd
376 fd
= _perf_event_open(attr
, -1, group
.cpu
, group_leader
, 0)
379 raise Exception('perf_event_open failed, errno = ' + err
.__str
__())
382 fcntl
.ioctl(fd
, ioctl_numbers
['SET_FILTER'], filter)
386 fcntl
.ioctl(self
.fd
, ioctl_numbers
['ENABLE'], 0)
389 fcntl
.ioctl(self
.fd
, ioctl_numbers
['DISABLE'], 0)
392 fcntl
.ioctl(self
.fd
, ioctl_numbers
['RESET'], 0)
394 class TracepointProvider(object):
396 path
= os
.path
.join(sys_tracing
, 'events', 'kvm')
398 for f
in os
.listdir(path
)
399 if os
.path
.isdir(os
.path
.join(path
, f
))]
403 subfield
, values
= filters
[f
]
404 for name
, number
in values
.iteritems():
405 extra
.append(f
+ '(' + name
+ ')')
412 def _online_cpus(self
):
414 pattern
= r
'cpu([0-9]+)'
415 basedir
= '/sys/devices/system/cpu'
416 for entry
in os
.listdir(basedir
):
417 match
= re
.match(pattern
, entry
)
420 path
= os
.path
.join(basedir
, entry
, 'online')
421 if os
.path
.exists(path
) and open(path
).read().strip() != '1':
423 l
.append(int(match
.group(1)))
426 def _setup(self
, _fields
):
427 self
._fields
= _fields
428 cpus
= self
._online
_cpus
()
430 nfiles
= len(cpus
) * 1000
431 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (nfiles
, nfiles
))
433 self
.group_leaders
= []
439 m
= re
.match(r
'(.*)\((.*)\)', name
)
441 tracepoint
, sub
= m
.groups()
442 filter = '%s==%d\0' % (filters
[tracepoint
][0],
443 filters
[tracepoint
][1][sub
])
444 event
= group
.add_event(name
, event_set
= 'kvm',
445 tracepoint
= tracepoint
,
447 self
.group_leaders
.append(group
)
448 def select(self
, fields
):
449 for group
in self
.group_leaders
:
450 for event
in group
.events
:
451 if event
.name
in fields
:
457 from collections
import defaultdict
458 ret
= defaultdict(int)
459 for group
in self
.group_leaders
:
460 for name
, val
in group
.read().iteritems():
465 def __init__(self
, providers
, fields
= None):
466 self
.providers
= providers
467 self
.fields_filter
= fields
472 if not self
.fields_filter
:
474 return re
.match(self
.fields_filter
, key
) is not None
477 provider_fields
= [key
for key
in d
.fields() if wanted(key
)]
478 for key
in provider_fields
:
479 self
.values
[key
] = None
480 d
.select(provider_fields
)
481 def set_fields_filter(self
, fields_filter
):
482 self
.fields_filter
= fields_filter
487 for key
in d
.fields():
488 oldval
= self
.values
.get(key
, (0, 0))
491 if oldval
is not None:
492 newdelta
= newval
- oldval
[0]
493 self
.values
[key
] = (newval
, newdelta
)
496 if not os
.access('/sys/kernel/debug', os
.F_OK
):
497 print 'Please enable CONFIG_DEBUG_FS in your kernel'
499 if not os
.access('/sys/kernel/debug/kvm', os
.F_OK
):
500 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
501 print "and ensure the kvm modules are loaded"
507 def tui(screen
, stats
):
508 curses
.use_default_colors()
511 fields_filter
= stats
.fields_filter
512 def update_drilldown():
513 if not fields_filter
:
515 stats
.set_fields_filter(None)
517 stats
.set_fields_filter(r
'^[^\(]*$')
519 def refresh(sleeptime
):
521 screen
.addstr(0, 0, 'kvm statistics')
522 screen
.addstr(2, 1, 'Event')
523 screen
.addstr(2, 1 + label_width
+ number_width
- len('Total'), 'Total')
524 screen
.addstr(2, 1 + label_width
+ number_width
+ 8 - len('Current'), 'Current')
529 return (-s
[x
][1], -s
[x
][0])
532 for key
in sorted(s
.keys(), key
= sortkey
):
533 if row
>= screen
.getmaxyx()[0]:
536 if not values
[0] and not values
[1]:
539 screen
.addstr(row
, col
, key
)
541 screen
.addstr(row
, col
, '%10d' % (values
[0],))
543 if values
[1] is not None:
544 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
551 curses
.halfdelay(int(sleeptime
* 10))
556 drilldown
= not drilldown
560 except KeyboardInterrupt:
569 for key
in sorted(s
.keys()):
571 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
574 keys
= sorted(stats
.get().iterkeys())
577 print '%10s' % k
[0:9],
582 print ' %9d' % s
[k
][1],
588 if line
% banner_repeat
== 0:
593 options
= optparse
.OptionParser()
594 options
.add_option('-1', '--once', '--batch',
595 action
= 'store_true',
598 help = 'run in batch mode for one second',
600 options
.add_option('-l', '--log',
601 action
= 'store_true',
604 help = 'run in logging mode (like vmstat)',
606 options
.add_option('-t', '--tracepoints',
607 action
= 'store_true',
609 dest
= 'tracepoints',
610 help = 'retrieve statistics from tracepoints',
612 options
.add_option('-d', '--debugfs',
613 action
= 'store_true',
616 help = 'retrieve statistics from debugfs',
618 options
.add_option('-f', '--fields',
622 help = 'fields to display (regex)',
624 (options
, args
) = options
.parse_args(sys
.argv
)
627 if options
.tracepoints
:
628 providers
.append(TracepointProvider())
630 providers
.append(DebugfsProvider())
632 if len(providers
) == 0:
634 providers
= [TracepointProvider()]
636 providers
= [DebugfsProvider()]
638 stats
= Stats(providers
, fields
= options
.fields
)
642 elif not options
.once
:
643 import curses
.wrapper
644 curses
.wrapper(tui
, stats
)