diff options
Diffstat (limited to 'qemu/scripts/kvm')
-rwxr-xr-x | qemu/scripts/kvm/kvm_flightrecorder | 126 | ||||
-rwxr-xr-x | qemu/scripts/kvm/kvm_stat | 646 | ||||
-rw-r--r-- | qemu/scripts/kvm/kvm_stat.texi | 55 | ||||
-rwxr-xr-x | qemu/scripts/kvm/vmxcap | 260 |
4 files changed, 1087 insertions, 0 deletions
diff --git a/qemu/scripts/kvm/kvm_flightrecorder b/qemu/scripts/kvm/kvm_flightrecorder new file mode 100755 index 000000000..7fb1c2d1a --- /dev/null +++ b/qemu/scripts/kvm/kvm_flightrecorder @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# KVM Flight Recorder - ring buffer tracing script +# +# Copyright (C) 2012 IBM Corp +# +# Author: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> +# +# This script provides a command-line interface to kvm ftrace and is designed +# to be used as a flight recorder that is always running. To start in-memory +# recording: +# +# sudo kvm_flightrecorder start 8192 # 8 MB per-cpu ring buffers +# +# The per-cpu ring buffer size can be given in KB as an optional argument to +# the 'start' subcommand. +# +# To stop the flight recorder: +# +# sudo kvm_flightrecorder stop +# +# To dump the contents of the flight recorder (this can be done when the +# recorder is stopped or while it is running): +# +# sudo kvm_flightrecorder dump >/path/to/dump.txt +# +# To observe the trace while it is running, use the 'tail' subcommand: +# +# sudo kvm_flightrecorder tail +# +# Note that the flight recorder may impact overall system performance by +# consuming CPU cycles. No disk I/O is performed since the ring buffer holds a +# fixed-size in-memory trace. + +import sys +import os + +tracing_dir = '/sys/kernel/debug/tracing' + +def trace_path(*args): + return os.path.join(tracing_dir, *args) + +def write_file(path, data): + open(path, 'wb').write(data) + +def enable_event(subsystem, event, enable): + write_file(trace_path('events', subsystem, event, 'enable'), '1' if enable else '0') + +def enable_subsystem(subsystem, enable): + write_file(trace_path('events', subsystem, 'enable'), '1' if enable else '0') + +def start_tracing(): + enable_subsystem('kvm', True) + write_file(trace_path('tracing_on'), '1') + +def stop_tracing(): + write_file(trace_path('tracing_on'), '0') + enable_subsystem('kvm', False) + write_file(trace_path('events', 'enable'), '0') + write_file(trace_path('current_tracer'), 'nop') + +def dump_trace(): + tracefile = open(trace_path('trace'), 'r') + try: + lines = True + while lines: + lines = tracefile.readlines(64 * 1024) + sys.stdout.writelines(lines) + except KeyboardInterrupt: + pass + +def tail_trace(): + try: + for line in open(trace_path('trace_pipe'), 'r'): + sys.stdout.write(line) + except KeyboardInterrupt: + pass + +def usage(): + print 'Usage: %s start [buffer_size_kb] | stop | dump | tail' % sys.argv[0] + print 'Control the KVM flight recorder tracing.' + sys.exit(0) + +def main(): + if len(sys.argv) < 2: + usage() + + cmd = sys.argv[1] + if cmd == '--version': + print 'kvm_flightrecorder version 1.0' + sys.exit(0) + + if not os.path.isdir(tracing_dir): + print 'Unable to tracing debugfs directory, try:' + print 'mount -t debugfs none /sys/kernel/debug' + sys.exit(1) + if not os.access(tracing_dir, os.W_OK): + print 'Unable to write to tracing debugfs directory, please run as root' + sys.exit(1) + + if cmd == 'start': + stop_tracing() # clean up first + + if len(sys.argv) == 3: + try: + buffer_size_kb = int(sys.argv[2]) + except ValueError: + print 'Invalid per-cpu trace buffer size in KB' + sys.exit(1) + write_file(trace_path('buffer_size_kb'), str(buffer_size_kb)) + print 'Per-CPU ring buffer size set to %d KB' % buffer_size_kb + + start_tracing() + print 'KVM flight recorder enabled' + elif cmd == 'stop': + stop_tracing() + print 'KVM flight recorder disabled' + elif cmd == 'dump': + dump_trace() + elif cmd == 'tail': + tail_trace() + else: + usage() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/qemu/scripts/kvm/kvm_stat b/qemu/scripts/kvm/kvm_stat new file mode 100755 index 000000000..7e5d25612 --- /dev/null +++ b/qemu/scripts/kvm/kvm_stat @@ -0,0 +1,646 @@ +#!/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, os, time, optparse, ctypes +from ctypes import * + +class DebugfsProvider(object): + def __init__(self): + self.base = '/sys/kernel/debug/kvm' + self._fields = os.listdir(self.base) + def fields(self): + return self._fields + def select(self, fields): + self._fields = fields + def read(self): + def val(key): + return int(file(self.base + '/' + key).read()) + return dict([(key, val(key)) for key in self._fields]) + +vmx_exit_reasons = { + 0: 'EXCEPTION_NMI', + 1: 'EXTERNAL_INTERRUPT', + 2: 'TRIPLE_FAULT', + 7: 'PENDING_INTERRUPT', + 8: 'NMI_WINDOW', + 9: 'TASK_SWITCH', + 10: 'CPUID', + 12: 'HLT', + 14: 'INVLPG', + 15: 'RDPMC', + 16: 'RDTSC', + 18: 'VMCALL', + 19: 'VMCLEAR', + 20: 'VMLAUNCH', + 21: 'VMPTRLD', + 22: 'VMPTRST', + 23: 'VMREAD', + 24: 'VMRESUME', + 25: 'VMWRITE', + 26: 'VMOFF', + 27: 'VMON', + 28: 'CR_ACCESS', + 29: 'DR_ACCESS', + 30: 'IO_INSTRUCTION', + 31: 'MSR_READ', + 32: 'MSR_WRITE', + 33: 'INVALID_STATE', + 36: 'MWAIT_INSTRUCTION', + 39: 'MONITOR_INSTRUCTION', + 40: 'PAUSE_INSTRUCTION', + 41: 'MCE_DURING_VMENTRY', + 43: 'TPR_BELOW_THRESHOLD', + 44: 'APIC_ACCESS', + 48: 'EPT_VIOLATION', + 49: 'EPT_MISCONFIG', + 54: 'WBINVD', + 55: 'XSETBV', + 56: 'APIC_WRITE', + 58: 'INVPCID', +} + +svm_exit_reasons = { + 0x000: 'READ_CR0', + 0x003: 'READ_CR3', + 0x004: 'READ_CR4', + 0x008: 'READ_CR8', + 0x010: 'WRITE_CR0', + 0x013: 'WRITE_CR3', + 0x014: 'WRITE_CR4', + 0x018: 'WRITE_CR8', + 0x020: 'READ_DR0', + 0x021: 'READ_DR1', + 0x022: 'READ_DR2', + 0x023: 'READ_DR3', + 0x024: 'READ_DR4', + 0x025: 'READ_DR5', + 0x026: 'READ_DR6', + 0x027: 'READ_DR7', + 0x030: 'WRITE_DR0', + 0x031: 'WRITE_DR1', + 0x032: 'WRITE_DR2', + 0x033: 'WRITE_DR3', + 0x034: 'WRITE_DR4', + 0x035: 'WRITE_DR5', + 0x036: 'WRITE_DR6', + 0x037: 'WRITE_DR7', + 0x040: 'EXCP_BASE', + 0x060: 'INTR', + 0x061: 'NMI', + 0x062: 'SMI', + 0x063: 'INIT', + 0x064: 'VINTR', + 0x065: 'CR0_SEL_WRITE', + 0x066: 'IDTR_READ', + 0x067: 'GDTR_READ', + 0x068: 'LDTR_READ', + 0x069: 'TR_READ', + 0x06a: 'IDTR_WRITE', + 0x06b: 'GDTR_WRITE', + 0x06c: 'LDTR_WRITE', + 0x06d: 'TR_WRITE', + 0x06e: 'RDTSC', + 0x06f: 'RDPMC', + 0x070: 'PUSHF', + 0x071: 'POPF', + 0x072: 'CPUID', + 0x073: 'RSM', + 0x074: 'IRET', + 0x075: 'SWINT', + 0x076: 'INVD', + 0x077: 'PAUSE', + 0x078: 'HLT', + 0x079: 'INVLPG', + 0x07a: 'INVLPGA', + 0x07b: 'IOIO', + 0x07c: 'MSR', + 0x07d: 'TASK_SWITCH', + 0x07e: 'FERR_FREEZE', + 0x07f: 'SHUTDOWN', + 0x080: 'VMRUN', + 0x081: 'VMMCALL', + 0x082: 'VMLOAD', + 0x083: 'VMSAVE', + 0x084: 'STGI', + 0x085: 'CLGI', + 0x086: 'SKINIT', + 0x087: 'RDTSCP', + 0x088: 'ICEBP', + 0x089: 'WBINVD', + 0x08a: 'MONITOR', + 0x08b: 'MWAIT', + 0x08c: 'MWAIT_COND', + 0x08d: 'XSETBV', + 0x400: 'NPF', +} + +# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h) +aarch64_exit_reasons = { + 0x00: 'UNKNOWN', + 0x01: 'WFI', + 0x03: 'CP15_32', + 0x04: 'CP15_64', + 0x05: 'CP14_MR', + 0x06: 'CP14_LS', + 0x07: 'FP_ASIMD', + 0x08: 'CP10_ID', + 0x0C: 'CP14_64', + 0x0E: 'ILL_ISS', + 0x11: 'SVC32', + 0x12: 'HVC32', + 0x13: 'SMC32', + 0x15: 'SVC64', + 0x16: 'HVC64', + 0x17: 'SMC64', + 0x18: 'SYS64', + 0x20: 'IABT', + 0x21: 'IABT_HYP', + 0x22: 'PC_ALIGN', + 0x24: 'DABT', + 0x25: 'DABT_HYP', + 0x26: 'SP_ALIGN', + 0x28: 'FP_EXC32', + 0x2C: 'FP_EXC64', + 0x2F: 'SERROR', + 0x30: 'BREAKPT', + 0x31: 'BREAKPT_HYP', + 0x32: 'SOFTSTP', + 0x33: 'SOFTSTP_HYP', + 0x34: 'WATCHPT', + 0x35: 'WATCHPT_HYP', + 0x38: 'BKPT32', + 0x3A: 'VECTOR32', + 0x3C: 'BRK64', +} + +# From include/uapi/linux/kvm.h, KVM_EXIT_xxx +userspace_exit_reasons = { + 0: 'UNKNOWN', + 1: 'EXCEPTION', + 2: 'IO', + 3: 'HYPERCALL', + 4: 'DEBUG', + 5: 'HLT', + 6: 'MMIO', + 7: 'IRQ_WINDOW_OPEN', + 8: 'SHUTDOWN', + 9: 'FAIL_ENTRY', + 10: 'INTR', + 11: 'SET_TPR', + 12: 'TPR_ACCESS', + 13: 'S390_SIEIC', + 14: 'S390_RESET', + 15: 'DCR', + 16: 'NMI', + 17: 'INTERNAL_ERROR', + 18: 'OSI', + 19: 'PAPR_HCALL', + 20: 'S390_UCONTROL', + 21: 'WATCHDOG', + 22: 'S390_TSCH', + 23: 'EPR', + 24: 'SYSTEM_EVENT', +} + +x86_exit_reasons = { + 'vmx': vmx_exit_reasons, + 'svm': svm_exit_reasons, +} + +sc_perf_evt_open = None +exit_reasons = None + +ioctl_numbers = { + 'SET_FILTER' : 0x40082406, + 'ENABLE' : 0x00002400, + 'DISABLE' : 0x00002401, + 'RESET' : 0x00002403, +} + +def x86_init(flag): + globals().update({ + 'sc_perf_evt_open' : 298, + 'exit_reasons' : x86_exit_reasons[flag], + }) + +def s390_init(): + globals().update({ + 'sc_perf_evt_open' : 331 + }) + +def ppc_init(): + globals().update({ + 'sc_perf_evt_open' : 319, + 'ioctl_numbers' : { + 'SET_FILTER' : 0x80002406 | (ctypes.sizeof(ctypes.c_char_p) << 16), + 'ENABLE' : 0x20002400, + 'DISABLE' : 0x20002401, + } + }) + +def aarch64_init(): + globals().update({ + 'sc_perf_evt_open' : 241, + 'exit_reasons' : aarch64_exit_reasons, + }) + +def detect_platform(): + if os.uname()[4].startswith('ppc'): + ppc_init() + return + elif os.uname()[4].startswith('aarch64'): + aarch64_init() + return + + for line in file('/proc/cpuinfo').readlines(): + if line.startswith('flags'): + for flag in line.split(): + if flag in x86_exit_reasons: + x86_init(flag) + return + elif line.startswith('vendor_id'): + for flag in line.split(): + if flag == 'IBM/S390': + s390_init() + return + +detect_platform() + +def invert(d): + return dict((x[1], x[0]) for x in d.iteritems()) + +filters = {} +filters['kvm_userspace_exit'] = ('reason', invert(userspace_exit_reasons)) +if exit_reasons: + filters['kvm_exit'] = ('exit_reason', invert(exit_reasons)) + +import struct, array + +libc = ctypes.CDLL('libc.so.6') +syscall = libc.syscall +get_errno = libc.__errno_location +get_errno.restype = POINTER(c_int) + +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 _perf_event_open(attr, pid, cpu, group_fd, flags): + return syscall(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_HARDWARE = 0 +PERF_TYPE_SOFTWARE = 1 +PERF_TYPE_TRACEPOINT = 2 +PERF_TYPE_HW_CACHE = 3 +PERF_TYPE_RAW = 4 +PERF_TYPE_BREAKPOINT = 5 + +PERF_SAMPLE_IP = 1 << 0 +PERF_SAMPLE_TID = 1 << 1 +PERF_SAMPLE_TIME = 1 << 2 +PERF_SAMPLE_ADDR = 1 << 3 +PERF_SAMPLE_READ = 1 << 4 +PERF_SAMPLE_CALLCHAIN = 1 << 5 +PERF_SAMPLE_ID = 1 << 6 +PERF_SAMPLE_CPU = 1 << 7 +PERF_SAMPLE_PERIOD = 1 << 8 +PERF_SAMPLE_STREAM_ID = 1 << 9 +PERF_SAMPLE_RAW = 1 << 10 + +PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0 +PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1 +PERF_FORMAT_ID = 1 << 2 +PERF_FORMAT_GROUP = 1 << 3 + +import re + +sys_tracing = '/sys/kernel/debug/tracing' + +class Group(object): + def __init__(self, cpu): + self.events = [] + self.group_leader = None + self.cpu = cpu + def add_event(self, name, event_set, tracepoint, filter = None): + self.events.append(Event(group = self, + name = name, event_set = event_set, + tracepoint = tracepoint, filter = filter)) + if len(self.events) == 1: + self.file = os.fdopen(self.events[0].fd) + def read(self): + bytes = 8 * (1 + len(self.events)) + fmt = 'xxxxxxxx' + 'q' * len(self.events) + return dict(zip([event.name for event in self.events], + struct.unpack(fmt, self.file.read(bytes)))) + +class Event(object): + def __init__(self, group, name, event_set, tracepoint, filter = None): + self.name = name + attr = perf_event_attr() + attr.type = PERF_TYPE_TRACEPOINT + attr.size = ctypes.sizeof(attr) + id_path = os.path.join(sys_tracing, 'events', event_set, + tracepoint, 'id') + id = int(file(id_path).read()) + attr.config = id + attr.sample_type = (PERF_SAMPLE_RAW + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU) + attr.sample_period = 1 + attr.read_format = PERF_FORMAT_GROUP + group_leader = -1 + if group.events: + group_leader = group.events[0].fd + fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) + if fd == -1: + err = get_errno()[0] + raise Exception('perf_event_open failed, errno = ' + err.__str__()) + if filter: + import fcntl + fcntl.ioctl(fd, ioctl_numbers['SET_FILTER'], filter) + self.fd = fd + def enable(self): + import fcntl + fcntl.ioctl(self.fd, ioctl_numbers['ENABLE'], 0) + def disable(self): + import fcntl + fcntl.ioctl(self.fd, ioctl_numbers['DISABLE'], 0) + def reset(self): + import fcntl + fcntl.ioctl(self.fd, ioctl_numbers['RESET'], 0) + +class TracepointProvider(object): + def __init__(self): + path = os.path.join(sys_tracing, 'events', 'kvm') + fields = [f + for f in os.listdir(path) + if os.path.isdir(os.path.join(path, f))] + extra = [] + for f in fields: + if f in filters: + subfield, values = filters[f] + for name, number in values.iteritems(): + extra.append(f + '(' + name + ')') + fields += extra + self._setup(fields) + self.select(fields) + def fields(self): + return self._fields + + def _online_cpus(self): + l = [] + pattern = r'cpu([0-9]+)' + basedir = '/sys/devices/system/cpu' + for entry in os.listdir(basedir): + match = re.match(pattern, entry) + if not match: + continue + path = os.path.join(basedir, entry, 'online') + if os.path.exists(path) and open(path).read().strip() != '1': + continue + l.append(int(match.group(1))) + return l + + def _setup(self, _fields): + self._fields = _fields + cpus = self._online_cpus() + import resource + nfiles = len(cpus) * 1000 + resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) + events = [] + self.group_leaders = [] + for cpu in cpus: + group = Group(cpu) + for name in _fields: + tracepoint = name + filter = None + m = re.match(r'(.*)\((.*)\)', name) + if m: + tracepoint, sub = m.groups() + filter = '%s==%d\0' % (filters[tracepoint][0], + filters[tracepoint][1][sub]) + event = group.add_event(name, event_set = 'kvm', + tracepoint = tracepoint, + filter = filter) + self.group_leaders.append(group) + def select(self, fields): + for group in self.group_leaders: + for event in group.events: + if event.name in fields: + event.reset() + event.enable() + else: + event.disable() + def read(self): + from collections import defaultdict + ret = defaultdict(int) + for group in self.group_leaders: + for name, val in group.read().iteritems(): + ret[name] += val + return ret + +class Stats: + def __init__(self, providers, fields = None): + self.providers = providers + self.fields_filter = fields + self._update() + def _update(self): + def wanted(key): + import re + if not self.fields_filter: + return True + return re.match(self.fields_filter, key) is not None + self.values = dict() + for d in providers: + provider_fields = [key for key in d.fields() if wanted(key)] + for key in provider_fields: + self.values[key] = None + d.select(provider_fields) + def set_fields_filter(self, fields_filter): + self.fields_filter = fields_filter + self._update() + def get(self): + for d in providers: + new = d.read() + for key in d.fields(): + oldval = self.values.get(key, (0, 0)) + newval = new[key] + newdelta = None + if oldval is not None: + newdelta = newval - oldval[0] + self.values[key] = (newval, newdelta) + return self.values + +if not os.access('/sys/kernel/debug', os.F_OK): + print 'Please enable CONFIG_DEBUG_FS in your kernel' + sys.exit(1) +if not os.access('/sys/kernel/debug/kvm', os.F_OK): + print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')" + print "and ensure the kvm modules are loaded" + sys.exit(1) + +label_width = 40 +number_width = 10 + +def tui(screen, stats): + curses.use_default_colors() + curses.noecho() + drilldown = False + fields_filter = stats.fields_filter + def update_drilldown(): + if not fields_filter: + if drilldown: + stats.set_fields_filter(None) + else: + stats.set_fields_filter(r'^[^\(]*$') + update_drilldown() + def refresh(sleeptime): + screen.erase() + screen.addstr(0, 0, 'kvm statistics') + screen.addstr(2, 1, 'Event') + screen.addstr(2, 1 + label_width + number_width - len('Total'), 'Total') + screen.addstr(2, 1 + label_width + number_width + 8 - len('Current'), 'Current') + row = 3 + s = stats.get() + def sortkey(x): + if s[x][1]: + return (-s[x][1], -s[x][0]) + else: + return (0, -s[x][0]) + for key in sorted(s.keys(), key = sortkey): + if row >= screen.getmaxyx()[0]: + break + values = s[key] + if not values[0] and not values[1]: + break + col = 1 + screen.addstr(row, col, key) + col += label_width + screen.addstr(row, col, '%10d' % (values[0],)) + col += number_width + if values[1] is not None: + screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) + row += 1 + screen.refresh() + + sleeptime = 0.25 + while True: + refresh(sleeptime) + curses.halfdelay(int(sleeptime * 10)) + sleeptime = 3 + try: + c = screen.getkey() + if c == 'x': + drilldown = not drilldown + update_drilldown() + if c == 'q': + break + 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 '%-22s%10d%10d' % (key, values[0], values[1]) + +def log(stats): + keys = sorted(stats.get().iterkeys()) + def banner(): + for k in keys: + print '%10s' % k[0:9], + 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 + +options = optparse.OptionParser() +options.add_option('-1', '--once', '--batch', + action = 'store_true', + default = False, + dest = 'once', + help = 'run in batch mode for one second', + ) +options.add_option('-l', '--log', + action = 'store_true', + default = False, + dest = 'log', + help = 'run in logging mode (like vmstat)', + ) +options.add_option('-t', '--tracepoints', + action = 'store_true', + default = False, + dest = 'tracepoints', + help = 'retrieve statistics from tracepoints', + ) +options.add_option('-d', '--debugfs', + action = 'store_true', + default = False, + dest = 'debugfs', + help = 'retrieve statistics from debugfs', + ) +options.add_option('-f', '--fields', + action = 'store', + default = None, + dest = 'fields', + help = 'fields to display (regex)', + ) +(options, args) = options.parse_args(sys.argv) + +providers = [] +if options.tracepoints: + providers.append(TracepointProvider()) +if options.debugfs: + providers.append(DebugfsProvider()) + +if len(providers) == 0: + try: + providers = [TracepointProvider()] + except: + providers = [DebugfsProvider()] + +stats = Stats(providers, fields = options.fields) + +if options.log: + log(stats) +elif not options.once: + import curses.wrapper + curses.wrapper(tui, stats) +else: + batch(stats) diff --git a/qemu/scripts/kvm/kvm_stat.texi b/qemu/scripts/kvm/kvm_stat.texi new file mode 100644 index 000000000..6ce00d80e --- /dev/null +++ b/qemu/scripts/kvm/kvm_stat.texi @@ -0,0 +1,55 @@ +@example +@c man begin SYNOPSIS +usage: kvm_stat [OPTION]... +@c man end +@end example + +@c man begin DESCRIPTION + +kvm_stat prints counts of KVM kernel module trace events. These events signify +state transitions such as guest mode entry and exit. + +This tool is useful for observing guest behavior from the host perspective. +Often conclusions about performance or buggy behavior can be drawn from the +output. + +The set of KVM kernel module trace events may be specific to the kernel version +or architecture. It is best to check the KVM kernel module source code for the +meaning of events. + +Note that trace events are counted globally across all running guests. + +@c man end + +@c man begin OPTIONS +@table @option +@item -1, --once, --batch + run in batch mode for one second +@item -l, --log + run in logging mode (like vmstat) +@item -t, --tracepoints + retrieve statistics from tracepoints +@item -d, --debugfs + retrieve statistics from debugfs +@item -f, --fields=@var{fields} + fields to display (regex) +@item -h, --help + show help message +@end table + +@c man end + +@ignore + +@setfilename kvm_stat +@settitle Report KVM kernel module event counters. + +@c man begin AUTHOR +Stefan Hajnoczi <stefanha@redhat.com> +@c man end + +@c man begin SEEALSO +perf(1), trace-cmd(1) +@c man end + +@end ignore diff --git a/qemu/scripts/kvm/vmxcap b/qemu/scripts/kvm/vmxcap new file mode 100755 index 000000000..8f0371f49 --- /dev/null +++ b/qemu/scripts/kvm/vmxcap @@ -0,0 +1,260 @@ +#!/usr/bin/python +# +# tool for querying VMX capabilities +# +# Copyright 2009-2010 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. + +MSR_IA32_VMX_BASIC = 0x480 +MSR_IA32_VMX_PINBASED_CTLS = 0x481 +MSR_IA32_VMX_PROCBASED_CTLS = 0x482 +MSR_IA32_VMX_EXIT_CTLS = 0x483 +MSR_IA32_VMX_ENTRY_CTLS = 0x484 +MSR_IA32_VMX_MISC_CTLS = 0x485 +MSR_IA32_VMX_PROCBASED_CTLS2 = 0x48B +MSR_IA32_VMX_EPT_VPID_CAP = 0x48C +MSR_IA32_VMX_TRUE_PINBASED_CTLS = 0x48D +MSR_IA32_VMX_TRUE_PROCBASED_CTLS = 0x48E +MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F +MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490 +MSR_IA32_VMX_VMFUNC = 0x491 + +class msr(object): + def __init__(self): + try: + self.f = open('/dev/cpu/0/msr', 'r', 0) + except: + self.f = open('/dev/msr0', 'r', 0) + def read(self, index, default = None): + import struct + self.f.seek(index) + try: + return struct.unpack('Q', self.f.read(8))[0] + except: + return default + +class Control(object): + def __init__(self, name, bits, cap_msr, true_cap_msr = None): + self.name = name + self.bits = bits + self.cap_msr = cap_msr + self.true_cap_msr = true_cap_msr + def read2(self, nr): + m = msr() + val = m.read(nr, 0) + return (val & 0xffffffff, val >> 32) + def show(self): + print self.name + mbz, mb1 = self.read2(self.cap_msr) + tmbz, tmb1 = 0, 0 + if self.true_cap_msr: + tmbz, tmb1 = self.read2(self.true_cap_msr) + for bit in sorted(self.bits.keys()): + zero = not (mbz & (1 << bit)) + one = mb1 & (1 << bit) + true_zero = not (tmbz & (1 << bit)) + true_one = tmb1 & (1 << bit) + s= '?' + if (self.true_cap_msr and true_zero and true_one + and one and not zero): + s = 'default' + elif zero and not one: + s = 'no' + elif one and not zero: + s = 'forced' + elif one and zero: + s = 'yes' + print ' %-40s %s' % (self.bits[bit], s) + +class Misc(object): + def __init__(self, name, bits, msr): + self.name = name + self.bits = bits + self.msr = msr + def show(self): + print self.name + value = msr().read(self.msr, 0) + def first_bit(key): + if type(key) is tuple: + return key[0] + else: + return key + for bits in sorted(self.bits.keys(), key = first_bit): + if type(bits) is tuple: + lo, hi = bits + fmt = int + else: + lo = hi = bits + def fmt(x): + return { True: 'yes', False: 'no' }[x] + v = (value >> lo) & ((1 << (hi - lo + 1)) - 1) + print ' %-40s %s' % (self.bits[bits], fmt(v)) + +controls = [ + Misc( + name = 'Basic VMX Information', + bits = { + (0, 30): 'Revision', + (32,44): 'VMCS size', + 48: 'VMCS restricted to 32 bit addresses', + 49: 'Dual-monitor support', + (50, 53): 'VMCS memory type', + 54: 'INS/OUTS instruction information', + 55: 'IA32_VMX_TRUE_*_CTLS support', + }, + msr = MSR_IA32_VMX_BASIC, + ), + Control( + name = 'pin-based controls', + bits = { + 0: 'External interrupt exiting', + 3: 'NMI exiting', + 5: 'Virtual NMIs', + 6: 'Activate VMX-preemption timer', + 7: 'Process posted interrupts', + }, + cap_msr = MSR_IA32_VMX_PINBASED_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_PINBASED_CTLS, + ), + + Control( + name = 'primary processor-based controls', + bits = { + 2: 'Interrupt window exiting', + 3: 'Use TSC offsetting', + 7: 'HLT exiting', + 9: 'INVLPG exiting', + 10: 'MWAIT exiting', + 11: 'RDPMC exiting', + 12: 'RDTSC exiting', + 15: 'CR3-load exiting', + 16: 'CR3-store exiting', + 19: 'CR8-load exiting', + 20: 'CR8-store exiting', + 21: 'Use TPR shadow', + 22: 'NMI-window exiting', + 23: 'MOV-DR exiting', + 24: 'Unconditional I/O exiting', + 25: 'Use I/O bitmaps', + 27: 'Monitor trap flag', + 28: 'Use MSR bitmaps', + 29: 'MONITOR exiting', + 30: 'PAUSE exiting', + 31: 'Activate secondary control', + }, + cap_msr = MSR_IA32_VMX_PROCBASED_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_PROCBASED_CTLS, + ), + + Control( + name = 'secondary processor-based controls', + bits = { + 0: 'Virtualize APIC accesses', + 1: 'Enable EPT', + 2: 'Descriptor-table exiting', + 3: 'Enable RDTSCP', + 4: 'Virtualize x2APIC mode', + 5: 'Enable VPID', + 6: 'WBINVD exiting', + 7: 'Unrestricted guest', + 8: 'APIC register emulation', + 9: 'Virtual interrupt delivery', + 10: 'PAUSE-loop exiting', + 11: 'RDRAND exiting', + 12: 'Enable INVPCID', + 13: 'Enable VM functions', + 14: 'VMCS shadowing', + 16: 'RDSEED exiting', + 18: 'EPT-violation #VE', + 20: 'Enable XSAVES/XRSTORS', + }, + cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2, + ), + + Control( + name = 'VM-Exit controls', + bits = { + 2: 'Save debug controls', + 9: 'Host address-space size', + 12: 'Load IA32_PERF_GLOBAL_CTRL', + 15: 'Acknowledge interrupt on exit', + 18: 'Save IA32_PAT', + 19: 'Load IA32_PAT', + 20: 'Save IA32_EFER', + 21: 'Load IA32_EFER', + 22: 'Save VMX-preemption timer value', + }, + cap_msr = MSR_IA32_VMX_EXIT_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS, + ), + + Control( + name = 'VM-Entry controls', + bits = { + 2: 'Load debug controls', + 9: 'IA-32e mode guest', + 10: 'Entry to SMM', + 11: 'Deactivate dual-monitor treatment', + 13: 'Load IA32_PERF_GLOBAL_CTRL', + 14: 'Load IA32_PAT', + 15: 'Load IA32_EFER', + }, + cap_msr = MSR_IA32_VMX_ENTRY_CTLS, + true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS, + ), + + Misc( + name = 'Miscellaneous data', + bits = { + (0,4): 'VMX-preemption timer scale (log2)', + 5: 'Store EFER.LMA into IA-32e mode guest control', + 6: 'HLT activity state', + 7: 'Shutdown activity state', + 8: 'Wait-for-SIPI activity state', + 15: 'IA32_SMBASE support', + (16,24): 'Number of CR3-target values', + (25,27): 'MSR-load/store count recommendation', + 28: 'IA32_SMM_MONITOR_CTL[2] can be set to 1', + 29: 'VMWRITE to VM-exit information fields', + (32,63): 'MSEG revision identifier', + }, + msr = MSR_IA32_VMX_MISC_CTLS, + ), + + Misc( + name = 'VPID and EPT capabilities', + bits = { + 0: 'Execute-only EPT translations', + 6: 'Page-walk length 4', + 8: 'Paging-structure memory type UC', + 14: 'Paging-structure memory type WB', + 16: '2MB EPT pages', + 17: '1GB EPT pages', + 20: 'INVEPT supported', + 21: 'EPT accessed and dirty flags', + 25: 'Single-context INVEPT', + 26: 'All-context INVEPT', + 32: 'INVVPID supported', + 40: 'Individual-address INVVPID', + 41: 'Single-context INVVPID', + 42: 'All-context INVVPID', + 43: 'Single-context-retaining-globals INVVPID', + }, + msr = MSR_IA32_VMX_EPT_VPID_CAP, + ), + Misc( + name = 'VM Functions', + bits = { + 0: 'EPTP Switching', + }, + msr = MSR_IA32_VMX_VMFUNC, + ), + ] + +for c in controls: + c.show() |