3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
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)