scripts/kvm/kvm_stat: Group arch specific data
[qemu/ar7.git] / scripts / kvm / kvm_stat
blob42d35f5d95786cb1225cfe04af064894b3a81545
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 VMX_EXIT_REASONS = {
27 'EXCEPTION_NMI': 0,
28 'EXTERNAL_INTERRUPT': 1,
29 'TRIPLE_FAULT': 2,
30 'PENDING_INTERRUPT': 7,
31 'NMI_WINDOW': 8,
32 'TASK_SWITCH': 9,
33 'CPUID': 10,
34 'HLT': 12,
35 'INVLPG': 14,
36 'RDPMC': 15,
37 'RDTSC': 16,
38 'VMCALL': 18,
39 'VMCLEAR': 19,
40 'VMLAUNCH': 20,
41 'VMPTRLD': 21,
42 'VMPTRST': 22,
43 'VMREAD': 23,
44 'VMRESUME': 24,
45 'VMWRITE': 25,
46 'VMOFF': 26,
47 'VMON': 27,
48 'CR_ACCESS': 28,
49 'DR_ACCESS': 29,
50 'IO_INSTRUCTION': 30,
51 'MSR_READ': 31,
52 'MSR_WRITE': 32,
53 'INVALID_STATE': 33,
54 'MWAIT_INSTRUCTION': 36,
55 'MONITOR_INSTRUCTION': 39,
56 'PAUSE_INSTRUCTION': 40,
57 'MCE_DURING_VMENTRY': 41,
58 'TPR_BELOW_THRESHOLD': 43,
59 'APIC_ACCESS': 44,
60 'EPT_VIOLATION': 48,
61 'EPT_MISCONFIG': 49,
62 'WBINVD': 54,
63 'XSETBV': 55,
64 'APIC_WRITE': 56,
65 'INVPCID': 58,
68 SVM_EXIT_REASONS = {
69 'READ_CR0': 0x000,
70 'READ_CR3': 0x003,
71 'READ_CR4': 0x004,
72 'READ_CR8': 0x008,
73 'WRITE_CR0': 0x010,
74 'WRITE_CR3': 0x013,
75 'WRITE_CR4': 0x014,
76 'WRITE_CR8': 0x018,
77 'READ_DR0': 0x020,
78 'READ_DR1': 0x021,
79 'READ_DR2': 0x022,
80 'READ_DR3': 0x023,
81 'READ_DR4': 0x024,
82 'READ_DR5': 0x025,
83 'READ_DR6': 0x026,
84 'READ_DR7': 0x027,
85 'WRITE_DR0': 0x030,
86 'WRITE_DR1': 0x031,
87 'WRITE_DR2': 0x032,
88 'WRITE_DR3': 0x033,
89 'WRITE_DR4': 0x034,
90 'WRITE_DR5': 0x035,
91 'WRITE_DR6': 0x036,
92 'WRITE_DR7': 0x037,
93 'EXCP_BASE': 0x040,
94 'INTR': 0x060,
95 'NMI': 0x061,
96 'SMI': 0x062,
97 'INIT': 0x063,
98 'VINTR': 0x064,
99 'CR0_SEL_WRITE': 0x065,
100 'IDTR_READ': 0x066,
101 'GDTR_READ': 0x067,
102 'LDTR_READ': 0x068,
103 'TR_READ': 0x069,
104 'IDTR_WRITE': 0x06a,
105 'GDTR_WRITE': 0x06b,
106 'LDTR_WRITE': 0x06c,
107 'TR_WRITE': 0x06d,
108 'RDTSC': 0x06e,
109 'RDPMC': 0x06f,
110 'PUSHF': 0x070,
111 'POPF': 0x071,
112 'CPUID': 0x072,
113 'RSM': 0x073,
114 'IRET': 0x074,
115 'SWINT': 0x075,
116 'INVD': 0x076,
117 'PAUSE': 0x077,
118 'HLT': 0x078,
119 'INVLPG': 0x079,
120 'INVLPGA': 0x07a,
121 'IOIO': 0x07b,
122 'MSR': 0x07c,
123 'TASK_SWITCH': 0x07d,
124 'FERR_FREEZE': 0x07e,
125 'SHUTDOWN': 0x07f,
126 'VMRUN': 0x080,
127 'VMMCALL': 0x081,
128 'VMLOAD': 0x082,
129 'VMSAVE': 0x083,
130 'STGI': 0x084,
131 'CLGI': 0x085,
132 'SKINIT': 0x086,
133 'RDTSCP': 0x087,
134 'ICEBP': 0x088,
135 'WBINVD': 0x089,
136 'MONITOR': 0x08a,
137 'MWAIT': 0x08b,
138 'MWAIT_COND': 0x08c,
139 'XSETBV': 0x08d,
140 'NPF': 0x400,
143 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
144 AARCH64_EXIT_REASONS = {
145 'UNKNOWN': 0x00,
146 'WFI': 0x01,
147 'CP15_32': 0x03,
148 'CP15_64': 0x04,
149 'CP14_MR': 0x05,
150 'CP14_LS': 0x06,
151 'FP_ASIMD': 0x07,
152 'CP10_ID': 0x08,
153 'CP14_64': 0x0C,
154 'ILL_ISS': 0x0E,
155 'SVC32': 0x11,
156 'HVC32': 0x12,
157 'SMC32': 0x13,
158 'SVC64': 0x15,
159 'HVC64': 0x16,
160 'SMC64': 0x17,
161 'SYS64': 0x18,
162 'IABT': 0x20,
163 'IABT_HYP': 0x21,
164 'PC_ALIGN': 0x22,
165 'DABT': 0x24,
166 'DABT_HYP': 0x25,
167 'SP_ALIGN': 0x26,
168 'FP_EXC32': 0x28,
169 'FP_EXC64': 0x2C,
170 'SERROR': 0x2F,
171 'BREAKPT': 0x30,
172 'BREAKPT_HYP': 0x31,
173 'SOFTSTP': 0x32,
174 'SOFTSTP_HYP': 0x33,
175 'WATCHPT': 0x34,
176 'WATCHPT_HYP': 0x35,
177 'BKPT32': 0x38,
178 'VECTOR32': 0x3A,
179 'BRK64': 0x3C,
182 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
183 USERSPACE_EXIT_REASONS = {
184 'UNKNOWN': 0,
185 'EXCEPTION': 1,
186 'IO': 2,
187 'HYPERCALL': 3,
188 'DEBUG': 4,
189 'HLT': 5,
190 'MMIO': 6,
191 'IRQ_WINDOW_OPEN': 7,
192 'SHUTDOWN': 8,
193 'FAIL_ENTRY': 9,
194 'INTR': 10,
195 'SET_TPR': 11,
196 'TPR_ACCESS': 12,
197 'S390_SIEIC': 13,
198 'S390_RESET': 14,
199 'DCR': 15,
200 'NMI': 16,
201 'INTERNAL_ERROR': 17,
202 'OSI': 18,
203 'PAPR_HCALL': 19,
204 'S390_UCONTROL': 20,
205 'WATCHDOG': 21,
206 'S390_TSCH': 22,
207 'EPR': 23,
208 'SYSTEM_EVENT': 24,
211 X86_EXIT_REASONS = {
212 'vmx': VMX_EXIT_REASONS,
213 'svm': SVM_EXIT_REASONS,
216 IOCTL_NUMBERS = {
217 'SET_FILTER': 0x40082406,
218 'ENABLE': 0x00002400,
219 'DISABLE': 0x00002401,
220 'RESET': 0x00002403,
223 class Arch(object):
224 """Class that encapsulates global architecture specific data like
225 syscall and ioctl numbers.
228 @staticmethod
229 def get_arch():
230 machine = os.uname()[4]
232 if machine.startswith('ppc'):
233 return ArchPPC()
234 elif machine.startswith('aarch64'):
235 return ArchA64()
236 elif machine.startswith('s390'):
237 return ArchS390()
238 else:
239 # X86_64
240 for line in open('/proc/cpuinfo'):
241 if not line.startswith('flags'):
242 continue
244 flags = line.split()
245 if 'vmx' in flags:
246 return ArchX86(VMX_EXIT_REASONS)
247 if 'svm' in flags:
248 return ArchX86(SVM_EXIT_REASONS)
249 return
251 class ArchX86(Arch):
252 def __init__(self, exit_reasons):
253 self.sc_perf_evt_open = 298
254 self.ioctl_numbers = IOCTL_NUMBERS
255 self.exit_reasons = exit_reasons
257 class ArchPPC(Arch):
258 def __init__(self):
259 self.sc_perf_evt_open = 319
260 self.ioctl_numbers = IOCTL_NUMBERS
261 self.ioctl_numbers['ENABLE'] = 0x20002400
262 self.ioctl_numbers['DISABLE'] = 0x20002401
264 # PPC comes in 32 and 64 bit and some generated ioctl
265 # numbers depend on the wordsize.
266 char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
267 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
269 class ArchA64(Arch):
270 def __init__(self):
271 self.sc_perf_evt_open = 241
272 self.ioctl_numbers = IOCTL_NUMBERS
273 self.exit_reasons = AARCH64_EXIT_REASONS
275 class ArchS390(Arch):
276 def __init__(self):
277 self.sc_perf_evt_open = 331
278 self.ioctl_numbers = IOCTL_NUMBERS
279 self.exit_reasons = None
281 ARCH = Arch.get_arch()
284 def walkdir(path):
285 """Returns os.walk() data for specified directory.
287 As it is only a wrapper it returns the same 3-tuple of (dirpath,
288 dirnames, filenames).
290 return next(os.walk(path))
293 def parse_int_list(list_string):
294 """Returns an int list from a string of comma separated integers and
295 integer ranges."""
296 integers = []
297 members = list_string.split(',')
299 for member in members:
300 if '-' not in member:
301 integers.append(int(member))
302 else:
303 int_range = member.split('-')
304 integers.extend(range(int(int_range[0]),
305 int(int_range[1]) + 1))
307 return integers
310 def get_online_cpus():
311 with open('/sys/devices/system/cpu/online') as cpu_list:
312 cpu_string = cpu_list.readline()
313 return parse_int_list(cpu_string)
316 def get_filters():
317 filters = {}
318 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
319 if ARCH.exit_reasons:
320 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
321 return filters
323 libc = ctypes.CDLL('libc.so.6', use_errno=True)
324 syscall = libc.syscall
326 class perf_event_attr(ctypes.Structure):
327 _fields_ = [('type', ctypes.c_uint32),
328 ('size', ctypes.c_uint32),
329 ('config', ctypes.c_uint64),
330 ('sample_freq', ctypes.c_uint64),
331 ('sample_type', ctypes.c_uint64),
332 ('read_format', ctypes.c_uint64),
333 ('flags', ctypes.c_uint64),
334 ('wakeup_events', ctypes.c_uint32),
335 ('bp_type', ctypes.c_uint32),
336 ('bp_addr', ctypes.c_uint64),
337 ('bp_len', ctypes.c_uint64),
339 def perf_event_open(attr, pid, cpu, group_fd, flags):
340 return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
341 ctypes.c_int(pid), ctypes.c_int(cpu),
342 ctypes.c_int(group_fd), ctypes.c_long(flags))
344 PERF_TYPE_TRACEPOINT = 2
345 PERF_FORMAT_GROUP = 1 << 3
347 PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
348 PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
350 class Group(object):
351 def __init__(self):
352 self.events = []
354 def add_event(self, event):
355 self.events.append(event)
357 def read(self):
358 length = 8 * (1 + len(self.events))
359 read_format = 'xxxxxxxx' + 'q' * len(self.events)
360 return dict(zip([event.name for event in self.events],
361 struct.unpack(read_format,
362 os.read(self.events[0].fd, length))))
364 class Event(object):
365 def __init__(self, name, group, trace_cpu, trace_point, trace_filter,
366 trace_set='kvm'):
367 self.name = name
368 self.fd = None
369 self.setup_event(group, trace_cpu, trace_point, trace_filter,
370 trace_set)
372 def setup_event_attribute(self, trace_set, trace_point):
373 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
374 trace_point, 'id')
376 event_attr = perf_event_attr()
377 event_attr.type = PERF_TYPE_TRACEPOINT
378 event_attr.size = ctypes.sizeof(event_attr)
379 event_attr.config = int(open(id_path).read())
380 event_attr.sample_period = 1
381 event_attr.read_format = PERF_FORMAT_GROUP
382 return event_attr
384 def setup_event(self, group, trace_cpu, trace_point, trace_filter,
385 trace_set):
386 event_attr = self.setup_event_attribute(trace_set, trace_point)
388 group_leader = -1
389 if group.events:
390 group_leader = group.events[0].fd
392 fd = perf_event_open(event_attr, -1, trace_cpu,
393 group_leader, 0)
394 if fd == -1:
395 err = ctypes.get_errno()
396 raise OSError(err, os.strerror(err),
397 'while calling sys_perf_event_open().')
399 if trace_filter:
400 fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
401 trace_filter)
403 self.fd = fd
405 def enable(self):
406 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
408 def disable(self):
409 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
411 def reset(self):
412 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
414 class TracepointProvider(object):
415 def __init__(self):
416 self.group_leaders = []
417 self.filters = get_filters()
418 self._fields = self.get_available_fields()
419 self.setup_traces()
420 self.fields = self._fields
422 def get_available_fields(self):
423 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
424 fields = walkdir(path)[1]
425 extra = []
426 for field in fields:
427 if field in self.filters:
428 filter_name_, filter_dicts = self.filters[field]
429 for name in filter_dicts:
430 extra.append(field + '(' + name + ')')
431 fields += extra
432 return fields
434 def setup_traces(self):
435 cpus = get_online_cpus()
437 # The constant is needed as a buffer for python libs, std
438 # streams and other files that the script opens.
439 rlimit = len(cpus) * len(self._fields) + 50
440 try:
441 resource.setrlimit(resource.RLIMIT_NOFILE, (rlimit, rlimit))
442 except ValueError:
443 sys.exit("NOFILE rlimit could not be raised to {0}".format(rlimit))
445 for cpu in cpus:
446 group = Group()
447 for name in self._fields:
448 tracepoint = name
449 tracefilter = None
450 match = re.match(r'(.*)\((.*)\)', name)
451 if match:
452 tracepoint, sub = match.groups()
453 tracefilter = ('%s==%d\0' %
454 (self.filters[tracepoint][0],
455 self.filters[tracepoint][1][sub]))
457 group.add_event(Event(name=name,
458 group=group,
459 trace_cpu=cpu,
460 trace_point=tracepoint,
461 trace_filter=tracefilter))
462 self.group_leaders.append(group)
464 @property
465 def fields(self):
466 return self._fields
468 @fields.setter
469 def fields(self, fields):
470 self._fields = fields
471 for group in self.group_leaders:
472 for event in group.events:
473 if event.name in fields:
474 event.reset()
475 event.enable()
476 else:
477 event.disable()
479 def read(self):
480 ret = defaultdict(int)
481 for group in self.group_leaders:
482 for name, val in group.read().iteritems():
483 ret[name] += val
484 return ret
486 class DebugfsProvider(object):
487 def __init__(self):
488 self._fields = walkdir(PATH_DEBUGFS_KVM)[2]
490 @property
491 def fields(self):
492 return self._fields
494 @fields.setter
495 def fields(self, fields):
496 self._fields = fields
498 def read(self):
499 def val(key):
500 return int(file(PATH_DEBUGFS_KVM + '/' + key).read())
501 return dict([(key, val(key)) for key in self._fields])
503 class Stats(object):
504 def __init__(self, providers, fields=None):
505 self.providers = providers
506 self._fields_filter = fields
507 self.values = {}
508 self.update_provider_filters()
510 def update_provider_filters(self):
511 def wanted(key):
512 if not self._fields_filter:
513 return True
514 return re.match(self._fields_filter, key) is not None
516 # As we reset the counters when updating the fields we can
517 # also clear the cache of old values.
518 self.values = {}
519 for provider in self.providers:
520 provider_fields = [key for key in provider.fields if wanted(key)]
521 provider.fields = provider_fields
523 @property
524 def fields_filter(self):
525 return self._fields_filter
527 @fields_filter.setter
528 def fields_filter(self, fields_filter):
529 self._fields_filter = fields_filter
530 self.update_provider_filters()
532 def get(self):
533 for provider in self.providers:
534 new = provider.read()
535 for key in provider.fields:
536 oldval = self.values.get(key, (0, 0))
537 newval = new.get(key, 0)
538 newdelta = None
539 if oldval is not None:
540 newdelta = newval - oldval[0]
541 self.values[key] = (newval, newdelta)
542 return self.values
544 LABEL_WIDTH = 40
545 NUMBER_WIDTH = 10
547 def tui(screen, stats):
548 curses.use_default_colors()
549 curses.noecho()
550 drilldown = False
551 fields_filter = stats.fields_filter
552 def update_drilldown():
553 if not fields_filter:
554 if drilldown:
555 stats.fields_filter = None
556 else:
557 stats.fields_filter = r'^[^\(]*$'
558 update_drilldown()
559 def refresh(sleeptime):
560 screen.erase()
561 screen.addstr(0, 0, 'kvm statistics')
562 screen.addstr(2, 1, 'Event')
563 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
564 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
565 row = 3
566 s = stats.get()
567 def sortkey(x):
568 if s[x][1]:
569 return (-s[x][1], -s[x][0])
570 else:
571 return (0, -s[x][0])
572 for key in sorted(s.keys(), key=sortkey):
573 if row >= screen.getmaxyx()[0]:
574 break
575 values = s[key]
576 if not values[0] and not values[1]:
577 break
578 col = 1
579 screen.addstr(row, col, key)
580 col += LABEL_WIDTH
581 screen.addstr(row, col, '%10d' % (values[0],))
582 col += NUMBER_WIDTH
583 if values[1] is not None:
584 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
585 row += 1
586 screen.refresh()
588 sleeptime = 0.25
589 while True:
590 refresh(sleeptime)
591 curses.halfdelay(int(sleeptime * 10))
592 sleeptime = 3
593 try:
594 c = screen.getkey()
595 if c == 'x':
596 drilldown = not drilldown
597 update_drilldown()
598 if c == 'q':
599 break
600 except KeyboardInterrupt:
601 break
602 except curses.error:
603 continue
605 def batch(stats):
606 s = stats.get()
607 time.sleep(1)
608 s = stats.get()
609 for key in sorted(s.keys()):
610 values = s[key]
611 print '%-22s%10d%10d' % (key, values[0], values[1])
613 def log(stats):
614 keys = sorted(stats.get().iterkeys())
615 def banner():
616 for k in keys:
617 print '%10s' % k[0:9],
618 print
619 def statline():
620 s = stats.get()
621 for k in keys:
622 print ' %9d' % s[k][1],
623 print
624 line = 0
625 banner_repeat = 20
626 while True:
627 time.sleep(1)
628 if line % banner_repeat == 0:
629 banner()
630 statline()
631 line += 1
633 def get_options():
634 optparser = optparse.OptionParser()
635 optparser.add_option('-1', '--once', '--batch',
636 action='store_true',
637 default=False,
638 dest='once',
639 help='run in batch mode for one second',
641 optparser.add_option('-l', '--log',
642 action='store_true',
643 default=False,
644 dest='log',
645 help='run in logging mode (like vmstat)',
647 optparser.add_option('-t', '--tracepoints',
648 action='store_true',
649 default=False,
650 dest='tracepoints',
651 help='retrieve statistics from tracepoints',
653 optparser.add_option('-d', '--debugfs',
654 action='store_true',
655 default=False,
656 dest='debugfs',
657 help='retrieve statistics from debugfs',
659 optparser.add_option('-f', '--fields',
660 action='store',
661 default=None,
662 dest='fields',
663 help='fields to display (regex)',
665 (options, _) = optparser.parse_args(sys.argv)
666 return options
668 def get_providers(options):
669 providers = []
671 if options.tracepoints:
672 providers.append(TracepointProvider())
673 if options.debugfs:
674 providers.append(DebugfsProvider())
675 if len(providers) == 0:
676 providers.append(TracepointProvider())
678 return providers
680 def check_access():
681 if not os.path.exists('/sys/kernel/debug'):
682 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
683 sys.exit(1)
685 if not os.path.exists(PATH_DEBUGFS_KVM):
686 sys.stderr.write("Please make sure, that debugfs is mounted and "
687 "readable by the current user:\n"
688 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
689 "Also ensure, that the kvm modules are loaded.\n")
690 sys.exit(1)
692 if not os.path.exists(PATH_DEBUGFS_TRACING):
693 sys.stderr.write("Please make {0} readable by the current user.\n"
694 .format(PATH_DEBUGFS_TRACING))
695 sys.exit(1)
697 def main():
698 check_access()
699 options = get_options()
700 providers = get_providers(options)
701 stats = Stats(providers, fields=options.fields)
703 if options.log:
704 log(stats)
705 elif not options.once:
706 curses.wrapper(tui, stats)
707 else:
708 batch(stats)
710 if __name__ == "__main__":
711 main()