scripts/kvm/kvm_stat: Removed unneeded PERF constants
[qemu/ar7.git] / scripts / kvm / kvm_stat
blob7a8617dced0a63bd84c56b6964d5bf23f09b4c2e
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 globals().update({
241 'SC_PERF_EVT_OPEN' : 298,
242 'EXIT_REASONS' : X86_EXIT_REASONS[flag],
245 def s390_init():
246 globals().update({
247 'SC_PERF_EVT_OPEN' : 331
250 def ppc_init():
251 globals().update({
252 'SC_PERF_EVT_OPEN' : 319,
253 'IOCTL_NUMBERS' : {
254 'SET_FILTER' : 0x80002406 | (ctypes.sizeof(ctypes.c_char_p) << 16),
255 'ENABLE' : 0x20002400,
256 'DISABLE' : 0x20002401,
260 def aarch64_init():
261 globals().update({
262 'SC_PERF_EVT_OPEN' : 241,
263 'EXIT_REASONS' : AARCH64_EXIT_REASONS,
266 def detect_platform():
267 if os.uname()[4].startswith('ppc'):
268 ppc_init()
269 return
270 elif os.uname()[4].startswith('aarch64'):
271 aarch64_init()
272 return
274 for line in file('/proc/cpuinfo').readlines():
275 if line.startswith('flags'):
276 for flag in line.split():
277 if flag in X86_EXIT_REASONS:
278 x86_init(flag)
279 return
280 elif line.startswith('vendor_id'):
281 for flag in line.split():
282 if flag == 'IBM/S390':
283 s390_init()
284 return
286 detect_platform()
289 def walkdir(path):
290 """Returns os.walk() data for specified directory.
292 As it is only a wrapper it returns the same 3-tuple of (dirpath,
293 dirnames, filenames).
295 return next(os.walk(path))
297 def invert(d):
298 return dict((x[1], x[0]) for x in d.iteritems())
300 filters = {}
301 filters['kvm_userspace_exit'] = ('reason', invert(USERSPACE_EXIT_REASONS))
302 if EXIT_REASONS:
303 filters['kvm_exit'] = ('exit_reason', invert(EXIT_REASONS))
305 libc = ctypes.CDLL('libc.so.6')
306 syscall = libc.syscall
307 get_errno = libc.__errno_location
308 get_errno.restype = ctypes.POINTER(ctypes.c_int)
310 class perf_event_attr(ctypes.Structure):
311 _fields_ = [('type', ctypes.c_uint32),
312 ('size', ctypes.c_uint32),
313 ('config', ctypes.c_uint64),
314 ('sample_freq', ctypes.c_uint64),
315 ('sample_type', ctypes.c_uint64),
316 ('read_format', ctypes.c_uint64),
317 ('flags', ctypes.c_uint64),
318 ('wakeup_events', ctypes.c_uint32),
319 ('bp_type', ctypes.c_uint32),
320 ('bp_addr', ctypes.c_uint64),
321 ('bp_len', ctypes.c_uint64),
323 def _perf_event_open(attr, pid, cpu, group_fd, flags):
324 return syscall(SC_PERF_EVT_OPEN, ctypes.pointer(attr), ctypes.c_int(pid),
325 ctypes.c_int(cpu), ctypes.c_int(group_fd),
326 ctypes.c_long(flags))
328 PERF_TYPE_TRACEPOINT = 2
329 PERF_FORMAT_GROUP = 1 << 3
331 sys_tracing = '/sys/kernel/debug/tracing'
333 class Group(object):
334 def __init__(self, cpu):
335 self.events = []
336 self.group_leader = None
337 self.cpu = cpu
338 def add_event(self, name, event_set, tracepoint, filter = None):
339 self.events.append(Event(group = self,
340 name = name, event_set = event_set,
341 tracepoint = tracepoint, filter = filter))
342 if len(self.events) == 1:
343 self.file = os.fdopen(self.events[0].fd)
344 def read(self):
345 bytes = 8 * (1 + len(self.events))
346 fmt = 'xxxxxxxx' + 'q' * len(self.events)
347 return dict(zip([event.name for event in self.events],
348 struct.unpack(fmt, self.file.read(bytes))))
350 class Event(object):
351 def __init__(self, group, name, event_set, tracepoint, filter = None):
352 self.name = name
353 attr = perf_event_attr()
354 attr.type = PERF_TYPE_TRACEPOINT
355 attr.size = ctypes.sizeof(attr)
356 id_path = os.path.join(sys_tracing, 'events', event_set,
357 tracepoint, 'id')
358 id = int(file(id_path).read())
359 attr.config = id
360 attr.sample_period = 1
361 attr.read_format = PERF_FORMAT_GROUP
362 group_leader = -1
363 if group.events:
364 group_leader = group.events[0].fd
365 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
366 if fd == -1:
367 err = get_errno()[0]
368 raise Exception('perf_event_open failed, errno = ' + err.__str__())
369 if filter:
370 fcntl.ioctl(fd, IOCTL_NUMBERS['SET_FILTER'], filter)
371 self.fd = fd
372 def enable(self):
373 fcntl.ioctl(self.fd, IOCTL_NUMBERS['ENABLE'], 0)
374 def disable(self):
375 fcntl.ioctl(self.fd, IOCTL_NUMBERS['DISABLE'], 0)
376 def reset(self):
377 fcntl.ioctl(self.fd, IOCTL_NUMBERS['RESET'], 0)
379 class TracepointProvider(object):
380 def __init__(self):
381 path = os.path.join(sys_tracing, 'events', 'kvm')
382 fields = walkdir(path)[1]
383 extra = []
384 for f in fields:
385 if f in filters:
386 subfield, values = filters[f]
387 for name, number in values.iteritems():
388 extra.append(f + '(' + name + ')')
389 fields += extra
390 self._setup(fields)
391 self.select(fields)
392 def fields(self):
393 return self._fields
395 def _online_cpus(self):
396 l = []
397 pattern = r'cpu([0-9]+)'
398 basedir = '/sys/devices/system/cpu'
399 for entry in os.listdir(basedir):
400 match = re.match(pattern, entry)
401 if not match:
402 continue
403 path = os.path.join(basedir, entry, 'online')
404 if os.path.exists(path) and open(path).read().strip() != '1':
405 continue
406 l.append(int(match.group(1)))
407 return l
409 def _setup(self, _fields):
410 self._fields = _fields
411 cpus = self._online_cpus()
412 nfiles = len(cpus) * 1000
413 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
414 events = []
415 self.group_leaders = []
416 for cpu in cpus:
417 group = Group(cpu)
418 for name in _fields:
419 tracepoint = name
420 filter = None
421 m = re.match(r'(.*)\((.*)\)', name)
422 if m:
423 tracepoint, sub = m.groups()
424 filter = '%s==%d\0' % (filters[tracepoint][0],
425 filters[tracepoint][1][sub])
426 event = group.add_event(name, event_set = 'kvm',
427 tracepoint = tracepoint,
428 filter = filter)
429 self.group_leaders.append(group)
430 def select(self, fields):
431 for group in self.group_leaders:
432 for event in group.events:
433 if event.name in fields:
434 event.reset()
435 event.enable()
436 else:
437 event.disable()
438 def read(self):
439 ret = defaultdict(int)
440 for group in self.group_leaders:
441 for name, val in group.read().iteritems():
442 ret[name] += val
443 return ret
445 class Stats:
446 def __init__(self, providers, fields = None):
447 self.providers = providers
448 self.fields_filter = fields
449 self._update()
450 def _update(self):
451 def wanted(key):
452 if not self.fields_filter:
453 return True
454 return re.match(self.fields_filter, key) is not None
455 self.values = dict()
456 for d in providers:
457 provider_fields = [key for key in d.fields() if wanted(key)]
458 for key in provider_fields:
459 self.values[key] = None
460 d.select(provider_fields)
461 def set_fields_filter(self, fields_filter):
462 self.fields_filter = fields_filter
463 self._update()
464 def get(self):
465 for d in providers:
466 new = d.read()
467 for key in d.fields():
468 oldval = self.values.get(key, (0, 0))
469 newval = new[key]
470 newdelta = None
471 if oldval is not None:
472 newdelta = newval - oldval[0]
473 self.values[key] = (newval, newdelta)
474 return self.values
476 if not os.access('/sys/kernel/debug', os.F_OK):
477 print 'Please enable CONFIG_DEBUG_FS in your kernel'
478 sys.exit(1)
479 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
480 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
481 print "and ensure the kvm modules are loaded"
482 sys.exit(1)
484 LABEL_WIDTH = 40
485 NUMBER_WIDTH = 10
487 def tui(screen, stats):
488 curses.use_default_colors()
489 curses.noecho()
490 drilldown = False
491 fields_filter = stats.fields_filter
492 def update_drilldown():
493 if not fields_filter:
494 if drilldown:
495 stats.set_fields_filter(None)
496 else:
497 stats.set_fields_filter(r'^[^\(]*$')
498 update_drilldown()
499 def refresh(sleeptime):
500 screen.erase()
501 screen.addstr(0, 0, 'kvm statistics')
502 screen.addstr(2, 1, 'Event')
503 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
504 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
505 row = 3
506 s = stats.get()
507 def sortkey(x):
508 if s[x][1]:
509 return (-s[x][1], -s[x][0])
510 else:
511 return (0, -s[x][0])
512 for key in sorted(s.keys(), key = sortkey):
513 if row >= screen.getmaxyx()[0]:
514 break
515 values = s[key]
516 if not values[0] and not values[1]:
517 break
518 col = 1
519 screen.addstr(row, col, key)
520 col += LABEL_WIDTH
521 screen.addstr(row, col, '%10d' % (values[0],))
522 col += NUMBER_WIDTH
523 if values[1] is not None:
524 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
525 row += 1
526 screen.refresh()
528 sleeptime = 0.25
529 while True:
530 refresh(sleeptime)
531 curses.halfdelay(int(sleeptime * 10))
532 sleeptime = 3
533 try:
534 c = screen.getkey()
535 if c == 'x':
536 drilldown = not drilldown
537 update_drilldown()
538 if c == 'q':
539 break
540 except KeyboardInterrupt:
541 break
542 except curses.error:
543 continue
545 def batch(stats):
546 s = stats.get()
547 time.sleep(1)
548 s = stats.get()
549 for key in sorted(s.keys()):
550 values = s[key]
551 print '%-22s%10d%10d' % (key, values[0], values[1])
553 def log(stats):
554 keys = sorted(stats.get().iterkeys())
555 def banner():
556 for k in keys:
557 print '%10s' % k[0:9],
558 print
559 def statline():
560 s = stats.get()
561 for k in keys:
562 print ' %9d' % s[k][1],
563 print
564 line = 0
565 banner_repeat = 20
566 while True:
567 time.sleep(1)
568 if line % banner_repeat == 0:
569 banner()
570 statline()
571 line += 1
573 options = optparse.OptionParser()
574 options.add_option('-1', '--once', '--batch',
575 action = 'store_true',
576 default = False,
577 dest = 'once',
578 help = 'run in batch mode for one second',
580 options.add_option('-l', '--log',
581 action = 'store_true',
582 default = False,
583 dest = 'log',
584 help = 'run in logging mode (like vmstat)',
586 options.add_option('-t', '--tracepoints',
587 action = 'store_true',
588 default = False,
589 dest = 'tracepoints',
590 help = 'retrieve statistics from tracepoints',
592 options.add_option('-d', '--debugfs',
593 action = 'store_true',
594 default = False,
595 dest = 'debugfs',
596 help = 'retrieve statistics from debugfs',
598 options.add_option('-f', '--fields',
599 action = 'store',
600 default = None,
601 dest = 'fields',
602 help = 'fields to display (regex)',
604 (options, args) = options.parse_args(sys.argv)
606 providers = []
607 if options.tracepoints:
608 providers.append(TracepointProvider())
609 if options.debugfs:
610 providers.append(DebugfsProvider())
612 if len(providers) == 0:
613 try:
614 providers = [TracepointProvider()]
615 except:
616 providers = [DebugfsProvider()]
618 stats = Stats(providers, fields = options.fields)
620 if options.log:
621 log(stats)
622 elif not options.once:
623 curses.wrapper(tui, stats)
624 else:
625 batch(stats)