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.
24 from collections
import defaultdict
28 'EXTERNAL_INTERRUPT': 1,
30 'PENDING_INTERRUPT': 7,
54 'MWAIT_INSTRUCTION': 36,
55 'MONITOR_INSTRUCTION': 39,
56 'PAUSE_INSTRUCTION': 40,
57 'MCE_DURING_VMENTRY': 41,
58 'TPR_BELOW_THRESHOLD': 43,
99 'CR0_SEL_WRITE': 0x065,
123 'TASK_SWITCH': 0x07d,
124 'FERR_FREEZE': 0x07e,
143 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
144 AARCH64_EXIT_REASONS
= {
182 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
183 USERSPACE_EXIT_REASONS
= {
191 'IRQ_WINDOW_OPEN': 7,
201 'INTERNAL_ERROR': 17,
212 'vmx': VMX_EXIT_REASONS
,
213 'svm': SVM_EXIT_REASONS
,
217 'SET_FILTER': 0x40082406,
218 'ENABLE': 0x00002400,
219 'DISABLE': 0x00002401,
224 """Class that encapsulates global architecture specific data like
225 syscall and ioctl numbers.
230 machine
= os
.uname()[4]
232 if machine
.startswith('ppc'):
234 elif machine
.startswith('aarch64'):
236 elif machine
.startswith('s390'):
240 for line
in open('/proc/cpuinfo'):
241 if not line
.startswith('flags'):
246 return ArchX86(VMX_EXIT_REASONS
)
248 return ArchX86(SVM_EXIT_REASONS
)
252 def __init__(self
, exit_reasons
):
253 self
.sc_perf_evt_open
= 298
254 self
.ioctl_numbers
= IOCTL_NUMBERS
255 self
.exit_reasons
= exit_reasons
259 self
.sc_perf_evt_open
= 319
260 self
.ioctl_numbers
= IOCTL_NUMBERS
261 self
.ioctl_numbers
['ENABLE'] = 0x20002400
262 self
.ioctl_numbers
['DISABLE'] = 0x20002401
264 # PPC comes in 32 and 64 bit and some generated ioctl
265 # numbers depend on the wordsize.
266 char_ptr_size
= ctypes
.sizeof(ctypes
.c_char_p
)
267 self
.ioctl_numbers
['SET_FILTER'] = 0x80002406 | char_ptr_size
<< 16
271 self
.sc_perf_evt_open
= 241
272 self
.ioctl_numbers
= IOCTL_NUMBERS
273 self
.exit_reasons
= AARCH64_EXIT_REASONS
275 class ArchS390(Arch
):
277 self
.sc_perf_evt_open
= 331
278 self
.ioctl_numbers
= IOCTL_NUMBERS
279 self
.exit_reasons
= None
281 ARCH
= Arch
.get_arch()
285 """Returns os.walk() data for specified directory.
287 As it is only a wrapper it returns the same 3-tuple of (dirpath,
288 dirnames, filenames).
290 return next(os
.walk(path
))
293 def parse_int_list(list_string
):
294 """Returns an int list from a string of comma separated integers and
297 members
= list_string
.split(',')
299 for member
in members
:
300 if '-' not in member
:
301 integers
.append(int(member
))
303 int_range
= member
.split('-')
304 integers
.extend(range(int(int_range
[0]),
305 int(int_range
[1]) + 1))
310 def get_online_cpus():
311 with
open('/sys/devices/system/cpu/online') as cpu_list
:
312 cpu_string
= cpu_list
.readline()
313 return parse_int_list(cpu_string
)
318 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
319 if ARCH
.exit_reasons
:
320 filters
['kvm_exit'] = ('exit_reason', ARCH
.exit_reasons
)
323 libc
= ctypes
.CDLL('libc.so.6', use_errno
=True)
324 syscall
= libc
.syscall
326 class perf_event_attr(ctypes
.Structure
):
327 _fields_
= [('type', ctypes
.c_uint32
),
328 ('size', ctypes
.c_uint32
),
329 ('config', ctypes
.c_uint64
),
330 ('sample_freq', ctypes
.c_uint64
),
331 ('sample_type', ctypes
.c_uint64
),
332 ('read_format', ctypes
.c_uint64
),
333 ('flags', ctypes
.c_uint64
),
334 ('wakeup_events', ctypes
.c_uint32
),
335 ('bp_type', ctypes
.c_uint32
),
336 ('bp_addr', ctypes
.c_uint64
),
337 ('bp_len', ctypes
.c_uint64
),
339 def perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
340 return syscall(ARCH
.sc_perf_evt_open
, ctypes
.pointer(attr
),
341 ctypes
.c_int(pid
), ctypes
.c_int(cpu
),
342 ctypes
.c_int(group_fd
), ctypes
.c_long(flags
))
344 PERF_TYPE_TRACEPOINT
= 2
345 PERF_FORMAT_GROUP
= 1 << 3
347 PATH_DEBUGFS_TRACING
= '/sys/kernel/debug/tracing'
348 PATH_DEBUGFS_KVM
= '/sys/kernel/debug/kvm'
354 def add_event(self
, event
):
355 self
.events
.append(event
)
358 length
= 8 * (1 + len(self
.events
))
359 read_format
= 'xxxxxxxx' + 'q' * len(self
.events
)
360 return dict(zip([event
.name
for event
in self
.events
],
361 struct
.unpack(read_format
,
362 os
.read(self
.events
[0].fd
, length
))))
365 def __init__(self
, name
, group
, trace_cpu
, trace_point
, trace_filter
,
369 self
.setup_event(group
, trace_cpu
, trace_point
, trace_filter
,
372 def setup_event_attribute(self
, trace_set
, trace_point
):
373 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', trace_set
,
376 event_attr
= perf_event_attr()
377 event_attr
.type = PERF_TYPE_TRACEPOINT
378 event_attr
.size
= ctypes
.sizeof(event_attr
)
379 event_attr
.config
= int(open(id_path
).read())
380 event_attr
.sample_period
= 1
381 event_attr
.read_format
= PERF_FORMAT_GROUP
384 def setup_event(self
, group
, trace_cpu
, trace_point
, trace_filter
,
386 event_attr
= self
.setup_event_attribute(trace_set
, trace_point
)
390 group_leader
= group
.events
[0].fd
392 fd
= perf_event_open(event_attr
, -1, trace_cpu
,
395 err
= ctypes
.get_errno()
396 raise OSError(err
, os
.strerror(err
),
397 'while calling sys_perf_event_open().')
400 fcntl
.ioctl(fd
, ARCH
.ioctl_numbers
['SET_FILTER'],
406 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['ENABLE'], 0)
409 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['DISABLE'], 0)
412 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['RESET'], 0)
414 class TracepointProvider(object):
416 self
.group_leaders
= []
417 self
.filters
= get_filters()
418 self
._fields
= self
.get_available_fields()
420 self
.fields
= self
._fields
422 def get_available_fields(self
):
423 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
424 fields
= walkdir(path
)[1]
427 if field
in self
.filters
:
428 filter_name_
, filter_dicts
= self
.filters
[field
]
429 for name
in filter_dicts
:
430 extra
.append(field
+ '(' + name
+ ')')
434 def setup_traces(self
):
435 cpus
= get_online_cpus()
437 # The constant is needed as a buffer for python libs, std
438 # streams and other files that the script opens.
439 rlimit
= len(cpus
) * len(self
._fields
) + 50
441 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (rlimit
, rlimit
))
443 sys
.exit("NOFILE rlimit could not be raised to {0}".format(rlimit
))
447 for name
in self
._fields
:
450 match
= re
.match(r
'(.*)\((.*)\)', name
)
452 tracepoint
, sub
= match
.groups()
453 tracefilter
= ('%s==%d\0' %
454 (self
.filters
[tracepoint
][0],
455 self
.filters
[tracepoint
][1][sub
]))
457 group
.add_event(Event(name
=name
,
460 trace_point
=tracepoint
,
461 trace_filter
=tracefilter
))
462 self
.group_leaders
.append(group
)
469 def fields(self
, fields
):
470 self
._fields
= fields
471 for group
in self
.group_leaders
:
472 for event
in group
.events
:
473 if event
.name
in fields
:
480 ret
= defaultdict(int)
481 for group
in self
.group_leaders
:
482 for name
, val
in group
.read().iteritems():
486 class DebugfsProvider(object):
488 self
._fields
= walkdir(PATH_DEBUGFS_KVM
)[2]
495 def fields(self
, fields
):
496 self
._fields
= fields
500 return int(file(PATH_DEBUGFS_KVM
+ '/' + key
).read())
501 return dict([(key
, val(key
)) for key
in self
._fields
])
504 def __init__(self
, providers
, fields
=None):
505 self
.providers
= providers
506 self
._fields
_filter
= fields
508 self
.update_provider_filters()
510 def update_provider_filters(self
):
512 if not self
._fields
_filter
:
514 return re
.match(self
._fields
_filter
, key
) is not None
516 # As we reset the counters when updating the fields we can
517 # also clear the cache of old values.
519 for provider
in self
.providers
:
520 provider_fields
= [key
for key
in provider
.fields
if wanted(key
)]
521 provider
.fields
= provider_fields
524 def fields_filter(self
):
525 return self
._fields
_filter
527 @fields_filter.setter
528 def fields_filter(self
, fields_filter
):
529 self
._fields
_filter
= fields_filter
530 self
.update_provider_filters()
533 for provider
in self
.providers
:
534 new
= provider
.read()
535 for key
in provider
.fields
:
536 oldval
= self
.values
.get(key
, (0, 0))
537 newval
= new
.get(key
, 0)
539 if oldval
is not None:
540 newdelta
= newval
- oldval
[0]
541 self
.values
[key
] = (newval
, newdelta
)
547 def tui(screen
, stats
):
548 curses
.use_default_colors()
551 fields_filter
= stats
.fields_filter
552 def update_drilldown():
553 if not fields_filter
:
555 stats
.fields_filter
= None
557 stats
.fields_filter
= r
'^[^\(]*$'
559 def refresh(sleeptime
):
561 screen
.addstr(0, 0, 'kvm statistics')
562 screen
.addstr(2, 1, 'Event')
563 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
- len('Total'), 'Total')
564 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 8 - len('Current'), 'Current')
569 return (-s
[x
][1], -s
[x
][0])
572 for key
in sorted(s
.keys(), key
=sortkey
):
573 if row
>= screen
.getmaxyx()[0]:
576 if not values
[0] and not values
[1]:
579 screen
.addstr(row
, col
, key
)
581 screen
.addstr(row
, col
, '%10d' % (values
[0],))
583 if values
[1] is not None:
584 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
591 curses
.halfdelay(int(sleeptime
* 10))
596 drilldown
= not drilldown
600 except KeyboardInterrupt:
609 for key
in sorted(s
.keys()):
611 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
614 keys
= sorted(stats
.get().iterkeys())
617 print '%10s' % k
[0:9],
622 print ' %9d' % s
[k
][1],
628 if line
% banner_repeat
== 0:
634 optparser
= optparse
.OptionParser()
635 optparser
.add_option('-1', '--once', '--batch',
639 help='run in batch mode for one second',
641 optparser
.add_option('-l', '--log',
645 help='run in logging mode (like vmstat)',
647 optparser
.add_option('-t', '--tracepoints',
651 help='retrieve statistics from tracepoints',
653 optparser
.add_option('-d', '--debugfs',
657 help='retrieve statistics from debugfs',
659 optparser
.add_option('-f', '--fields',
663 help='fields to display (regex)',
665 (options
, _
) = optparser
.parse_args(sys
.argv
)
668 def get_providers(options
):
671 if options
.tracepoints
:
672 providers
.append(TracepointProvider())
674 providers
.append(DebugfsProvider())
675 if len(providers
) == 0:
676 providers
.append(TracepointProvider())
681 if not os
.path
.exists('/sys/kernel/debug'):
682 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
685 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
686 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
687 "readable by the current user:\n"
688 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
689 "Also ensure, that the kvm modules are loaded.\n")
692 if not os
.path
.exists(PATH_DEBUGFS_TRACING
):
693 sys
.stderr
.write("Please make {0} readable by the current user.\n"
694 .format(PATH_DEBUGFS_TRACING
))
699 options
= get_options()
700 providers
= get_providers(options
)
701 stats
= Stats(providers
, fields
=options
.fields
)
705 elif not options
.once
:
706 curses
.wrapper(tui
, stats
)
710 if __name__
== "__main__":