|
|
1.1 ! root 1: #!/usr/bin/python ! 2: # ! 3: # top-like utility for displaying kvm statistics ! 4: # ! 5: # Copyright 2006-2008 Qumranet Technologies ! 6: # Copyright 2008-2011 Red Hat, Inc. ! 7: # ! 8: # Authors: ! 9: # Avi Kivity <[email protected]> ! 10: # ! 11: # This work is licensed under the terms of the GNU GPL, version 2. See ! 12: # the COPYING file in the top-level directory. ! 13: ! 14: import curses ! 15: import sys, os, time, optparse ! 16: ! 17: class DebugfsProvider(object): ! 18: def __init__(self): ! 19: self.base = '/sys/kernel/debug/kvm' ! 20: self._fields = os.listdir(self.base) ! 21: def fields(self): ! 22: return self._fields ! 23: def select(self, fields): ! 24: self._fields = fields ! 25: def read(self): ! 26: def val(key): ! 27: return int(file(self.base + '/' + key).read()) ! 28: return dict([(key, val(key)) for key in self._fields]) ! 29: ! 30: vmx_exit_reasons = { ! 31: 0: 'EXCEPTION_NMI', ! 32: 1: 'EXTERNAL_INTERRUPT', ! 33: 2: 'TRIPLE_FAULT', ! 34: 7: 'PENDING_INTERRUPT', ! 35: 8: 'NMI_WINDOW', ! 36: 9: 'TASK_SWITCH', ! 37: 10: 'CPUID', ! 38: 12: 'HLT', ! 39: 14: 'INVLPG', ! 40: 15: 'RDPMC', ! 41: 16: 'RDTSC', ! 42: 18: 'VMCALL', ! 43: 19: 'VMCLEAR', ! 44: 20: 'VMLAUNCH', ! 45: 21: 'VMPTRLD', ! 46: 22: 'VMPTRST', ! 47: 23: 'VMREAD', ! 48: 24: 'VMRESUME', ! 49: 25: 'VMWRITE', ! 50: 26: 'VMOFF', ! 51: 27: 'VMON', ! 52: 28: 'CR_ACCESS', ! 53: 29: 'DR_ACCESS', ! 54: 30: 'IO_INSTRUCTION', ! 55: 31: 'MSR_READ', ! 56: 32: 'MSR_WRITE', ! 57: 33: 'INVALID_STATE', ! 58: 36: 'MWAIT_INSTRUCTION', ! 59: 39: 'MONITOR_INSTRUCTION', ! 60: 40: 'PAUSE_INSTRUCTION', ! 61: 41: 'MCE_DURING_VMENTRY', ! 62: 43: 'TPR_BELOW_THRESHOLD', ! 63: 44: 'APIC_ACCESS', ! 64: 48: 'EPT_VIOLATION', ! 65: 49: 'EPT_MISCONFIG', ! 66: 54: 'WBINVD', ! 67: 55: 'XSETBV', ! 68: } ! 69: ! 70: svm_exit_reasons = { ! 71: 0x000: 'READ_CR0', ! 72: 0x003: 'READ_CR3', ! 73: 0x004: 'READ_CR4', ! 74: 0x008: 'READ_CR8', ! 75: 0x010: 'WRITE_CR0', ! 76: 0x013: 'WRITE_CR3', ! 77: 0x014: 'WRITE_CR4', ! 78: 0x018: 'WRITE_CR8', ! 79: 0x020: 'READ_DR0', ! 80: 0x021: 'READ_DR1', ! 81: 0x022: 'READ_DR2', ! 82: 0x023: 'READ_DR3', ! 83: 0x024: 'READ_DR4', ! 84: 0x025: 'READ_DR5', ! 85: 0x026: 'READ_DR6', ! 86: 0x027: 'READ_DR7', ! 87: 0x030: 'WRITE_DR0', ! 88: 0x031: 'WRITE_DR1', ! 89: 0x032: 'WRITE_DR2', ! 90: 0x033: 'WRITE_DR3', ! 91: 0x034: 'WRITE_DR4', ! 92: 0x035: 'WRITE_DR5', ! 93: 0x036: 'WRITE_DR6', ! 94: 0x037: 'WRITE_DR7', ! 95: 0x040: 'EXCP_BASE', ! 96: 0x060: 'INTR', ! 97: 0x061: 'NMI', ! 98: 0x062: 'SMI', ! 99: 0x063: 'INIT', ! 100: 0x064: 'VINTR', ! 101: 0x065: 'CR0_SEL_WRITE', ! 102: 0x066: 'IDTR_READ', ! 103: 0x067: 'GDTR_READ', ! 104: 0x068: 'LDTR_READ', ! 105: 0x069: 'TR_READ', ! 106: 0x06a: 'IDTR_WRITE', ! 107: 0x06b: 'GDTR_WRITE', ! 108: 0x06c: 'LDTR_WRITE', ! 109: 0x06d: 'TR_WRITE', ! 110: 0x06e: 'RDTSC', ! 111: 0x06f: 'RDPMC', ! 112: 0x070: 'PUSHF', ! 113: 0x071: 'POPF', ! 114: 0x072: 'CPUID', ! 115: 0x073: 'RSM', ! 116: 0x074: 'IRET', ! 117: 0x075: 'SWINT', ! 118: 0x076: 'INVD', ! 119: 0x077: 'PAUSE', ! 120: 0x078: 'HLT', ! 121: 0x079: 'INVLPG', ! 122: 0x07a: 'INVLPGA', ! 123: 0x07b: 'IOIO', ! 124: 0x07c: 'MSR', ! 125: 0x07d: 'TASK_SWITCH', ! 126: 0x07e: 'FERR_FREEZE', ! 127: 0x07f: 'SHUTDOWN', ! 128: 0x080: 'VMRUN', ! 129: 0x081: 'VMMCALL', ! 130: 0x082: 'VMLOAD', ! 131: 0x083: 'VMSAVE', ! 132: 0x084: 'STGI', ! 133: 0x085: 'CLGI', ! 134: 0x086: 'SKINIT', ! 135: 0x087: 'RDTSCP', ! 136: 0x088: 'ICEBP', ! 137: 0x089: 'WBINVD', ! 138: 0x08a: 'MONITOR', ! 139: 0x08b: 'MWAIT', ! 140: 0x08c: 'MWAIT_COND', ! 141: 0x400: 'NPF', ! 142: } ! 143: ! 144: vendor_exit_reasons = { ! 145: 'vmx': vmx_exit_reasons, ! 146: 'svm': svm_exit_reasons, ! 147: } ! 148: ! 149: exit_reasons = None ! 150: ! 151: for line in file('/proc/cpuinfo').readlines(): ! 152: if line.startswith('flags'): ! 153: for flag in line.split(): ! 154: if flag in vendor_exit_reasons: ! 155: exit_reasons = vendor_exit_reasons[flag] ! 156: ! 157: filters = { ! 158: 'kvm_exit': ('exit_reason', exit_reasons) ! 159: } ! 160: ! 161: def invert(d): ! 162: return dict((x[1], x[0]) for x in d.iteritems()) ! 163: ! 164: for f in filters: ! 165: filters[f] = (filters[f][0], invert(filters[f][1])) ! 166: ! 167: import ctypes, struct, array ! 168: ! 169: libc = ctypes.CDLL('libc.so.6') ! 170: syscall = libc.syscall ! 171: class perf_event_attr(ctypes.Structure): ! 172: _fields_ = [('type', ctypes.c_uint32), ! 173: ('size', ctypes.c_uint32), ! 174: ('config', ctypes.c_uint64), ! 175: ('sample_freq', ctypes.c_uint64), ! 176: ('sample_type', ctypes.c_uint64), ! 177: ('read_format', ctypes.c_uint64), ! 178: ('flags', ctypes.c_uint64), ! 179: ('wakeup_events', ctypes.c_uint32), ! 180: ('bp_type', ctypes.c_uint32), ! 181: ('bp_addr', ctypes.c_uint64), ! 182: ('bp_len', ctypes.c_uint64), ! 183: ] ! 184: def _perf_event_open(attr, pid, cpu, group_fd, flags): ! 185: return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid), ! 186: ctypes.c_int(cpu), ctypes.c_int(group_fd), ! 187: ctypes.c_long(flags)) ! 188: ! 189: PERF_TYPE_HARDWARE = 0 ! 190: PERF_TYPE_SOFTWARE = 1 ! 191: PERF_TYPE_TRACEPOINT = 2 ! 192: PERF_TYPE_HW_CACHE = 3 ! 193: PERF_TYPE_RAW = 4 ! 194: PERF_TYPE_BREAKPOINT = 5 ! 195: ! 196: PERF_SAMPLE_IP = 1 << 0 ! 197: PERF_SAMPLE_TID = 1 << 1 ! 198: PERF_SAMPLE_TIME = 1 << 2 ! 199: PERF_SAMPLE_ADDR = 1 << 3 ! 200: PERF_SAMPLE_READ = 1 << 4 ! 201: PERF_SAMPLE_CALLCHAIN = 1 << 5 ! 202: PERF_SAMPLE_ID = 1 << 6 ! 203: PERF_SAMPLE_CPU = 1 << 7 ! 204: PERF_SAMPLE_PERIOD = 1 << 8 ! 205: PERF_SAMPLE_STREAM_ID = 1 << 9 ! 206: PERF_SAMPLE_RAW = 1 << 10 ! 207: ! 208: PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0 ! 209: PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1 ! 210: PERF_FORMAT_ID = 1 << 2 ! 211: PERF_FORMAT_GROUP = 1 << 3 ! 212: ! 213: import re ! 214: ! 215: sys_tracing = '/sys/kernel/debug/tracing' ! 216: ! 217: class Group(object): ! 218: def __init__(self, cpu): ! 219: self.events = [] ! 220: self.group_leader = None ! 221: self.cpu = cpu ! 222: def add_event(self, name, event_set, tracepoint, filter = None): ! 223: self.events.append(Event(group = self, ! 224: name = name, event_set = event_set, ! 225: tracepoint = tracepoint, filter = filter)) ! 226: if len(self.events) == 1: ! 227: self.file = os.fdopen(self.events[0].fd) ! 228: def read(self): ! 229: bytes = 8 * (1 + len(self.events)) ! 230: fmt = 'xxxxxxxx' + 'q' * len(self.events) ! 231: return dict(zip([event.name for event in self.events], ! 232: struct.unpack(fmt, self.file.read(bytes)))) ! 233: ! 234: class Event(object): ! 235: def __init__(self, group, name, event_set, tracepoint, filter = None): ! 236: self.name = name ! 237: attr = perf_event_attr() ! 238: attr.type = PERF_TYPE_TRACEPOINT ! 239: attr.size = ctypes.sizeof(attr) ! 240: id_path = os.path.join(sys_tracing, 'events', event_set, ! 241: tracepoint, 'id') ! 242: id = int(file(id_path).read()) ! 243: attr.config = id ! 244: attr.sample_type = (PERF_SAMPLE_RAW ! 245: | PERF_SAMPLE_TIME ! 246: | PERF_SAMPLE_CPU) ! 247: attr.sample_period = 1 ! 248: attr.read_format = PERF_FORMAT_GROUP ! 249: group_leader = -1 ! 250: if group.events: ! 251: group_leader = group.events[0].fd ! 252: fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) ! 253: if fd == -1: ! 254: raise Exception('perf_event_open failed') ! 255: if filter: ! 256: import fcntl ! 257: fcntl.ioctl(fd, 0x40082406, filter) ! 258: self.fd = fd ! 259: def enable(self): ! 260: import fcntl ! 261: fcntl.ioctl(self.fd, 0x00002400, 0) ! 262: def disable(self): ! 263: import fcntl ! 264: fcntl.ioctl(self.fd, 0x00002401, 0) ! 265: ! 266: class TracepointProvider(object): ! 267: def __init__(self): ! 268: path = os.path.join(sys_tracing, 'events', 'kvm') ! 269: fields = [f ! 270: for f in os.listdir(path) ! 271: if os.path.isdir(os.path.join(path, f))] ! 272: extra = [] ! 273: for f in fields: ! 274: if f in filters: ! 275: subfield, values = filters[f] ! 276: for name, number in values.iteritems(): ! 277: extra.append(f + '(' + name + ')') ! 278: fields += extra ! 279: self._setup(fields) ! 280: self.select(fields) ! 281: def fields(self): ! 282: return self._fields ! 283: def _setup(self, _fields): ! 284: self._fields = _fields ! 285: cpure = r'cpu([0-9]+)' ! 286: self.cpus = [int(re.match(cpure, x).group(1)) ! 287: for x in os.listdir('/sys/devices/system/cpu') ! 288: if re.match(cpure, x)] ! 289: import resource ! 290: nfiles = len(self.cpus) * 1000 ! 291: resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) ! 292: events = [] ! 293: self.group_leaders = [] ! 294: for cpu in self.cpus: ! 295: group = Group(cpu) ! 296: for name in _fields: ! 297: tracepoint = name ! 298: filter = None ! 299: m = re.match(r'(.*)\((.*)\)', name) ! 300: if m: ! 301: tracepoint, sub = m.groups() ! 302: filter = '%s==%d\0' % (filters[tracepoint][0], ! 303: filters[tracepoint][1][sub]) ! 304: event = group.add_event(name, event_set = 'kvm', ! 305: tracepoint = tracepoint, ! 306: filter = filter) ! 307: self.group_leaders.append(group) ! 308: def select(self, fields): ! 309: for group in self.group_leaders: ! 310: for event in group.events: ! 311: if event.name in fields: ! 312: event.enable() ! 313: else: ! 314: event.disable() ! 315: def read(self): ! 316: from collections import defaultdict ! 317: ret = defaultdict(int) ! 318: for group in self.group_leaders: ! 319: for name, val in group.read().iteritems(): ! 320: ret[name] += val ! 321: return ret ! 322: ! 323: class Stats: ! 324: def __init__(self, provider, fields = None): ! 325: self.provider = provider ! 326: self.fields_filter = fields ! 327: self._update() ! 328: def _update(self): ! 329: def wanted(key): ! 330: import re ! 331: if not self.fields_filter: ! 332: return True ! 333: return re.match(self.fields_filter, key) is not None ! 334: self.values = dict([(key, None) ! 335: for key in provider.fields() ! 336: if wanted(key)]) ! 337: self.provider.select(self.values.keys()) ! 338: def set_fields_filter(self, fields_filter): ! 339: self.fields_filter = fields_filter ! 340: self._update() ! 341: def get(self): ! 342: new = self.provider.read() ! 343: for key in self.provider.fields(): ! 344: oldval = self.values.get(key, (0, 0)) ! 345: newval = new[key] ! 346: newdelta = None ! 347: if oldval is not None: ! 348: newdelta = newval - oldval[0] ! 349: self.values[key] = (newval, newdelta) ! 350: return self.values ! 351: ! 352: if not os.access('/sys/kernel/debug', os.F_OK): ! 353: print 'Please enable CONFIG_DEBUG_FS in your kernel' ! 354: sys.exit(1) ! 355: if not os.access('/sys/kernel/debug/kvm', os.F_OK): ! 356: print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')" ! 357: print "and ensure the kvm modules are loaded" ! 358: sys.exit(1) ! 359: ! 360: label_width = 40 ! 361: number_width = 10 ! 362: ! 363: def tui(screen, stats): ! 364: curses.use_default_colors() ! 365: curses.noecho() ! 366: drilldown = False ! 367: fields_filter = stats.fields_filter ! 368: def update_drilldown(): ! 369: if not fields_filter: ! 370: if drilldown: ! 371: stats.set_fields_filter(None) ! 372: else: ! 373: stats.set_fields_filter(r'^[^\(]*$') ! 374: update_drilldown() ! 375: def refresh(sleeptime): ! 376: screen.erase() ! 377: screen.addstr(0, 0, 'kvm statistics') ! 378: row = 2 ! 379: s = stats.get() ! 380: def sortkey(x): ! 381: if s[x][1]: ! 382: return (-s[x][1], -s[x][0]) ! 383: else: ! 384: return (0, -s[x][0]) ! 385: for key in sorted(s.keys(), key = sortkey): ! 386: if row >= screen.getmaxyx()[0]: ! 387: break ! 388: values = s[key] ! 389: if not values[0] and not values[1]: ! 390: break ! 391: col = 1 ! 392: screen.addstr(row, col, key) ! 393: col += label_width ! 394: screen.addstr(row, col, '%10d' % (values[0],)) ! 395: col += number_width ! 396: if values[1] is not None: ! 397: screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) ! 398: row += 1 ! 399: screen.refresh() ! 400: ! 401: sleeptime = 0.25 ! 402: while True: ! 403: refresh(sleeptime) ! 404: curses.halfdelay(int(sleeptime * 10)) ! 405: sleeptime = 3 ! 406: try: ! 407: c = screen.getkey() ! 408: if c == 'x': ! 409: drilldown = not drilldown ! 410: update_drilldown() ! 411: if c == 'q': ! 412: break ! 413: except KeyboardInterrupt: ! 414: break ! 415: except curses.error: ! 416: continue ! 417: ! 418: def batch(stats): ! 419: s = stats.get() ! 420: time.sleep(1) ! 421: s = stats.get() ! 422: for key in sorted(s.keys()): ! 423: values = s[key] ! 424: print '%-22s%10d%10d' % (key, values[0], values[1]) ! 425: ! 426: def log(stats): ! 427: keys = sorted(stats.get().iterkeys()) ! 428: def banner(): ! 429: for k in keys: ! 430: print '%10s' % k[0:9], ! 431: print ! 432: def statline(): ! 433: s = stats.get() ! 434: for k in keys: ! 435: print ' %9d' % s[k][1], ! 436: print ! 437: line = 0 ! 438: banner_repeat = 20 ! 439: while True: ! 440: time.sleep(1) ! 441: if line % banner_repeat == 0: ! 442: banner() ! 443: statline() ! 444: line += 1 ! 445: ! 446: options = optparse.OptionParser() ! 447: options.add_option('-1', '--once', '--batch', ! 448: action = 'store_true', ! 449: default = False, ! 450: dest = 'once', ! 451: help = 'run in batch mode for one second', ! 452: ) ! 453: options.add_option('-l', '--log', ! 454: action = 'store_true', ! 455: default = False, ! 456: dest = 'log', ! 457: help = 'run in logging mode (like vmstat)', ! 458: ) ! 459: options.add_option('-f', '--fields', ! 460: action = 'store', ! 461: default = None, ! 462: dest = 'fields', ! 463: help = 'fields to display (regex)', ! 464: ) ! 465: (options, args) = options.parse_args(sys.argv) ! 466: ! 467: try: ! 468: provider = TracepointProvider() ! 469: except: ! 470: provider = DebugfsProvider() ! 471: ! 472: stats = Stats(provider, fields = options.fields) ! 473: ! 474: if options.log: ! 475: log(stats) ! 476: elif not options.once: ! 477: import curses.wrapper ! 478: curses.wrapper(tui, stats) ! 479: else: ! 480: batch(stats)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.