blob: 95c650b1c3924152864f6ef2abd3d6ef5445e929 [file] [log] [blame]
Jan Kiszka626c4272011-10-07 09:37:49 +02001#!/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 <avi@redhat.com>
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
14import curses
Michael Ellerman47253982014-06-17 17:54:35 +100015import sys, os, time, optparse, ctypes
Jan Kiszka626c4272011-10-07 09:37:49 +020016
17class 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
30vmx_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',
Wei Huang2c9d5352015-01-21 16:15:30 -050068 56: 'APIC_WRITE',
69 58: 'INVPCID',
Jan Kiszka626c4272011-10-07 09:37:49 +020070}
71
72svm_exit_reasons = {
73 0x000: 'READ_CR0',
74 0x003: 'READ_CR3',
75 0x004: 'READ_CR4',
76 0x008: 'READ_CR8',
77 0x010: 'WRITE_CR0',
78 0x013: 'WRITE_CR3',
79 0x014: 'WRITE_CR4',
80 0x018: 'WRITE_CR8',
81 0x020: 'READ_DR0',
82 0x021: 'READ_DR1',
83 0x022: 'READ_DR2',
84 0x023: 'READ_DR3',
85 0x024: 'READ_DR4',
86 0x025: 'READ_DR5',
87 0x026: 'READ_DR6',
88 0x027: 'READ_DR7',
89 0x030: 'WRITE_DR0',
90 0x031: 'WRITE_DR1',
91 0x032: 'WRITE_DR2',
92 0x033: 'WRITE_DR3',
93 0x034: 'WRITE_DR4',
94 0x035: 'WRITE_DR5',
95 0x036: 'WRITE_DR6',
96 0x037: 'WRITE_DR7',
97 0x040: 'EXCP_BASE',
98 0x060: 'INTR',
99 0x061: 'NMI',
100 0x062: 'SMI',
101 0x063: 'INIT',
102 0x064: 'VINTR',
103 0x065: 'CR0_SEL_WRITE',
104 0x066: 'IDTR_READ',
105 0x067: 'GDTR_READ',
106 0x068: 'LDTR_READ',
107 0x069: 'TR_READ',
108 0x06a: 'IDTR_WRITE',
109 0x06b: 'GDTR_WRITE',
110 0x06c: 'LDTR_WRITE',
111 0x06d: 'TR_WRITE',
112 0x06e: 'RDTSC',
113 0x06f: 'RDPMC',
114 0x070: 'PUSHF',
115 0x071: 'POPF',
116 0x072: 'CPUID',
117 0x073: 'RSM',
118 0x074: 'IRET',
119 0x075: 'SWINT',
120 0x076: 'INVD',
121 0x077: 'PAUSE',
122 0x078: 'HLT',
123 0x079: 'INVLPG',
124 0x07a: 'INVLPGA',
125 0x07b: 'IOIO',
126 0x07c: 'MSR',
127 0x07d: 'TASK_SWITCH',
128 0x07e: 'FERR_FREEZE',
129 0x07f: 'SHUTDOWN',
130 0x080: 'VMRUN',
131 0x081: 'VMMCALL',
132 0x082: 'VMLOAD',
133 0x083: 'VMSAVE',
134 0x084: 'STGI',
135 0x085: 'CLGI',
136 0x086: 'SKINIT',
137 0x087: 'RDTSCP',
138 0x088: 'ICEBP',
139 0x089: 'WBINVD',
140 0x08a: 'MONITOR',
141 0x08b: 'MWAIT',
142 0x08c: 'MWAIT_COND',
Wei Huang2c9d5352015-01-21 16:15:30 -0500143 0x08d: 'XSETBV',
Jan Kiszka626c4272011-10-07 09:37:49 +0200144 0x400: 'NPF',
145}
146
Michael Ellerman27d318a2014-06-17 17:54:31 +1000147# From include/uapi/linux/kvm.h, KVM_EXIT_xxx
148userspace_exit_reasons = {
149 0: 'UNKNOWN',
150 1: 'EXCEPTION',
151 2: 'IO',
152 3: 'HYPERCALL',
153 4: 'DEBUG',
154 5: 'HLT',
155 6: 'MMIO',
156 7: 'IRQ_WINDOW_OPEN',
157 8: 'SHUTDOWN',
158 9: 'FAIL_ENTRY',
159 10: 'INTR',
160 11: 'SET_TPR',
161 12: 'TPR_ACCESS',
162 13: 'S390_SIEIC',
163 14: 'S390_RESET',
164 15: 'DCR',
165 16: 'NMI',
166 17: 'INTERNAL_ERROR',
167 18: 'OSI',
168 19: 'PAPR_HCALL',
169 20: 'S390_UCONTROL',
170 21: 'WATCHDOG',
171 22: 'S390_TSCH',
172 23: 'EPR',
Wei Huang2c9d5352015-01-21 16:15:30 -0500173 24: 'SYSTEM_EVENT',
Jens Freimannc5854ac2012-06-06 02:05:18 +0000174}
175
Michael Ellerman4d4103f2014-06-17 17:54:32 +1000176x86_exit_reasons = {
Jan Kiszka626c4272011-10-07 09:37:49 +0200177 'vmx': vmx_exit_reasons,
178 'svm': svm_exit_reasons,
179}
180
Michael Ellerman4d4103f2014-06-17 17:54:32 +1000181sc_perf_evt_open = None
Jan Kiszka626c4272011-10-07 09:37:49 +0200182exit_reasons = None
183
Michael Ellermana15d5642014-06-17 17:54:34 +1000184ioctl_numbers = {
185 'SET_FILTER' : 0x40082406,
186 'ENABLE' : 0x00002400,
187 'DISABLE' : 0x00002401,
188}
189
Michael Ellerman4d4103f2014-06-17 17:54:32 +1000190def x86_init(flag):
191 globals().update({
192 'sc_perf_evt_open' : 298,
193 'exit_reasons' : x86_exit_reasons[flag],
194 })
195
196def s390_init():
197 globals().update({
198 'sc_perf_evt_open' : 331
199 })
200
Michael Ellerman47253982014-06-17 17:54:35 +1000201def ppc_init():
202 globals().update({
203 'sc_perf_evt_open' : 319,
204 'ioctl_numbers' : {
205 'SET_FILTER' : 0x80002406 | (ctypes.sizeof(ctypes.c_char_p) << 16),
206 'ENABLE' : 0x20002400,
207 'DISABLE' : 0x20002401,
208 }
209 })
210
Wei Huangedcbc402015-01-21 16:15:29 -0500211def aarch64_init():
212 globals().update({
213 'sc_perf_evt_open' : 241
214 })
215
Michael Ellerman4d4103f2014-06-17 17:54:32 +1000216def detect_platform():
Michael Ellerman47253982014-06-17 17:54:35 +1000217 if os.uname()[4].startswith('ppc'):
218 ppc_init()
219 return
Wei Huangedcbc402015-01-21 16:15:29 -0500220 elif os.uname()[4].startswith('aarch64'):
221 aarch64_init()
222 return
Michael Ellerman47253982014-06-17 17:54:35 +1000223
Michael Ellerman4d4103f2014-06-17 17:54:32 +1000224 for line in file('/proc/cpuinfo').readlines():
225 if line.startswith('flags'):
226 for flag in line.split():
227 if flag in x86_exit_reasons:
228 x86_init(flag)
229 return
230 elif line.startswith('vendor_id'):
231 for flag in line.split():
232 if flag == 'IBM/S390':
233 s390_init()
234 return
235
236detect_platform()
Jan Kiszka626c4272011-10-07 09:37:49 +0200237
238def invert(d):
239 return dict((x[1], x[0]) for x in d.iteritems())
240
Michael Ellerman27d318a2014-06-17 17:54:31 +1000241filters = {}
242filters['kvm_userspace_exit'] = ('reason', invert(userspace_exit_reasons))
243if exit_reasons:
244 filters['kvm_exit'] = ('exit_reason', invert(exit_reasons))
Jan Kiszka626c4272011-10-07 09:37:49 +0200245
Michael Ellerman47253982014-06-17 17:54:35 +1000246import struct, array
Jan Kiszka626c4272011-10-07 09:37:49 +0200247
248libc = ctypes.CDLL('libc.so.6')
249syscall = libc.syscall
250class perf_event_attr(ctypes.Structure):
251 _fields_ = [('type', ctypes.c_uint32),
252 ('size', ctypes.c_uint32),
253 ('config', ctypes.c_uint64),
254 ('sample_freq', ctypes.c_uint64),
255 ('sample_type', ctypes.c_uint64),
256 ('read_format', ctypes.c_uint64),
257 ('flags', ctypes.c_uint64),
258 ('wakeup_events', ctypes.c_uint32),
259 ('bp_type', ctypes.c_uint32),
260 ('bp_addr', ctypes.c_uint64),
261 ('bp_len', ctypes.c_uint64),
262 ]
263def _perf_event_open(attr, pid, cpu, group_fd, flags):
Heinz Graalfs1b3e6f82012-10-29 02:13:20 +0000264 return syscall(sc_perf_evt_open, ctypes.pointer(attr), ctypes.c_int(pid),
Jan Kiszka626c4272011-10-07 09:37:49 +0200265 ctypes.c_int(cpu), ctypes.c_int(group_fd),
266 ctypes.c_long(flags))
267
268PERF_TYPE_HARDWARE = 0
269PERF_TYPE_SOFTWARE = 1
270PERF_TYPE_TRACEPOINT = 2
271PERF_TYPE_HW_CACHE = 3
272PERF_TYPE_RAW = 4
273PERF_TYPE_BREAKPOINT = 5
274
275PERF_SAMPLE_IP = 1 << 0
276PERF_SAMPLE_TID = 1 << 1
277PERF_SAMPLE_TIME = 1 << 2
278PERF_SAMPLE_ADDR = 1 << 3
279PERF_SAMPLE_READ = 1 << 4
280PERF_SAMPLE_CALLCHAIN = 1 << 5
281PERF_SAMPLE_ID = 1 << 6
282PERF_SAMPLE_CPU = 1 << 7
283PERF_SAMPLE_PERIOD = 1 << 8
284PERF_SAMPLE_STREAM_ID = 1 << 9
285PERF_SAMPLE_RAW = 1 << 10
286
287PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
288PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
289PERF_FORMAT_ID = 1 << 2
290PERF_FORMAT_GROUP = 1 << 3
291
292import re
293
294sys_tracing = '/sys/kernel/debug/tracing'
295
296class Group(object):
297 def __init__(self, cpu):
298 self.events = []
299 self.group_leader = None
300 self.cpu = cpu
301 def add_event(self, name, event_set, tracepoint, filter = None):
302 self.events.append(Event(group = self,
303 name = name, event_set = event_set,
304 tracepoint = tracepoint, filter = filter))
305 if len(self.events) == 1:
306 self.file = os.fdopen(self.events[0].fd)
307 def read(self):
308 bytes = 8 * (1 + len(self.events))
309 fmt = 'xxxxxxxx' + 'q' * len(self.events)
310 return dict(zip([event.name for event in self.events],
311 struct.unpack(fmt, self.file.read(bytes))))
312
313class Event(object):
314 def __init__(self, group, name, event_set, tracepoint, filter = None):
315 self.name = name
316 attr = perf_event_attr()
317 attr.type = PERF_TYPE_TRACEPOINT
318 attr.size = ctypes.sizeof(attr)
319 id_path = os.path.join(sys_tracing, 'events', event_set,
320 tracepoint, 'id')
321 id = int(file(id_path).read())
322 attr.config = id
323 attr.sample_type = (PERF_SAMPLE_RAW
324 | PERF_SAMPLE_TIME
325 | PERF_SAMPLE_CPU)
326 attr.sample_period = 1
327 attr.read_format = PERF_FORMAT_GROUP
328 group_leader = -1
329 if group.events:
330 group_leader = group.events[0].fd
331 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
332 if fd == -1:
333 raise Exception('perf_event_open failed')
334 if filter:
335 import fcntl
Michael Ellermana15d5642014-06-17 17:54:34 +1000336 fcntl.ioctl(fd, ioctl_numbers['SET_FILTER'], filter)
Jan Kiszka626c4272011-10-07 09:37:49 +0200337 self.fd = fd
338 def enable(self):
339 import fcntl
Michael Ellermana15d5642014-06-17 17:54:34 +1000340 fcntl.ioctl(self.fd, ioctl_numbers['ENABLE'], 0)
Jan Kiszka626c4272011-10-07 09:37:49 +0200341 def disable(self):
342 import fcntl
Michael Ellermana15d5642014-06-17 17:54:34 +1000343 fcntl.ioctl(self.fd, ioctl_numbers['DISABLE'], 0)
Jan Kiszka626c4272011-10-07 09:37:49 +0200344
345class TracepointProvider(object):
346 def __init__(self):
347 path = os.path.join(sys_tracing, 'events', 'kvm')
348 fields = [f
349 for f in os.listdir(path)
350 if os.path.isdir(os.path.join(path, f))]
351 extra = []
352 for f in fields:
353 if f in filters:
354 subfield, values = filters[f]
355 for name, number in values.iteritems():
356 extra.append(f + '(' + name + ')')
357 fields += extra
358 self._setup(fields)
359 self.select(fields)
360 def fields(self):
361 return self._fields
Michael Ellerman763952d2014-06-17 17:54:30 +1000362
363 def _online_cpus(self):
364 l = []
365 pattern = r'cpu([0-9]+)'
366 basedir = '/sys/devices/system/cpu'
367 for entry in os.listdir(basedir):
368 match = re.match(pattern, entry)
369 if not match:
370 continue
371 path = os.path.join(basedir, entry, 'online')
372 if os.path.exists(path) and open(path).read().strip() != '1':
373 continue
374 l.append(int(match.group(1)))
375 return l
376
Jan Kiszka626c4272011-10-07 09:37:49 +0200377 def _setup(self, _fields):
378 self._fields = _fields
Michael Ellerman763952d2014-06-17 17:54:30 +1000379 cpus = self._online_cpus()
Jan Kiszka626c4272011-10-07 09:37:49 +0200380 import resource
Michael Ellerman763952d2014-06-17 17:54:30 +1000381 nfiles = len(cpus) * 1000
Jan Kiszka626c4272011-10-07 09:37:49 +0200382 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
383 events = []
384 self.group_leaders = []
Michael Ellerman763952d2014-06-17 17:54:30 +1000385 for cpu in cpus:
Jan Kiszka626c4272011-10-07 09:37:49 +0200386 group = Group(cpu)
387 for name in _fields:
388 tracepoint = name
389 filter = None
390 m = re.match(r'(.*)\((.*)\)', name)
391 if m:
392 tracepoint, sub = m.groups()
393 filter = '%s==%d\0' % (filters[tracepoint][0],
394 filters[tracepoint][1][sub])
395 event = group.add_event(name, event_set = 'kvm',
396 tracepoint = tracepoint,
397 filter = filter)
398 self.group_leaders.append(group)
399 def select(self, fields):
400 for group in self.group_leaders:
401 for event in group.events:
402 if event.name in fields:
403 event.enable()
404 else:
405 event.disable()
406 def read(self):
407 from collections import defaultdict
408 ret = defaultdict(int)
409 for group in self.group_leaders:
410 for name, val in group.read().iteritems():
411 ret[name] += val
412 return ret
413
414class Stats:
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200415 def __init__(self, providers, fields = None):
416 self.providers = providers
Jan Kiszka626c4272011-10-07 09:37:49 +0200417 self.fields_filter = fields
418 self._update()
419 def _update(self):
420 def wanted(key):
421 import re
422 if not self.fields_filter:
423 return True
424 return re.match(self.fields_filter, key) is not None
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200425 self.values = dict()
426 for d in providers:
427 provider_fields = [key for key in d.fields() if wanted(key)]
428 for key in provider_fields:
429 self.values[key] = None
430 d.select(provider_fields)
Jan Kiszka626c4272011-10-07 09:37:49 +0200431 def set_fields_filter(self, fields_filter):
432 self.fields_filter = fields_filter
433 self._update()
434 def get(self):
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200435 for d in providers:
436 new = d.read()
437 for key in d.fields():
438 oldval = self.values.get(key, (0, 0))
439 newval = new[key]
440 newdelta = None
441 if oldval is not None:
442 newdelta = newval - oldval[0]
443 self.values[key] = (newval, newdelta)
Jan Kiszka626c4272011-10-07 09:37:49 +0200444 return self.values
445
446if not os.access('/sys/kernel/debug', os.F_OK):
447 print 'Please enable CONFIG_DEBUG_FS in your kernel'
448 sys.exit(1)
449if not os.access('/sys/kernel/debug/kvm', os.F_OK):
450 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
451 print "and ensure the kvm modules are loaded"
452 sys.exit(1)
453
454label_width = 40
455number_width = 10
456
457def tui(screen, stats):
458 curses.use_default_colors()
459 curses.noecho()
460 drilldown = False
461 fields_filter = stats.fields_filter
462 def update_drilldown():
463 if not fields_filter:
464 if drilldown:
465 stats.set_fields_filter(None)
466 else:
467 stats.set_fields_filter(r'^[^\(]*$')
468 update_drilldown()
469 def refresh(sleeptime):
470 screen.erase()
471 screen.addstr(0, 0, 'kvm statistics')
472 row = 2
473 s = stats.get()
474 def sortkey(x):
475 if s[x][1]:
476 return (-s[x][1], -s[x][0])
477 else:
478 return (0, -s[x][0])
479 for key in sorted(s.keys(), key = sortkey):
480 if row >= screen.getmaxyx()[0]:
481 break
482 values = s[key]
483 if not values[0] and not values[1]:
484 break
485 col = 1
486 screen.addstr(row, col, key)
487 col += label_width
488 screen.addstr(row, col, '%10d' % (values[0],))
489 col += number_width
490 if values[1] is not None:
491 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
492 row += 1
493 screen.refresh()
494
495 sleeptime = 0.25
496 while True:
497 refresh(sleeptime)
498 curses.halfdelay(int(sleeptime * 10))
499 sleeptime = 3
500 try:
501 c = screen.getkey()
502 if c == 'x':
503 drilldown = not drilldown
504 update_drilldown()
505 if c == 'q':
506 break
507 except KeyboardInterrupt:
508 break
509 except curses.error:
510 continue
511
512def batch(stats):
513 s = stats.get()
514 time.sleep(1)
515 s = stats.get()
516 for key in sorted(s.keys()):
517 values = s[key]
518 print '%-22s%10d%10d' % (key, values[0], values[1])
519
520def log(stats):
521 keys = sorted(stats.get().iterkeys())
522 def banner():
523 for k in keys:
524 print '%10s' % k[0:9],
525 print
526 def statline():
527 s = stats.get()
528 for k in keys:
529 print ' %9d' % s[k][1],
530 print
531 line = 0
532 banner_repeat = 20
533 while True:
534 time.sleep(1)
535 if line % banner_repeat == 0:
536 banner()
537 statline()
538 line += 1
539
540options = optparse.OptionParser()
541options.add_option('-1', '--once', '--batch',
542 action = 'store_true',
543 default = False,
544 dest = 'once',
545 help = 'run in batch mode for one second',
546 )
547options.add_option('-l', '--log',
548 action = 'store_true',
549 default = False,
550 dest = 'log',
551 help = 'run in logging mode (like vmstat)',
552 )
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200553options.add_option('-t', '--tracepoints',
554 action = 'store_true',
555 default = False,
556 dest = 'tracepoints',
557 help = 'retrieve statistics from tracepoints',
558 )
559options.add_option('-d', '--debugfs',
560 action = 'store_true',
561 default = False,
562 dest = 'debugfs',
563 help = 'retrieve statistics from debugfs',
564 )
Jan Kiszka626c4272011-10-07 09:37:49 +0200565options.add_option('-f', '--fields',
566 action = 'store',
567 default = None,
568 dest = 'fields',
569 help = 'fields to display (regex)',
570 )
571(options, args) = options.parse_args(sys.argv)
572
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200573providers = []
574if options.tracepoints:
575 providers.append(TracepointProvider())
576if options.debugfs:
577 providers.append(DebugfsProvider())
Jan Kiszka626c4272011-10-07 09:37:49 +0200578
Paolo Bonzinib763adf2014-05-21 12:42:26 +0200579if len(providers) == 0:
580 try:
581 providers = [TracepointProvider()]
582 except:
583 providers = [DebugfsProvider()]
584
585stats = Stats(providers, fields = options.fields)
Jan Kiszka626c4272011-10-07 09:37:49 +0200586
587if options.log:
588 log(stats)
589elif not options.once:
590 import curses.wrapper
591 curses.wrapper(tui, stats)
592else:
593 batch(stats)