scripts/kvm/kvm_stat: Mark globals in functions
[qemu/ar7.git] / scripts / kvm / kvm_stat
blob83450bebc5a1c54338f229baf2f9b77f1ec17564
1 #!/usr/bin/python
3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
8 # Authors:
9 # Avi Kivity <avi@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2. See
12 # the COPYING file in the top-level directory.
14 import curses
15 import sys
16 import os
17 import time
18 import optparse
19 import ctypes
20 import fcntl
21 import resource
22 import struct
23 import re
24 from collections import defaultdict
26 class DebugfsProvider(object):
27 def __init__(self):
28 self.base = '/sys/kernel/debug/kvm'
29 self._fields = walkdir(self.base)[2]
30 def fields(self):
31 return self._fields
32 def select(self, fields):
33 self._fields = fields
34 def read(self):
35 def val(key):
36 return int(file(self.base + '/' + key).read())
37 return dict([(key, val(key)) for key in self._fields])
39 VMX_EXIT_REASONS = {
40 0: 'EXCEPTION_NMI',
41 1: 'EXTERNAL_INTERRUPT',
42 2: 'TRIPLE_FAULT',
43 7: 'PENDING_INTERRUPT',
44 8: 'NMI_WINDOW',
45 9: 'TASK_SWITCH',
46 10: 'CPUID',
47 12: 'HLT',
48 14: 'INVLPG',
49 15: 'RDPMC',
50 16: 'RDTSC',
51 18: 'VMCALL',
52 19: 'VMCLEAR',
53 20: 'VMLAUNCH',
54 21: 'VMPTRLD',
55 22: 'VMPTRST',
56 23: 'VMREAD',
57 24: 'VMRESUME',
58 25: 'VMWRITE',
59 26: 'VMOFF',
60 27: 'VMON',
61 28: 'CR_ACCESS',
62 29: 'DR_ACCESS',
63 30: 'IO_INSTRUCTION',
64 31: 'MSR_READ',
65 32: 'MSR_WRITE',
66 33: 'INVALID_STATE',
67 36: 'MWAIT_INSTRUCTION',
68 39: 'MONITOR_INSTRUCTION',
69 40: 'PAUSE_INSTRUCTION',
70 41: 'MCE_DURING_VMENTRY',
71 43: 'TPR_BELOW_THRESHOLD',
72 44: 'APIC_ACCESS',
73 48: 'EPT_VIOLATION',
74 49: 'EPT_MISCONFIG',
75 54: 'WBINVD',
76 55: 'XSETBV',
77 56: 'APIC_WRITE',
78 58: 'INVPCID',
81 SVM_EXIT_REASONS = {
82 0x000: 'READ_CR0',
83 0x003: 'READ_CR3',
84 0x004: 'READ_CR4',
85 0x008: 'READ_CR8',
86 0x010: 'WRITE_CR0',
87 0x013: 'WRITE_CR3',
88 0x014: 'WRITE_CR4',
89 0x018: 'WRITE_CR8',
90 0x020: 'READ_DR0',
91 0x021: 'READ_DR1',
92 0x022: 'READ_DR2',
93 0x023: 'READ_DR3',
94 0x024: 'READ_DR4',
95 0x025: 'READ_DR5',
96 0x026: 'READ_DR6',
97 0x027: 'READ_DR7',
98 0x030: 'WRITE_DR0',
99 0x031: 'WRITE_DR1',
100 0x032: 'WRITE_DR2',
101 0x033: 'WRITE_DR3',
102 0x034: 'WRITE_DR4',
103 0x035: 'WRITE_DR5',
104 0x036: 'WRITE_DR6',
105 0x037: 'WRITE_DR7',
106 0x040: 'EXCP_BASE',
107 0x060: 'INTR',
108 0x061: 'NMI',
109 0x062: 'SMI',
110 0x063: 'INIT',
111 0x064: 'VINTR',
112 0x065: 'CR0_SEL_WRITE',
113 0x066: 'IDTR_READ',
114 0x067: 'GDTR_READ',
115 0x068: 'LDTR_READ',
116 0x069: 'TR_READ',
117 0x06a: 'IDTR_WRITE',
118 0x06b: 'GDTR_WRITE',
119 0x06c: 'LDTR_WRITE',
120 0x06d: 'TR_WRITE',
121 0x06e: 'RDTSC',
122 0x06f: 'RDPMC',
123 0x070: 'PUSHF',
124 0x071: 'POPF',
125 0x072: 'CPUID',
126 0x073: 'RSM',
127 0x074: 'IRET',
128 0x075: 'SWINT',
129 0x076: 'INVD',
130 0x077: 'PAUSE',
131 0x078: 'HLT',
132 0x079: 'INVLPG',
133 0x07a: 'INVLPGA',
134 0x07b: 'IOIO',
135 0x07c: 'MSR',
136 0x07d: 'TASK_SWITCH',
137 0x07e: 'FERR_FREEZE',
138 0x07f: 'SHUTDOWN',
139 0x080: 'VMRUN',
140 0x081: 'VMMCALL',
141 0x082: 'VMLOAD',
142 0x083: 'VMSAVE',
143 0x084: 'STGI',
144 0x085: 'CLGI',
145 0x086: 'SKINIT',
146 0x087: 'RDTSCP',
147 0x088: 'ICEBP',
148 0x089: 'WBINVD',
149 0x08a: 'MONITOR',
150 0x08b: 'MWAIT',
151 0x08c: 'MWAIT_COND',
152 0x08d: 'XSETBV',
153 0x400: 'NPF',
156 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
157 AARCH64_EXIT_REASONS = {
158 0x00: 'UNKNOWN',
159 0x01: 'WFI',
160 0x03: 'CP15_32',
161 0x04: 'CP15_64',
162 0x05: 'CP14_MR',
163 0x06: 'CP14_LS',
164 0x07: 'FP_ASIMD',
165 0x08: 'CP10_ID',
166 0x0C: 'CP14_64',
167 0x0E: 'ILL_ISS',
168 0x11: 'SVC32',
169 0x12: 'HVC32',
170 0x13: 'SMC32',
171 0x15: 'SVC64',
172 0x16: 'HVC64',
173 0x17: 'SMC64',
174 0x18: 'SYS64',
175 0x20: 'IABT',
176 0x21: 'IABT_HYP',
177 0x22: 'PC_ALIGN',
178 0x24: 'DABT',
179 0x25: 'DABT_HYP',
180 0x26: 'SP_ALIGN',
181 0x28: 'FP_EXC32',
182 0x2C: 'FP_EXC64',
183 0x2F: 'SERROR',
184 0x30: 'BREAKPT',
185 0x31: 'BREAKPT_HYP',
186 0x32: 'SOFTSTP',
187 0x33: 'SOFTSTP_HYP',
188 0x34: 'WATCHPT',
189 0x35: 'WATCHPT_HYP',
190 0x38: 'BKPT32',
191 0x3A: 'VECTOR32',
192 0x3C: 'BRK64',
195 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
196 USERSPACE_EXIT_REASONS = {
197 0: 'UNKNOWN',
198 1: 'EXCEPTION',
199 2: 'IO',
200 3: 'HYPERCALL',
201 4: 'DEBUG',
202 5: 'HLT',
203 6: 'MMIO',
204 7: 'IRQ_WINDOW_OPEN',
205 8: 'SHUTDOWN',
206 9: 'FAIL_ENTRY',
207 10: 'INTR',
208 11: 'SET_TPR',
209 12: 'TPR_ACCESS',
210 13: 'S390_SIEIC',
211 14: 'S390_RESET',
212 15: 'DCR',
213 16: 'NMI',
214 17: 'INTERNAL_ERROR',
215 18: 'OSI',
216 19: 'PAPR_HCALL',
217 20: 'S390_UCONTROL',
218 21: 'WATCHDOG',
219 22: 'S390_TSCH',
220 23: 'EPR',
221 24: 'SYSTEM_EVENT',
224 X86_EXIT_REASONS = {
225 'vmx': VMX_EXIT_REASONS,
226 'svm': SVM_EXIT_REASONS,
229 SC_PERF_EVT_OPEN = None
230 EXIT_REASONS = None
232 IOCTL_NUMBERS = {
233 'SET_FILTER' : 0x40082406,
234 'ENABLE' : 0x00002400,
235 'DISABLE' : 0x00002401,
236 'RESET' : 0x00002403,
239 def x86_init(flag):
240 global SC_PERF_EVT_OPEN
241 global EXIT_REASONS
243 SC_PERF_EVT_OPEN = 298
244 EXIT_REASONS = X86_EXIT_REASONS[flag]
246 def s390_init():
247 global SC_PERF_EVT_OPEN
249 SC_PERF_EVT_OPEN = 331
251 def ppc_init():
252 global SC_PERF_EVT_OPEN
253 global IOCTL_NUMBERS
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)
260 << 16)
262 def aarch64_init():
263 global SC_PERF_EVT_OPEN
264 global EXIT_REASONS
266 SC_PERF_EVT_OPEN = 241
267 EXIT_REASONS = AARCH64_EXIT_REASONS
269 def detect_platform():
270 if os.uname()[4].startswith('ppc'):
271 ppc_init()
272 return
273 elif os.uname()[4].startswith('aarch64'):
274 aarch64_init()
275 return
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:
281 x86_init(flag)
282 return
283 elif line.startswith('vendor_id'):
284 for flag in line.split():
285 if flag == 'IBM/S390':
286 s390_init()
287 return
289 detect_platform()
292 def walkdir(path):
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))
300 def invert(d):
301 return dict((x[1], x[0]) for x in d.iteritems())
303 filters = {}
304 filters['kvm_userspace_exit'] = ('reason', invert(USERSPACE_EXIT_REASONS))
305 if EXIT_REASONS:
306 filters['kvm_exit'] = ('exit_reason', invert(EXIT_REASONS))
308 libc = ctypes.CDLL('libc.so.6')
309 syscall = libc.syscall
310 get_errno = libc.__errno_location
311 get_errno.restype = ctypes.POINTER(ctypes.c_int)
313 class perf_event_attr(ctypes.Structure):
314 _fields_ = [('type', ctypes.c_uint32),
315 ('size', ctypes.c_uint32),
316 ('config', ctypes.c_uint64),
317 ('sample_freq', ctypes.c_uint64),
318 ('sample_type', ctypes.c_uint64),
319 ('read_format', ctypes.c_uint64),
320 ('flags', ctypes.c_uint64),
321 ('wakeup_events', ctypes.c_uint32),
322 ('bp_type', ctypes.c_uint32),
323 ('bp_addr', ctypes.c_uint64),
324 ('bp_len', ctypes.c_uint64),
326 def _perf_event_open(attr, pid, cpu, group_fd, flags):
327 return syscall(SC_PERF_EVT_OPEN, ctypes.pointer(attr), ctypes.c_int(pid),
328 ctypes.c_int(cpu), ctypes.c_int(group_fd),
329 ctypes.c_long(flags))
331 PERF_TYPE_TRACEPOINT = 2
332 PERF_FORMAT_GROUP = 1 << 3
334 sys_tracing = '/sys/kernel/debug/tracing'
336 class Group(object):
337 def __init__(self, cpu):
338 self.events = []
339 self.group_leader = None
340 self.cpu = cpu
341 def add_event(self, name, event_set, tracepoint, filter = None):
342 self.events.append(Event(group = self,
343 name = name, event_set = event_set,
344 tracepoint = tracepoint, filter = filter))
345 if len(self.events) == 1:
346 self.file = os.fdopen(self.events[0].fd)
347 def read(self):
348 bytes = 8 * (1 + len(self.events))
349 fmt = 'xxxxxxxx' + 'q' * len(self.events)
350 return dict(zip([event.name for event in self.events],
351 struct.unpack(fmt, self.file.read(bytes))))
353 class Event(object):
354 def __init__(self, group, name, event_set, tracepoint, filter = None):
355 self.name = name
356 attr = perf_event_attr()
357 attr.type = PERF_TYPE_TRACEPOINT
358 attr.size = ctypes.sizeof(attr)
359 id_path = os.path.join(sys_tracing, 'events', event_set,
360 tracepoint, 'id')
361 id = int(file(id_path).read())
362 attr.config = id
363 attr.sample_period = 1
364 attr.read_format = PERF_FORMAT_GROUP
365 group_leader = -1
366 if group.events:
367 group_leader = group.events[0].fd
368 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
369 if fd == -1:
370 err = get_errno()[0]
371 raise Exception('perf_event_open failed, errno = ' + err.__str__())
372 if filter:
373 fcntl.ioctl(fd, IOCTL_NUMBERS['SET_FILTER'], filter)
374 self.fd = fd
375 def enable(self):
376 fcntl.ioctl(self.fd, IOCTL_NUMBERS['ENABLE'], 0)
377 def disable(self):
378 fcntl.ioctl(self.fd, IOCTL_NUMBERS['DISABLE'], 0)
379 def reset(self):
380 fcntl.ioctl(self.fd, IOCTL_NUMBERS['RESET'], 0)
382 class TracepointProvider(object):
383 def __init__(self):
384 path = os.path.join(sys_tracing, 'events', 'kvm')
385 fields = walkdir(path)[1]
386 extra = []
387 for f in fields:
388 if f in filters:
389 subfield, values = filters[f]
390 for name, number in values.iteritems():
391 extra.append(f + '(' + name + ')')
392 fields += extra
393 self._setup(fields)
394 self.select(fields)
395 def fields(self):
396 return self._fields
398 def _online_cpus(self):
399 l = []
400 pattern = r'cpu([0-9]+)'
401 basedir = '/sys/devices/system/cpu'
402 for entry in os.listdir(basedir):
403 match = re.match(pattern, entry)
404 if not match:
405 continue
406 path = os.path.join(basedir, entry, 'online')
407 if os.path.exists(path) and open(path).read().strip() != '1':
408 continue
409 l.append(int(match.group(1)))
410 return l
412 def _setup(self, _fields):
413 self._fields = _fields
414 cpus = self._online_cpus()
415 nfiles = len(cpus) * 1000
416 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
417 events = []
418 self.group_leaders = []
419 for cpu in cpus:
420 group = Group(cpu)
421 for name in _fields:
422 tracepoint = name
423 filter = None
424 m = re.match(r'(.*)\((.*)\)', name)
425 if m:
426 tracepoint, sub = m.groups()
427 filter = '%s==%d\0' % (filters[tracepoint][0],
428 filters[tracepoint][1][sub])
429 event = group.add_event(name, event_set = 'kvm',
430 tracepoint = tracepoint,
431 filter = filter)
432 self.group_leaders.append(group)
433 def select(self, fields):
434 for group in self.group_leaders:
435 for event in group.events:
436 if event.name in fields:
437 event.reset()
438 event.enable()
439 else:
440 event.disable()
441 def read(self):
442 ret = defaultdict(int)
443 for group in self.group_leaders:
444 for name, val in group.read().iteritems():
445 ret[name] += val
446 return ret
448 class Stats:
449 def __init__(self, providers, fields = None):
450 self.providers = providers
451 self.fields_filter = fields
452 self._update()
453 def _update(self):
454 def wanted(key):
455 if not self.fields_filter:
456 return True
457 return re.match(self.fields_filter, key) is not None
458 self.values = dict()
459 for d in providers:
460 provider_fields = [key for key in d.fields() if wanted(key)]
461 for key in provider_fields:
462 self.values[key] = None
463 d.select(provider_fields)
464 def set_fields_filter(self, fields_filter):
465 self.fields_filter = fields_filter
466 self._update()
467 def get(self):
468 for d in providers:
469 new = d.read()
470 for key in d.fields():
471 oldval = self.values.get(key, (0, 0))
472 newval = new[key]
473 newdelta = None
474 if oldval is not None:
475 newdelta = newval - oldval[0]
476 self.values[key] = (newval, newdelta)
477 return self.values
479 if not os.access('/sys/kernel/debug', os.F_OK):
480 print 'Please enable CONFIG_DEBUG_FS in your kernel'
481 sys.exit(1)
482 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
483 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
484 print "and ensure the kvm modules are loaded"
485 sys.exit(1)
487 LABEL_WIDTH = 40
488 NUMBER_WIDTH = 10
490 def tui(screen, stats):
491 curses.use_default_colors()
492 curses.noecho()
493 drilldown = False
494 fields_filter = stats.fields_filter
495 def update_drilldown():
496 if not fields_filter:
497 if drilldown:
498 stats.set_fields_filter(None)
499 else:
500 stats.set_fields_filter(r'^[^\(]*$')
501 update_drilldown()
502 def refresh(sleeptime):
503 screen.erase()
504 screen.addstr(0, 0, 'kvm statistics')
505 screen.addstr(2, 1, 'Event')
506 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
507 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
508 row = 3
509 s = stats.get()
510 def sortkey(x):
511 if s[x][1]:
512 return (-s[x][1], -s[x][0])
513 else:
514 return (0, -s[x][0])
515 for key in sorted(s.keys(), key = sortkey):
516 if row >= screen.getmaxyx()[0]:
517 break
518 values = s[key]
519 if not values[0] and not values[1]:
520 break
521 col = 1
522 screen.addstr(row, col, key)
523 col += LABEL_WIDTH
524 screen.addstr(row, col, '%10d' % (values[0],))
525 col += NUMBER_WIDTH
526 if values[1] is not None:
527 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
528 row += 1
529 screen.refresh()
531 sleeptime = 0.25
532 while True:
533 refresh(sleeptime)
534 curses.halfdelay(int(sleeptime * 10))
535 sleeptime = 3
536 try:
537 c = screen.getkey()
538 if c == 'x':
539 drilldown = not drilldown
540 update_drilldown()
541 if c == 'q':
542 break
543 except KeyboardInterrupt:
544 break
545 except curses.error:
546 continue
548 def batch(stats):
549 s = stats.get()
550 time.sleep(1)
551 s = stats.get()
552 for key in sorted(s.keys()):
553 values = s[key]
554 print '%-22s%10d%10d' % (key, values[0], values[1])
556 def log(stats):
557 keys = sorted(stats.get().iterkeys())
558 def banner():
559 for k in keys:
560 print '%10s' % k[0:9],
561 print
562 def statline():
563 s = stats.get()
564 for k in keys:
565 print ' %9d' % s[k][1],
566 print
567 line = 0
568 banner_repeat = 20
569 while True:
570 time.sleep(1)
571 if line % banner_repeat == 0:
572 banner()
573 statline()
574 line += 1
576 options = optparse.OptionParser()
577 options.add_option('-1', '--once', '--batch',
578 action = 'store_true',
579 default = False,
580 dest = 'once',
581 help = 'run in batch mode for one second',
583 options.add_option('-l', '--log',
584 action = 'store_true',
585 default = False,
586 dest = 'log',
587 help = 'run in logging mode (like vmstat)',
589 options.add_option('-t', '--tracepoints',
590 action = 'store_true',
591 default = False,
592 dest = 'tracepoints',
593 help = 'retrieve statistics from tracepoints',
595 options.add_option('-d', '--debugfs',
596 action = 'store_true',
597 default = False,
598 dest = 'debugfs',
599 help = 'retrieve statistics from debugfs',
601 options.add_option('-f', '--fields',
602 action = 'store',
603 default = None,
604 dest = 'fields',
605 help = 'fields to display (regex)',
607 (options, args) = options.parse_args(sys.argv)
609 providers = []
610 if options.tracepoints:
611 providers.append(TracepointProvider())
612 if options.debugfs:
613 providers.append(DebugfsProvider())
615 if len(providers) == 0:
616 try:
617 providers = [TracepointProvider()]
618 except:
619 providers = [DebugfsProvider()]
621 stats = Stats(providers, fields = options.fields)
623 if options.log:
624 log(stats)
625 elif not options.once:
626 curses.wrapper(tui, stats)
627 else:
628 batch(stats)