scripts/kvm/kvm_stat: Improve debugfs access checking
[qemu/kevin.git] / scripts / kvm / kvm_stat
blob6f0692d60a520c4d61f9e1858076e26bcbf76087
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._fields = walkdir(PATH_DEBUGFS_KVM)[2]
29 def fields(self):
30 return self._fields
31 def select(self, fields):
32 self._fields = fields
33 def read(self):
34 def val(key):
35 return int(file(PATH_DEBUGFS_KVM + '/' + key).read())
36 return dict([(key, val(key)) for key in self._fields])
38 VMX_EXIT_REASONS = {
39 'EXCEPTION_NMI': 0,
40 'EXTERNAL_INTERRUPT': 1,
41 'TRIPLE_FAULT': 2,
42 'PENDING_INTERRUPT': 7,
43 'NMI_WINDOW': 8,
44 'TASK_SWITCH': 9,
45 'CPUID': 10,
46 'HLT': 12,
47 'INVLPG': 14,
48 'RDPMC': 15,
49 'RDTSC': 16,
50 'VMCALL': 18,
51 'VMCLEAR': 19,
52 'VMLAUNCH': 20,
53 'VMPTRLD': 21,
54 'VMPTRST': 22,
55 'VMREAD': 23,
56 'VMRESUME': 24,
57 'VMWRITE': 25,
58 'VMOFF': 26,
59 'VMON': 27,
60 'CR_ACCESS': 28,
61 'DR_ACCESS': 29,
62 'IO_INSTRUCTION': 30,
63 'MSR_READ': 31,
64 'MSR_WRITE': 32,
65 'INVALID_STATE': 33,
66 'MWAIT_INSTRUCTION': 36,
67 'MONITOR_INSTRUCTION': 39,
68 'PAUSE_INSTRUCTION': 40,
69 'MCE_DURING_VMENTRY': 41,
70 'TPR_BELOW_THRESHOLD': 43,
71 'APIC_ACCESS': 44,
72 'EPT_VIOLATION': 48,
73 'EPT_MISCONFIG': 49,
74 'WBINVD': 54,
75 'XSETBV': 55,
76 'APIC_WRITE': 56,
77 'INVPCID': 58,
80 SVM_EXIT_REASONS = {
81 'READ_CR0': 0x000,
82 'READ_CR3': 0x003,
83 'READ_CR4': 0x004,
84 'READ_CR8': 0x008,
85 'WRITE_CR0': 0x010,
86 'WRITE_CR3': 0x013,
87 'WRITE_CR4': 0x014,
88 'WRITE_CR8': 0x018,
89 'READ_DR0': 0x020,
90 'READ_DR1': 0x021,
91 'READ_DR2': 0x022,
92 'READ_DR3': 0x023,
93 'READ_DR4': 0x024,
94 'READ_DR5': 0x025,
95 'READ_DR6': 0x026,
96 'READ_DR7': 0x027,
97 'WRITE_DR0': 0x030,
98 'WRITE_DR1': 0x031,
99 'WRITE_DR2': 0x032,
100 'WRITE_DR3': 0x033,
101 'WRITE_DR4': 0x034,
102 'WRITE_DR5': 0x035,
103 'WRITE_DR6': 0x036,
104 'WRITE_DR7': 0x037,
105 'EXCP_BASE': 0x040,
106 'INTR': 0x060,
107 'NMI': 0x061,
108 'SMI': 0x062,
109 'INIT': 0x063,
110 'VINTR': 0x064,
111 'CR0_SEL_WRITE': 0x065,
112 'IDTR_READ': 0x066,
113 'GDTR_READ': 0x067,
114 'LDTR_READ': 0x068,
115 'TR_READ': 0x069,
116 'IDTR_WRITE': 0x06a,
117 'GDTR_WRITE': 0x06b,
118 'LDTR_WRITE': 0x06c,
119 'TR_WRITE': 0x06d,
120 'RDTSC': 0x06e,
121 'RDPMC': 0x06f,
122 'PUSHF': 0x070,
123 'POPF': 0x071,
124 'CPUID': 0x072,
125 'RSM': 0x073,
126 'IRET': 0x074,
127 'SWINT': 0x075,
128 'INVD': 0x076,
129 'PAUSE': 0x077,
130 'HLT': 0x078,
131 'INVLPG': 0x079,
132 'INVLPGA': 0x07a,
133 'IOIO': 0x07b,
134 'MSR': 0x07c,
135 'TASK_SWITCH': 0x07d,
136 'FERR_FREEZE': 0x07e,
137 'SHUTDOWN': 0x07f,
138 'VMRUN': 0x080,
139 'VMMCALL': 0x081,
140 'VMLOAD': 0x082,
141 'VMSAVE': 0x083,
142 'STGI': 0x084,
143 'CLGI': 0x085,
144 'SKINIT': 0x086,
145 'RDTSCP': 0x087,
146 'ICEBP': 0x088,
147 'WBINVD': 0x089,
148 'MONITOR': 0x08a,
149 'MWAIT': 0x08b,
150 'MWAIT_COND': 0x08c,
151 'XSETBV': 0x08d,
152 'NPF': 0x400,
155 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
156 AARCH64_EXIT_REASONS = {
157 'UNKNOWN': 0x00,
158 'WFI': 0x01,
159 'CP15_32': 0x03,
160 'CP15_64': 0x04,
161 'CP14_MR': 0x05,
162 'CP14_LS': 0x06,
163 'FP_ASIMD': 0x07,
164 'CP10_ID': 0x08,
165 'CP14_64': 0x0C,
166 'ILL_ISS': 0x0E,
167 'SVC32': 0x11,
168 'HVC32': 0x12,
169 'SMC32': 0x13,
170 'SVC64': 0x15,
171 'HVC64': 0x16,
172 'SMC64': 0x17,
173 'SYS64': 0x18,
174 'IABT': 0x20,
175 'IABT_HYP': 0x21,
176 'PC_ALIGN': 0x22,
177 'DABT': 0x24,
178 'DABT_HYP': 0x25,
179 'SP_ALIGN': 0x26,
180 'FP_EXC32': 0x28,
181 'FP_EXC64': 0x2C,
182 'SERROR': 0x2F,
183 'BREAKPT': 0x30,
184 'BREAKPT_HYP': 0x31,
185 'SOFTSTP': 0x32,
186 'SOFTSTP_HYP': 0x33,
187 'WATCHPT': 0x34,
188 'WATCHPT_HYP': 0x35,
189 'BKPT32': 0x38,
190 'VECTOR32': 0x3A,
191 'BRK64': 0x3C,
194 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
195 USERSPACE_EXIT_REASONS = {
196 'UNKNOWN': 0,
197 'EXCEPTION': 1,
198 'IO': 2,
199 'HYPERCALL': 3,
200 'DEBUG': 4,
201 'HLT': 5,
202 'MMIO': 6,
203 'IRQ_WINDOW_OPEN': 7,
204 'SHUTDOWN': 8,
205 'FAIL_ENTRY': 9,
206 'INTR': 10,
207 'SET_TPR': 11,
208 'TPR_ACCESS': 12,
209 'S390_SIEIC': 13,
210 'S390_RESET': 14,
211 'DCR': 15,
212 'NMI': 16,
213 'INTERNAL_ERROR': 17,
214 'OSI': 18,
215 'PAPR_HCALL': 19,
216 'S390_UCONTROL': 20,
217 'WATCHDOG': 21,
218 'S390_TSCH': 22,
219 'EPR': 23,
220 'SYSTEM_EVENT': 24,
223 X86_EXIT_REASONS = {
224 'vmx': VMX_EXIT_REASONS,
225 'svm': SVM_EXIT_REASONS,
228 SC_PERF_EVT_OPEN = None
229 EXIT_REASONS = None
231 IOCTL_NUMBERS = {
232 'SET_FILTER' : 0x40082406,
233 'ENABLE' : 0x00002400,
234 'DISABLE' : 0x00002401,
235 'RESET' : 0x00002403,
238 def x86_init(flag):
239 global SC_PERF_EVT_OPEN
240 global EXIT_REASONS
242 SC_PERF_EVT_OPEN = 298
243 EXIT_REASONS = X86_EXIT_REASONS[flag]
245 def s390_init():
246 global SC_PERF_EVT_OPEN
248 SC_PERF_EVT_OPEN = 331
250 def ppc_init():
251 global SC_PERF_EVT_OPEN
252 global IOCTL_NUMBERS
254 SC_PERF_EVT_OPEN = 319
256 IOCTL_NUMBERS['ENABLE'] = 0x20002400
257 IOCTL_NUMBERS['DISABLE'] = 0x20002401
258 IOCTL_NUMBERS['SET_FILTER'] = 0x80002406 | (ctypes.sizeof(ctypes.c_char_p)
259 << 16)
261 def aarch64_init():
262 global SC_PERF_EVT_OPEN
263 global EXIT_REASONS
265 SC_PERF_EVT_OPEN = 241
266 EXIT_REASONS = AARCH64_EXIT_REASONS
268 def detect_platform():
269 if os.uname()[4].startswith('ppc'):
270 ppc_init()
271 return
272 elif os.uname()[4].startswith('aarch64'):
273 aarch64_init()
274 return
276 for line in file('/proc/cpuinfo').readlines():
277 if line.startswith('flags'):
278 for flag in line.split():
279 if flag in X86_EXIT_REASONS:
280 x86_init(flag)
281 return
282 elif line.startswith('vendor_id'):
283 for flag in line.split():
284 if flag == 'IBM/S390':
285 s390_init()
286 return
288 detect_platform()
291 def walkdir(path):
292 """Returns os.walk() data for specified directory.
294 As it is only a wrapper it returns the same 3-tuple of (dirpath,
295 dirnames, filenames).
297 return next(os.walk(path))
299 filters = {}
300 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
301 if EXIT_REASONS:
302 filters['kvm_exit'] = ('exit_reason', EXIT_REASONS)
304 libc = ctypes.CDLL('libc.so.6')
305 syscall = libc.syscall
306 get_errno = libc.__errno_location
307 get_errno.restype = ctypes.POINTER(ctypes.c_int)
309 class perf_event_attr(ctypes.Structure):
310 _fields_ = [('type', ctypes.c_uint32),
311 ('size', ctypes.c_uint32),
312 ('config', ctypes.c_uint64),
313 ('sample_freq', ctypes.c_uint64),
314 ('sample_type', ctypes.c_uint64),
315 ('read_format', ctypes.c_uint64),
316 ('flags', ctypes.c_uint64),
317 ('wakeup_events', ctypes.c_uint32),
318 ('bp_type', ctypes.c_uint32),
319 ('bp_addr', ctypes.c_uint64),
320 ('bp_len', ctypes.c_uint64),
322 def _perf_event_open(attr, pid, cpu, group_fd, flags):
323 return syscall(SC_PERF_EVT_OPEN, ctypes.pointer(attr), ctypes.c_int(pid),
324 ctypes.c_int(cpu), ctypes.c_int(group_fd),
325 ctypes.c_long(flags))
327 PERF_TYPE_TRACEPOINT = 2
328 PERF_FORMAT_GROUP = 1 << 3
330 PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
331 PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
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(PATH_DEBUGFS_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(PATH_DEBUGFS_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.path.exists('/sys/kernel/debug'):
477 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
478 sys.exit(1)
479 if not os.path.exists(PATH_DEBUGFS_KVM):
480 sys.stderr.write("Please make sure, that debugfs is mounted and "
481 "readable by the current user:\n"
482 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
483 "Also ensure, that the kvm modules are loaded.\n")
484 sys.exit(1)
485 if not os.path.exists(PATH_DEBUGFS_TRACING):
486 sys.stderr.write("Please make {0} readable by the current user.\n"
487 .format(PATH_DEBUGFS_TRACING))
488 sys.exit(1)
490 LABEL_WIDTH = 40
491 NUMBER_WIDTH = 10
493 def tui(screen, stats):
494 curses.use_default_colors()
495 curses.noecho()
496 drilldown = False
497 fields_filter = stats.fields_filter
498 def update_drilldown():
499 if not fields_filter:
500 if drilldown:
501 stats.set_fields_filter(None)
502 else:
503 stats.set_fields_filter(r'^[^\(]*$')
504 update_drilldown()
505 def refresh(sleeptime):
506 screen.erase()
507 screen.addstr(0, 0, 'kvm statistics')
508 screen.addstr(2, 1, 'Event')
509 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
510 screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
511 row = 3
512 s = stats.get()
513 def sortkey(x):
514 if s[x][1]:
515 return (-s[x][1], -s[x][0])
516 else:
517 return (0, -s[x][0])
518 for key in sorted(s.keys(), key = sortkey):
519 if row >= screen.getmaxyx()[0]:
520 break
521 values = s[key]
522 if not values[0] and not values[1]:
523 break
524 col = 1
525 screen.addstr(row, col, key)
526 col += LABEL_WIDTH
527 screen.addstr(row, col, '%10d' % (values[0],))
528 col += NUMBER_WIDTH
529 if values[1] is not None:
530 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
531 row += 1
532 screen.refresh()
534 sleeptime = 0.25
535 while True:
536 refresh(sleeptime)
537 curses.halfdelay(int(sleeptime * 10))
538 sleeptime = 3
539 try:
540 c = screen.getkey()
541 if c == 'x':
542 drilldown = not drilldown
543 update_drilldown()
544 if c == 'q':
545 break
546 except KeyboardInterrupt:
547 break
548 except curses.error:
549 continue
551 def batch(stats):
552 s = stats.get()
553 time.sleep(1)
554 s = stats.get()
555 for key in sorted(s.keys()):
556 values = s[key]
557 print '%-22s%10d%10d' % (key, values[0], values[1])
559 def log(stats):
560 keys = sorted(stats.get().iterkeys())
561 def banner():
562 for k in keys:
563 print '%10s' % k[0:9],
564 print
565 def statline():
566 s = stats.get()
567 for k in keys:
568 print ' %9d' % s[k][1],
569 print
570 line = 0
571 banner_repeat = 20
572 while True:
573 time.sleep(1)
574 if line % banner_repeat == 0:
575 banner()
576 statline()
577 line += 1
579 options = optparse.OptionParser()
580 options.add_option('-1', '--once', '--batch',
581 action = 'store_true',
582 default = False,
583 dest = 'once',
584 help = 'run in batch mode for one second',
586 options.add_option('-l', '--log',
587 action = 'store_true',
588 default = False,
589 dest = 'log',
590 help = 'run in logging mode (like vmstat)',
592 options.add_option('-t', '--tracepoints',
593 action = 'store_true',
594 default = False,
595 dest = 'tracepoints',
596 help = 'retrieve statistics from tracepoints',
598 options.add_option('-d', '--debugfs',
599 action = 'store_true',
600 default = False,
601 dest = 'debugfs',
602 help = 'retrieve statistics from debugfs',
604 options.add_option('-f', '--fields',
605 action = 'store',
606 default = None,
607 dest = 'fields',
608 help = 'fields to display (regex)',
610 (options, args) = options.parse_args(sys.argv)
612 providers = []
613 if options.tracepoints:
614 providers.append(TracepointProvider())
615 if options.debugfs:
616 providers.append(DebugfsProvider())
618 if len(providers) == 0:
619 try:
620 providers = [TracepointProvider()]
621 except:
622 providers = [DebugfsProvider()]
624 stats = Stats(providers, fields = options.fields)
626 if options.log:
627 log(stats)
628 elif not options.once:
629 curses.wrapper(tui, stats)
630 else:
631 batch(stats)