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.
24 from collections import defaultdict
26 class DebugfsProvider(object):
28 self.base = '/sys/kernel/debug/kvm'
29 self._fields = walkdir(self.base)[2]
32 def select(self, fields):
36 return int(file(self.base + '/' + key).read())
37 return dict([(key, val(key)) for key in self._fields])
41 'EXTERNAL_INTERRUPT': 1,
43 'PENDING_INTERRUPT': 7,
67 'MWAIT_INSTRUCTION': 36,
68 'MONITOR_INSTRUCTION': 39,
69 'PAUSE_INSTRUCTION': 40,
70 'MCE_DURING_VMENTRY': 41,
71 'TPR_BELOW_THRESHOLD': 43,
112 'CR0_SEL_WRITE': 0x065,
136 'TASK_SWITCH': 0x07d,
137 'FERR_FREEZE': 0x07e,
156 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
157 AARCH64_EXIT_REASONS = {
195 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
196 USERSPACE_EXIT_REASONS = {
204 'IRQ_WINDOW_OPEN': 7,
214 'INTERNAL_ERROR': 17,
225 'vmx': VMX_EXIT_REASONS,
226 'svm': SVM_EXIT_REASONS,
229 SC_PERF_EVT_OPEN = None
233 'SET_FILTER' : 0x40082406,
234 'ENABLE' : 0x00002400,
235 'DISABLE' : 0x00002401,
236 'RESET' : 0x00002403,
240 global SC_PERF_EVT_OPEN
243 SC_PERF_EVT_OPEN = 298
244 EXIT_REASONS = X86_EXIT_REASONS[flag]
247 global SC_PERF_EVT_OPEN
249 SC_PERF_EVT_OPEN = 331
252 global SC_PERF_EVT_OPEN
255 SC_PERF_EVT_OPEN = 319
257 IOCTL_NUMBERS['ENABLE'] = 0x20002400
258 IOCTL_NUMBERS['DISABLE'] = 0x20002401
259 IOCTL_NUMBERS['SET_FILTER'] = 0x80002406 | (ctypes.sizeof(ctypes.c_char_p)
263 global SC_PERF_EVT_OPEN
266 SC_PERF_EVT_OPEN = 241
267 EXIT_REASONS = AARCH64_EXIT_REASONS
269 def detect_platform():
270 if os.uname()[4].startswith('ppc'):
273 elif os.uname()[4].startswith('aarch64'):
277 for line in file('/proc/cpuinfo').readlines():
278 if line.startswith('flags'):
279 for flag in line.split():
280 if flag in X86_EXIT_REASONS:
283 elif line.startswith('vendor_id'):
284 for flag in line.split():
285 if flag == 'IBM/S390':
293 """Returns os.walk() data for specified directory.
295 As it is only a wrapper it returns the same 3-tuple of (dirpath,
296 dirnames, filenames).
298 return next(os.walk(path))
301 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
303 filters['kvm_exit'] = ('exit_reason', EXIT_REASONS)
305 libc = ctypes.CDLL('libc.so.6')
306 syscall = libc.syscall
307 get_errno = libc.__errno_location
308 get_errno.restype = ctypes.POINTER(ctypes.c_int)
310 class perf_event_attr(ctypes.Structure):
311 _fields_ = [('type', ctypes.c_uint32),
312 ('size', ctypes.c_uint32),
313 ('config', ctypes.c_uint64),
314 ('sample_freq', ctypes.c_uint64),
315 ('sample_type', ctypes.c_uint64),
316 ('read_format', ctypes.c_uint64),
317 ('flags', ctypes.c_uint64),
318 ('wakeup_events', ctypes.c_uint32),
319 ('bp_type', ctypes.c_uint32),
320 ('bp_addr', ctypes.c_uint64),
321 ('bp_len', ctypes.c_uint64),
323 def _perf_event_open(attr, pid, cpu, group_fd, flags):
324 return syscall(SC_PERF_EVT_OPEN, ctypes.pointer(attr), ctypes.c_int(pid),
325 ctypes.c_int(cpu), ctypes.c_int(group_fd),
326 ctypes.c_long(flags))
328 PERF_TYPE_TRACEPOINT = 2
329 PERF_FORMAT_GROUP = 1 << 3
331 sys_tracing = '/sys/kernel/debug/tracing'
334 def __init__(self, cpu):
336 self.group_leader = None
338 def add_event(self, name, event_set, tracepoint, filter = None):
339 self.events.append(Event(group = self,
340 name = name, event_set = event_set,
341 tracepoint = tracepoint, filter = filter))
342 if len(self.events) == 1:
343 self.file = os.fdopen(self.events[0].fd)
345 bytes = 8 * (1 + len(self.events))
346 fmt = 'xxxxxxxx' + 'q' * len(self.events)
347 return dict(zip([event.name for event in self.events],
348 struct.unpack(fmt, self.file.read(bytes))))
351 def __init__(self, group, name, event_set, tracepoint, filter = None):
353 attr = perf_event_attr()
354 attr.type = PERF_TYPE_TRACEPOINT
355 attr.size = ctypes.sizeof(attr)
356 id_path = os.path.join(sys_tracing, 'events', event_set,
358 id = int(file(id_path).read())
360 attr.sample_period = 1
361 attr.read_format = PERF_FORMAT_GROUP
364 group_leader = group.events[0].fd
365 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
368 raise Exception('perf_event_open failed, errno = ' + err.__str__())
370 fcntl.ioctl(fd, IOCTL_NUMBERS['SET_FILTER'], filter)
373 fcntl.ioctl(self.fd, IOCTL_NUMBERS['ENABLE'], 0)
375 fcntl.ioctl(self.fd, IOCTL_NUMBERS['DISABLE'], 0)
377 fcntl.ioctl(self.fd, IOCTL_NUMBERS['RESET'], 0)
379 class TracepointProvider(object):
381 path = os.path.join(sys_tracing, 'events', 'kvm')
382 fields = walkdir(path)[1]
386 subfield, values = filters[f]
387 for name, number in values.iteritems():
388 extra.append(f + '(' + name + ')')
395 def _online_cpus(self):
397 pattern = r'cpu([0-9]+)'
398 basedir = '/sys/devices/system/cpu'
399 for entry in os.listdir(basedir):
400 match = re.match(pattern, entry)
403 path = os.path.join(basedir, entry, 'online')
404 if os.path.exists(path) and open(path).read().strip() != '1':
406 l.append(int(match.group(1)))
409 def _setup(self, _fields):
410 self._fields = _fields
411 cpus = self._online_cpus()
412 nfiles = len(cpus) * 1000
413 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
415 self.group_leaders = []
421 m = re.match(r'(.*)\((.*)\)', name)
423 tracepoint, sub = m.groups()
424 filter = '%s==%d\0' % (filters[tracepoint][0],
425 filters[tracepoint][1][sub])
426 event = group.add_event(name, event_set = 'kvm',
427 tracepoint = tracepoint,
429 self.group_leaders.append(group)
430 def select(self, fields):
431 for group in self.group_leaders:
432 for event in group.events:
433 if event.name in fields:
439 ret = defaultdict(int)
440 for group in self.group_leaders:
441 for name, val in group.read().iteritems():
446 def __init__(self, providers, fields = None):
447 self.providers = providers
448 self.fields_filter = fields
452 if not self.fields_filter:
454 return re.match(self.fields_filter, key) is not None
457 provider_fields = [key for key in d.fields() if wanted(key)]
458 for key in provider_fields:
459 self.values[key] = None
460 d.select(provider_fields)
461 def set_fields_filter(self, fields_filter):
462 self.fields_filter = fields_filter
467 for key in d.fields():
468 oldval = self.values.get(key, (0, 0))
471 if oldval is not None:
472 newdelta = newval - oldval[0]
473 self.values[key] = (newval, newdelta)
476 if not os.access('/sys/kernel/debug', os.F_OK):
477 print 'Please enable CONFIG_DEBUG_FS in your kernel'
479 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
480 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
481 print "and ensure the kvm modules are loaded"
487 def tui(screen, stats):
488 curses.use_default_colors()
491 fields_filter = stats.fields_filter
492 def update_drilldown():
493 if not fields_filter:
495 stats.set_fields_filter(None)
497 stats.set_fields_filter(r'^[^\(]*$')
499 def refresh(sleeptime):
501 screen.addstr(0, 0, 'kvm statistics')
502 screen.addstr(2, 1, 'Event')
503 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
504 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
509 return (-s[x][1], -s[x][0])
512 for key in sorted(s.keys(), key = sortkey):
513 if row >= screen.getmaxyx()[0]:
516 if not values[0] and not values[1]:
519 screen.addstr(row, col, key)
521 screen.addstr(row, col, '%10d' % (values[0],))
523 if values[1] is not None:
524 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
531 curses.halfdelay(int(sleeptime * 10))
536 drilldown = not drilldown
540 except KeyboardInterrupt:
549 for key in sorted(s.keys()):
551 print '%-22s%10d%10d' % (key, values[0], values[1])
554 keys = sorted(stats.get().iterkeys())
557 print '%10s' % k[0:9],
562 print ' %9d' % s[k][1],
568 if line % banner_repeat == 0:
573 options = optparse.OptionParser()
574 options.add_option('-1', '--once', '--batch',
575 action = 'store_true',
578 help = 'run in batch mode for one second',
580 options.add_option('-l', '--log',
581 action = 'store_true',
584 help = 'run in logging mode (like vmstat)',
586 options.add_option('-t', '--tracepoints',
587 action = 'store_true',
589 dest = 'tracepoints',
590 help = 'retrieve statistics from tracepoints',
592 options.add_option('-d', '--debugfs',
593 action = 'store_true',
596 help = 'retrieve statistics from debugfs',
598 options.add_option('-f', '--fields',
602 help = 'fields to display (regex)',
604 (options, args) = options.parse_args(sys.argv)
607 if options.tracepoints:
608 providers.append(TracepointProvider())
610 providers.append(DebugfsProvider())
612 if len(providers) == 0:
614 providers = [TracepointProvider()]
616 providers = [DebugfsProvider()]
618 stats = Stats(providers, fields = options.fields)
622 elif not options.once:
623 curses.wrapper(tui, stats)