diff options
Diffstat (limited to 'qemu/scripts/kvm/kvm_stat')
-rwxr-xr-x | qemu/scripts/kvm/kvm_stat | 825 |
1 files changed, 0 insertions, 825 deletions
diff --git a/qemu/scripts/kvm/kvm_stat b/qemu/scripts/kvm/kvm_stat deleted file mode 100755 index 769d884b6..000000000 --- a/qemu/scripts/kvm/kvm_stat +++ /dev/null @@ -1,825 +0,0 @@ -#!/usr/bin/python -# -# top-like utility for displaying kvm statistics -# -# Copyright 2006-2008 Qumranet Technologies -# Copyright 2008-2011 Red Hat, Inc. -# -# Authors: -# Avi Kivity <avi@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. - -import curses -import sys -import os -import time -import optparse -import ctypes -import fcntl -import resource -import struct -import re -from collections import defaultdict -from time import sleep - -VMX_EXIT_REASONS = { - 'EXCEPTION_NMI': 0, - 'EXTERNAL_INTERRUPT': 1, - 'TRIPLE_FAULT': 2, - 'PENDING_INTERRUPT': 7, - 'NMI_WINDOW': 8, - 'TASK_SWITCH': 9, - 'CPUID': 10, - 'HLT': 12, - 'INVLPG': 14, - 'RDPMC': 15, - 'RDTSC': 16, - 'VMCALL': 18, - 'VMCLEAR': 19, - 'VMLAUNCH': 20, - 'VMPTRLD': 21, - 'VMPTRST': 22, - 'VMREAD': 23, - 'VMRESUME': 24, - 'VMWRITE': 25, - 'VMOFF': 26, - 'VMON': 27, - 'CR_ACCESS': 28, - 'DR_ACCESS': 29, - 'IO_INSTRUCTION': 30, - 'MSR_READ': 31, - 'MSR_WRITE': 32, - 'INVALID_STATE': 33, - 'MWAIT_INSTRUCTION': 36, - 'MONITOR_INSTRUCTION': 39, - 'PAUSE_INSTRUCTION': 40, - 'MCE_DURING_VMENTRY': 41, - 'TPR_BELOW_THRESHOLD': 43, - 'APIC_ACCESS': 44, - 'EPT_VIOLATION': 48, - 'EPT_MISCONFIG': 49, - 'WBINVD': 54, - 'XSETBV': 55, - 'APIC_WRITE': 56, - 'INVPCID': 58, -} - -SVM_EXIT_REASONS = { - 'READ_CR0': 0x000, - 'READ_CR3': 0x003, - 'READ_CR4': 0x004, - 'READ_CR8': 0x008, - 'WRITE_CR0': 0x010, - 'WRITE_CR3': 0x013, - 'WRITE_CR4': 0x014, - 'WRITE_CR8': 0x018, - 'READ_DR0': 0x020, - 'READ_DR1': 0x021, - 'READ_DR2': 0x022, - 'READ_DR3': 0x023, - 'READ_DR4': 0x024, - 'READ_DR5': 0x025, - 'READ_DR6': 0x026, - 'READ_DR7': 0x027, - 'WRITE_DR0': 0x030, - 'WRITE_DR1': 0x031, - 'WRITE_DR2': 0x032, - 'WRITE_DR3': 0x033, - 'WRITE_DR4': 0x034, - 'WRITE_DR5': 0x035, - 'WRITE_DR6': 0x036, - 'WRITE_DR7': 0x037, - 'EXCP_BASE': 0x040, - 'INTR': 0x060, - 'NMI': 0x061, - 'SMI': 0x062, - 'INIT': 0x063, - 'VINTR': 0x064, - 'CR0_SEL_WRITE': 0x065, - 'IDTR_READ': 0x066, - 'GDTR_READ': 0x067, - 'LDTR_READ': 0x068, - 'TR_READ': 0x069, - 'IDTR_WRITE': 0x06a, - 'GDTR_WRITE': 0x06b, - 'LDTR_WRITE': 0x06c, - 'TR_WRITE': 0x06d, - 'RDTSC': 0x06e, - 'RDPMC': 0x06f, - 'PUSHF': 0x070, - 'POPF': 0x071, - 'CPUID': 0x072, - 'RSM': 0x073, - 'IRET': 0x074, - 'SWINT': 0x075, - 'INVD': 0x076, - 'PAUSE': 0x077, - 'HLT': 0x078, - 'INVLPG': 0x079, - 'INVLPGA': 0x07a, - 'IOIO': 0x07b, - 'MSR': 0x07c, - 'TASK_SWITCH': 0x07d, - 'FERR_FREEZE': 0x07e, - 'SHUTDOWN': 0x07f, - 'VMRUN': 0x080, - 'VMMCALL': 0x081, - 'VMLOAD': 0x082, - 'VMSAVE': 0x083, - 'STGI': 0x084, - 'CLGI': 0x085, - 'SKINIT': 0x086, - 'RDTSCP': 0x087, - 'ICEBP': 0x088, - 'WBINVD': 0x089, - 'MONITOR': 0x08a, - 'MWAIT': 0x08b, - 'MWAIT_COND': 0x08c, - 'XSETBV': 0x08d, - 'NPF': 0x400, -} - -# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h) -AARCH64_EXIT_REASONS = { - 'UNKNOWN': 0x00, - 'WFI': 0x01, - 'CP15_32': 0x03, - 'CP15_64': 0x04, - 'CP14_MR': 0x05, - 'CP14_LS': 0x06, - 'FP_ASIMD': 0x07, - 'CP10_ID': 0x08, - 'CP14_64': 0x0C, - 'ILL_ISS': 0x0E, - 'SVC32': 0x11, - 'HVC32': 0x12, - 'SMC32': 0x13, - 'SVC64': 0x15, - 'HVC64': 0x16, - 'SMC64': 0x17, - 'SYS64': 0x18, - 'IABT': 0x20, - 'IABT_HYP': 0x21, - 'PC_ALIGN': 0x22, - 'DABT': 0x24, - 'DABT_HYP': 0x25, - 'SP_ALIGN': 0x26, - 'FP_EXC32': 0x28, - 'FP_EXC64': 0x2C, - 'SERROR': 0x2F, - 'BREAKPT': 0x30, - 'BREAKPT_HYP': 0x31, - 'SOFTSTP': 0x32, - 'SOFTSTP_HYP': 0x33, - 'WATCHPT': 0x34, - 'WATCHPT_HYP': 0x35, - 'BKPT32': 0x38, - 'VECTOR32': 0x3A, - 'BRK64': 0x3C, -} - -# From include/uapi/linux/kvm.h, KVM_EXIT_xxx -USERSPACE_EXIT_REASONS = { - 'UNKNOWN': 0, - 'EXCEPTION': 1, - 'IO': 2, - 'HYPERCALL': 3, - 'DEBUG': 4, - 'HLT': 5, - 'MMIO': 6, - 'IRQ_WINDOW_OPEN': 7, - 'SHUTDOWN': 8, - 'FAIL_ENTRY': 9, - 'INTR': 10, - 'SET_TPR': 11, - 'TPR_ACCESS': 12, - 'S390_SIEIC': 13, - 'S390_RESET': 14, - 'DCR': 15, - 'NMI': 16, - 'INTERNAL_ERROR': 17, - 'OSI': 18, - 'PAPR_HCALL': 19, - 'S390_UCONTROL': 20, - 'WATCHDOG': 21, - 'S390_TSCH': 22, - 'EPR': 23, - 'SYSTEM_EVENT': 24, -} - -IOCTL_NUMBERS = { - 'SET_FILTER': 0x40082406, - 'ENABLE': 0x00002400, - 'DISABLE': 0x00002401, - 'RESET': 0x00002403, -} - -class Arch(object): - """Class that encapsulates global architecture specific data like - syscall and ioctl numbers. - - """ - @staticmethod - def get_arch(): - machine = os.uname()[4] - - if machine.startswith('ppc'): - return ArchPPC() - elif machine.startswith('aarch64'): - return ArchA64() - elif machine.startswith('s390'): - return ArchS390() - else: - # X86_64 - for line in open('/proc/cpuinfo'): - if not line.startswith('flags'): - continue - - flags = line.split() - if 'vmx' in flags: - return ArchX86(VMX_EXIT_REASONS) - if 'svm' in flags: - return ArchX86(SVM_EXIT_REASONS) - return - -class ArchX86(Arch): - def __init__(self, exit_reasons): - self.sc_perf_evt_open = 298 - self.ioctl_numbers = IOCTL_NUMBERS - self.exit_reasons = exit_reasons - -class ArchPPC(Arch): - def __init__(self): - self.sc_perf_evt_open = 319 - self.ioctl_numbers = IOCTL_NUMBERS - self.ioctl_numbers['ENABLE'] = 0x20002400 - self.ioctl_numbers['DISABLE'] = 0x20002401 - - # PPC comes in 32 and 64 bit and some generated ioctl - # numbers depend on the wordsize. - char_ptr_size = ctypes.sizeof(ctypes.c_char_p) - self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 - -class ArchA64(Arch): - def __init__(self): - self.sc_perf_evt_open = 241 - self.ioctl_numbers = IOCTL_NUMBERS - self.exit_reasons = AARCH64_EXIT_REASONS - -class ArchS390(Arch): - def __init__(self): - self.sc_perf_evt_open = 331 - self.ioctl_numbers = IOCTL_NUMBERS - self.exit_reasons = None - -ARCH = Arch.get_arch() - - -def walkdir(path): - """Returns os.walk() data for specified directory. - - As it is only a wrapper it returns the same 3-tuple of (dirpath, - dirnames, filenames). - """ - return next(os.walk(path)) - - -def parse_int_list(list_string): - """Returns an int list from a string of comma separated integers and - integer ranges.""" - integers = [] - members = list_string.split(',') - - for member in members: - if '-' not in member: - integers.append(int(member)) - else: - int_range = member.split('-') - integers.extend(range(int(int_range[0]), - int(int_range[1]) + 1)) - - return integers - - -def get_online_cpus(): - with open('/sys/devices/system/cpu/online') as cpu_list: - cpu_string = cpu_list.readline() - return parse_int_list(cpu_string) - - -def get_filters(): - filters = {} - filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) - if ARCH.exit_reasons: - filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) - return filters - -libc = ctypes.CDLL('libc.so.6', use_errno=True) -syscall = libc.syscall - -class perf_event_attr(ctypes.Structure): - _fields_ = [('type', ctypes.c_uint32), - ('size', ctypes.c_uint32), - ('config', ctypes.c_uint64), - ('sample_freq', ctypes.c_uint64), - ('sample_type', ctypes.c_uint64), - ('read_format', ctypes.c_uint64), - ('flags', ctypes.c_uint64), - ('wakeup_events', ctypes.c_uint32), - ('bp_type', ctypes.c_uint32), - ('bp_addr', ctypes.c_uint64), - ('bp_len', ctypes.c_uint64), - ] - - def __init__(self): - super(self.__class__, self).__init__() - self.type = PERF_TYPE_TRACEPOINT - self.size = ctypes.sizeof(self) - self.read_format = PERF_FORMAT_GROUP - -def perf_event_open(attr, pid, cpu, group_fd, flags): - return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), - ctypes.c_int(pid), ctypes.c_int(cpu), - ctypes.c_int(group_fd), ctypes.c_long(flags)) - -PERF_TYPE_TRACEPOINT = 2 -PERF_FORMAT_GROUP = 1 << 3 - -PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing' -PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm' - -class Group(object): - def __init__(self): - self.events = [] - - def add_event(self, event): - self.events.append(event) - - def read(self): - length = 8 * (1 + len(self.events)) - read_format = 'xxxxxxxx' + 'Q' * len(self.events) - return dict(zip([event.name for event in self.events], - struct.unpack(read_format, - os.read(self.events[0].fd, length)))) - -class Event(object): - def __init__(self, name, group, trace_cpu, trace_point, trace_filter, - trace_set='kvm'): - self.name = name - self.fd = None - self.setup_event(group, trace_cpu, trace_point, trace_filter, - trace_set) - - def setup_event_attribute(self, trace_set, trace_point): - id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, - trace_point, 'id') - - event_attr = perf_event_attr() - event_attr.config = int(open(id_path).read()) - return event_attr - - def setup_event(self, group, trace_cpu, trace_point, trace_filter, - trace_set): - event_attr = self.setup_event_attribute(trace_set, trace_point) - - group_leader = -1 - if group.events: - group_leader = group.events[0].fd - - fd = perf_event_open(event_attr, -1, trace_cpu, - group_leader, 0) - if fd == -1: - err = ctypes.get_errno() - raise OSError(err, os.strerror(err), - 'while calling sys_perf_event_open().') - - if trace_filter: - fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'], - trace_filter) - - self.fd = fd - - def enable(self): - fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0) - - def disable(self): - fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0) - - def reset(self): - fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0) - -class TracepointProvider(object): - def __init__(self): - self.group_leaders = [] - self.filters = get_filters() - self._fields = self.get_available_fields() - self.setup_traces() - self.fields = self._fields - - def get_available_fields(self): - path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm') - fields = walkdir(path)[1] - extra = [] - for field in fields: - if field in self.filters: - filter_name_, filter_dicts = self.filters[field] - for name in filter_dicts: - extra.append(field + '(' + name + ')') - fields += extra - return fields - - def setup_traces(self): - cpus = get_online_cpus() - - # The constant is needed as a buffer for python libs, std - # streams and other files that the script opens. - newlim = len(cpus) * len(self._fields) + 50 - try: - softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE) - - if hardlim < newlim: - # Now we need CAP_SYS_RESOURCE, to increase the hard limit. - resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim)) - else: - # Raising the soft limit is sufficient. - resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim)) - - except ValueError: - sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim)) - - for cpu in cpus: - group = Group() - for name in self._fields: - tracepoint = name - tracefilter = None - match = re.match(r'(.*)\((.*)\)', name) - if match: - tracepoint, sub = match.groups() - tracefilter = ('%s==%d\0' % - (self.filters[tracepoint][0], - self.filters[tracepoint][1][sub])) - - group.add_event(Event(name=name, - group=group, - trace_cpu=cpu, - trace_point=tracepoint, - trace_filter=tracefilter)) - self.group_leaders.append(group) - - def available_fields(self): - return self.get_available_fields() - - @property - def fields(self): - return self._fields - - @fields.setter - def fields(self, fields): - self._fields = fields - for group in self.group_leaders: - for index, event in enumerate(group.events): - if event.name in fields: - event.reset() - event.enable() - else: - # Do not disable the group leader. - # It would disable all of its events. - if index != 0: - event.disable() - - def read(self): - ret = defaultdict(int) - for group in self.group_leaders: - for name, val in group.read().iteritems(): - if name in self._fields: - ret[name] += val - return ret - -class DebugfsProvider(object): - def __init__(self): - self._fields = self.get_available_fields() - - def get_available_fields(self): - return walkdir(PATH_DEBUGFS_KVM)[2] - - @property - def fields(self): - return self._fields - - @fields.setter - def fields(self, fields): - self._fields = fields - - def read(self): - def val(key): - return int(file(PATH_DEBUGFS_KVM + '/' + key).read()) - return dict([(key, val(key)) for key in self._fields]) - -class Stats(object): - def __init__(self, providers, fields=None): - self.providers = providers - self._fields_filter = fields - self.values = {} - self.update_provider_filters() - - def update_provider_filters(self): - def wanted(key): - if not self._fields_filter: - return True - return re.match(self._fields_filter, key) is not None - - # As we reset the counters when updating the fields we can - # also clear the cache of old values. - self.values = {} - for provider in self.providers: - provider_fields = [key for key in provider.get_available_fields() - if wanted(key)] - provider.fields = provider_fields - - @property - def fields_filter(self): - return self._fields_filter - - @fields_filter.setter - def fields_filter(self, fields_filter): - self._fields_filter = fields_filter - self.update_provider_filters() - - def get(self): - for provider in self.providers: - new = provider.read() - for key in provider.fields: - oldval = self.values.get(key, (0, 0)) - newval = new.get(key, 0) - newdelta = None - if oldval is not None: - newdelta = newval - oldval[0] - self.values[key] = (newval, newdelta) - return self.values - -LABEL_WIDTH = 40 -NUMBER_WIDTH = 10 - -class Tui(object): - def __init__(self, stats): - self.stats = stats - self.screen = None - self.drilldown = False - self.update_drilldown() - - def __enter__(self): - """Initialises curses for later use. Based on curses.wrapper - implementation from the Python standard library.""" - self.screen = curses.initscr() - curses.noecho() - curses.cbreak() - - # The try/catch works around a minor bit of - # over-conscientiousness in the curses module, the error - # return from C start_color() is ignorable. - try: - curses.start_color() - except: - pass - - curses.use_default_colors() - return self - - def __exit__(self, *exception): - """Resets the terminal to its normal state. Based on curses.wrappre - implementation from the Python standard library.""" - if self.screen: - self.screen.keypad(0) - curses.echo() - curses.nocbreak() - curses.endwin() - - def update_drilldown(self): - if not self.stats.fields_filter: - self.stats.fields_filter = r'^[^\(]*$' - - elif self.stats.fields_filter == r'^[^\(]*$': - self.stats.fields_filter = None - - def refresh(self, sleeptime): - self.screen.erase() - self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD) - self.screen.addstr(2, 1, 'Event') - self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - - len('Total'), 'Total') - self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - - len('Current'), 'Current') - row = 3 - stats = self.stats.get() - def sortkey(x): - if stats[x][1]: - return (-stats[x][1], -stats[x][0]) - else: - return (0, -stats[x][0]) - for key in sorted(stats.keys(), key=sortkey): - - if row >= self.screen.getmaxyx()[0]: - break - values = stats[key] - if not values[0] and not values[1]: - break - col = 1 - self.screen.addstr(row, col, key) - col += LABEL_WIDTH - self.screen.addstr(row, col, '%10d' % (values[0],)) - col += NUMBER_WIDTH - if values[1] is not None: - self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) - row += 1 - self.screen.refresh() - - def show_filter_selection(self): - while True: - self.screen.erase() - self.screen.addstr(0, 0, - "Show statistics for events matching a regex.", - curses.A_BOLD) - self.screen.addstr(2, 0, - "Current regex: {0}" - .format(self.stats.fields_filter)) - self.screen.addstr(3, 0, "New regex: ") - curses.echo() - regex = self.screen.getstr() - curses.noecho() - if len(regex) == 0: - return - try: - re.compile(regex) - self.stats.fields_filter = regex - return - except re.error: - continue - - def show_stats(self): - sleeptime = 0.25 - while True: - self.refresh(sleeptime) - curses.halfdelay(int(sleeptime * 10)) - sleeptime = 3 - try: - char = self.screen.getkey() - if char == 'x': - self.drilldown = not self.drilldown - self.update_drilldown() - if char == 'q': - break - if char == 'f': - self.show_filter_selection() - except KeyboardInterrupt: - break - except curses.error: - continue - -def batch(stats): - s = stats.get() - time.sleep(1) - s = stats.get() - for key in sorted(s.keys()): - values = s[key] - print '%-42s%10d%10d' % (key, values[0], values[1]) - -def log(stats): - keys = sorted(stats.get().iterkeys()) - def banner(): - for k in keys: - print '%s' % k, - print - def statline(): - s = stats.get() - for k in keys: - print ' %9d' % s[k][1], - print - line = 0 - banner_repeat = 20 - while True: - time.sleep(1) - if line % banner_repeat == 0: - banner() - statline() - line += 1 - -def get_options(): - description_text = """ -This script displays various statistics about VMs running under KVM. -The statistics are gathered from the KVM debugfs entries and / or the -currently available perf traces. - -The monitoring takes additional cpu cycles and might affect the VM's -performance. - -Requirements: -- Access to: - /sys/kernel/debug/kvm - /sys/kernel/debug/trace/events/* - /proc/pid/task -- /proc/sys/kernel/perf_event_paranoid < 1 if user has no - CAP_SYS_ADMIN and perf events are used. -- CAP_SYS_RESOURCE if the hard limit is not high enough to allow - the large number of files that are possibly opened. -""" - - class PlainHelpFormatter(optparse.IndentedHelpFormatter): - def format_description(self, description): - if description: - return description + "\n" - else: - return "" - - optparser = optparse.OptionParser(description=description_text, - formatter=PlainHelpFormatter()) - optparser.add_option('-1', '--once', '--batch', - action='store_true', - default=False, - dest='once', - help='run in batch mode for one second', - ) - optparser.add_option('-l', '--log', - action='store_true', - default=False, - dest='log', - help='run in logging mode (like vmstat)', - ) - optparser.add_option('-t', '--tracepoints', - action='store_true', - default=False, - dest='tracepoints', - help='retrieve statistics from tracepoints', - ) - optparser.add_option('-d', '--debugfs', - action='store_true', - default=False, - dest='debugfs', - help='retrieve statistics from debugfs', - ) - optparser.add_option('-f', '--fields', - action='store', - default=None, - dest='fields', - help='fields to display (regex)', - ) - (options, _) = optparser.parse_args(sys.argv) - return options - -def get_providers(options): - providers = [] - - if options.tracepoints: - providers.append(TracepointProvider()) - if options.debugfs: - providers.append(DebugfsProvider()) - if len(providers) == 0: - providers.append(TracepointProvider()) - - return providers - -def check_access(options): - if not os.path.exists('/sys/kernel/debug'): - sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.') - sys.exit(1) - - if not os.path.exists(PATH_DEBUGFS_KVM): - sys.stderr.write("Please make sure, that debugfs is mounted and " - "readable by the current user:\n" - "('mount -t debugfs debugfs /sys/kernel/debug')\n" - "Also ensure, that the kvm modules are loaded.\n") - sys.exit(1) - - if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints - or not options.debugfs): - sys.stderr.write("Please enable CONFIG_TRACING in your kernel " - "when using the option -t (default).\n" - "If it is enabled, make {0} readable by the " - "current user.\n" - .format(PATH_DEBUGFS_TRACING)) - if options.tracepoints: - sys.exit(1) - - sys.stderr.write("Falling back to debugfs statistics!\n") - options.debugfs = True - sleep(5) - - return options - -def main(): - options = get_options() - options = check_access(options) - providers = get_providers(options) - stats = Stats(providers, fields=options.fields) - - if options.log: - log(stats) - elif not options.once: - with Tui(stats) as tui: - tui.show_stats() - else: - batch(stats) - -if __name__ == "__main__": - main() |