Re: [PATCH 1/5] tools: Add kvm_stat vm monitor script

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 06/04/2016 13:31, Janosch Frank wrote:
> This tool displays kvm vm exit statistics to ease vm monitoring. It
> takes its data from the kvm debugfs files or the vm tracepoints and
> outputs them as a curses ui or simple text.
> 
> It was moved from qemu, as it is dependent on the kernel whereas qemu
> works with a large number of kernel versions, some of which may break
> the script.
> 
> Signed-off-by: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx>

Hi,

you need to add support for this to tools/Makefile too.

Paolo

> ---
>  tools/kvm/kvm_stat | 825 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 825 insertions(+)
>  create mode 100755 tools/kvm/kvm_stat
> 
> diff --git a/tools/kvm/kvm_stat b/tools/kvm/kvm_stat
> new file mode 100755
> index 0000000..769d884
> --- /dev/null
> +++ b/tools/kvm/kvm_stat
> @@ -0,0 +1,825 @@
> +#!/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@xxxxxxxxxx>
> +#
> +# 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()
> 
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux