kvm_stat: add wrappers for perf_event enable and disable ioctls
[qemu/qemu-dev-zwu.git] / kvm / kvm_stat
blob2e1fe7308862ca9817c9bcecfbb1a02f82e33d18
1 #!/usr/bin/python
3 import curses
4 import sys, os, time, optparse
6 class DebugfsProvider(object):
7 def __init__(self):
8 self.base = '/sys/kernel/debug/kvm'
9 self._fields = os.listdir(self.base)
10 def fields(self):
11 return self._fields
12 def select(self, fields):
13 self._fields = fields
14 def read(self):
15 def val(key):
16 return int(file(self.base + '/' + key).read())
17 return dict([(key, val(key)) for key in self._fields])
19 vmx_exit_reasons = {
20 0: 'EXCEPTION_NMI',
21 1: 'EXTERNAL_INTERRUPT',
22 2: 'TRIPLE_FAULT',
23 7: 'PENDING_INTERRUPT',
24 8: 'NMI_WINDOW',
25 9: 'TASK_SWITCH',
26 10: 'CPUID',
27 12: 'HLT',
28 14: 'INVLPG',
29 15: 'RDPMC',
30 16: 'RDTSC',
31 18: 'VMCALL',
32 19: 'VMCLEAR',
33 20: 'VMLAUNCH',
34 21: 'VMPTRLD',
35 22: 'VMPTRST',
36 23: 'VMREAD',
37 24: 'VMRESUME',
38 25: 'VMWRITE',
39 26: 'VMOFF',
40 27: 'VMON',
41 28: 'CR_ACCESS',
42 29: 'DR_ACCESS',
43 30: 'IO_INSTRUCTION',
44 31: 'MSR_READ',
45 32: 'MSR_WRITE',
46 33: 'INVALID_STATE',
47 36: 'MWAIT_INSTRUCTION',
48 39: 'MONITOR_INSTRUCTION',
49 40: 'PAUSE_INSTRUCTION',
50 41: 'MCE_DURING_VMENTRY',
51 43: 'TPR_BELOW_THRESHOLD',
52 44: 'APIC_ACCESS',
53 48: 'EPT_VIOLATION',
54 49: 'EPT_MISCONFIG',
55 54: 'WBINVD',
56 55: 'XSETBV',
59 svm_exit_reasons = {
60 0x000: 'READ_CR0',
61 0x003: 'READ_CR3',
62 0x004: 'READ_CR4',
63 0x008: 'READ_CR8',
64 0x010: 'WRITE_CR0',
65 0x013: 'WRITE_CR3',
66 0x014: 'WRITE_CR4',
67 0x018: 'WRITE_CR8',
68 0x020: 'READ_DR0',
69 0x021: 'READ_DR1',
70 0x022: 'READ_DR2',
71 0x023: 'READ_DR3',
72 0x024: 'READ_DR4',
73 0x025: 'READ_DR5',
74 0x026: 'READ_DR6',
75 0x027: 'READ_DR7',
76 0x030: 'WRITE_DR0',
77 0x031: 'WRITE_DR1',
78 0x032: 'WRITE_DR2',
79 0x033: 'WRITE_DR3',
80 0x034: 'WRITE_DR4',
81 0x035: 'WRITE_DR5',
82 0x036: 'WRITE_DR6',
83 0x037: 'WRITE_DR7',
84 0x040: 'EXCP_BASE',
85 0x060: 'INTR',
86 0x061: 'NMI',
87 0x062: 'SMI',
88 0x063: 'INIT',
89 0x064: 'VINTR',
90 0x065: 'CR0_SEL_WRITE',
91 0x066: 'IDTR_READ',
92 0x067: 'GDTR_READ',
93 0x068: 'LDTR_READ',
94 0x069: 'TR_READ',
95 0x06a: 'IDTR_WRITE',
96 0x06b: 'GDTR_WRITE',
97 0x06c: 'LDTR_WRITE',
98 0x06d: 'TR_WRITE',
99 0x06e: 'RDTSC',
100 0x06f: 'RDPMC',
101 0x070: 'PUSHF',
102 0x071: 'POPF',
103 0x072: 'CPUID',
104 0x073: 'RSM',
105 0x074: 'IRET',
106 0x075: 'SWINT',
107 0x076: 'INVD',
108 0x077: 'PAUSE',
109 0x078: 'HLT',
110 0x079: 'INVLPG',
111 0x07a: 'INVLPGA',
112 0x07b: 'IOIO',
113 0x07c: 'MSR',
114 0x07d: 'TASK_SWITCH',
115 0x07e: 'FERR_FREEZE',
116 0x07f: 'SHUTDOWN',
117 0x080: 'VMRUN',
118 0x081: 'VMMCALL',
119 0x082: 'VMLOAD',
120 0x083: 'VMSAVE',
121 0x084: 'STGI',
122 0x085: 'CLGI',
123 0x086: 'SKINIT',
124 0x087: 'RDTSCP',
125 0x088: 'ICEBP',
126 0x089: 'WBINVD',
127 0x08a: 'MONITOR',
128 0x08b: 'MWAIT',
129 0x08c: 'MWAIT_COND',
130 0x400: 'NPF',
133 vendor_exit_reasons = {
134 'vmx': vmx_exit_reasons,
135 'svm': svm_exit_reasons,
138 exit_reasons = None
140 for line in file('/proc/cpuinfo').readlines():
141 if line.startswith('flags'):
142 for flag in line.split():
143 if flag in vendor_exit_reasons:
144 exit_reasons = vendor_exit_reasons[flag]
146 filters = {
147 'kvm_exit': ('exit_reason', exit_reasons)
150 def invert(d):
151 return dict((x[1], x[0]) for x in d.iteritems())
153 for f in filters:
154 filters[f] = (filters[f][0], invert(filters[f][1]))
156 import ctypes, struct, array
158 libc = ctypes.CDLL('libc.so.6')
159 syscall = libc.syscall
160 class perf_event_attr(ctypes.Structure):
161 _fields_ = [('type', ctypes.c_uint32),
162 ('size', ctypes.c_uint32),
163 ('config', ctypes.c_uint64),
164 ('sample_freq', ctypes.c_uint64),
165 ('sample_type', ctypes.c_uint64),
166 ('read_format', ctypes.c_uint64),
167 ('flags', ctypes.c_uint64),
168 ('wakeup_events', ctypes.c_uint32),
169 ('bp_type', ctypes.c_uint32),
170 ('bp_addr', ctypes.c_uint64),
171 ('bp_len', ctypes.c_uint64),
173 def _perf_event_open(attr, pid, cpu, group_fd, flags):
174 return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
175 ctypes.c_int(cpu), ctypes.c_int(group_fd),
176 ctypes.c_long(flags))
178 PERF_TYPE_HARDWARE = 0
179 PERF_TYPE_SOFTWARE = 1
180 PERF_TYPE_TRACEPOINT = 2
181 PERF_TYPE_HW_CACHE = 3
182 PERF_TYPE_RAW = 4
183 PERF_TYPE_BREAKPOINT = 5
185 PERF_SAMPLE_IP = 1 << 0
186 PERF_SAMPLE_TID = 1 << 1
187 PERF_SAMPLE_TIME = 1 << 2
188 PERF_SAMPLE_ADDR = 1 << 3
189 PERF_SAMPLE_READ = 1 << 4
190 PERF_SAMPLE_CALLCHAIN = 1 << 5
191 PERF_SAMPLE_ID = 1 << 6
192 PERF_SAMPLE_CPU = 1 << 7
193 PERF_SAMPLE_PERIOD = 1 << 8
194 PERF_SAMPLE_STREAM_ID = 1 << 9
195 PERF_SAMPLE_RAW = 1 << 10
197 PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
198 PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
199 PERF_FORMAT_ID = 1 << 2
200 PERF_FORMAT_GROUP = 1 << 3
202 import re
204 sys_tracing = '/sys/kernel/debug/tracing'
206 class Group(object):
207 def __init__(self, cpu):
208 self.events = []
209 self.group_leader = None
210 self.cpu = cpu
211 def add_event(self, name, event_set, tracepoint, filter = None):
212 self.events.append(Event(group = self,
213 name = name, event_set = event_set,
214 tracepoint = tracepoint, filter = filter))
215 if len(self.events) == 1:
216 self.file = os.fdopen(self.events[0].fd)
217 def read(self):
218 bytes = 8 * (1 + len(self.events))
219 fmt = 'xxxxxxxx' + 'q' * len(self.events)
220 return dict(zip([event.name for event in self.events],
221 struct.unpack(fmt, self.file.read(bytes))))
223 class Event(object):
224 def __init__(self, group, name, event_set, tracepoint, filter = None):
225 self.name = name
226 attr = perf_event_attr()
227 attr.type = PERF_TYPE_TRACEPOINT
228 attr.size = ctypes.sizeof(attr)
229 id_path = os.path.join(sys_tracing, 'events', event_set,
230 tracepoint, 'id')
231 id = int(file(id_path).read())
232 attr.config = id
233 attr.sample_type = (PERF_SAMPLE_RAW
234 | PERF_SAMPLE_TIME
235 | PERF_SAMPLE_CPU)
236 attr.sample_period = 1
237 attr.read_format = PERF_FORMAT_GROUP
238 group_leader = -1
239 if group.events:
240 group_leader = group.events[0].fd
241 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
242 if fd == -1:
243 raise Exception('perf_event_open failed')
244 if filter:
245 import fcntl
246 fcntl.ioctl(fd, 0x40082406, filter)
247 self.fd = fd
248 def enable(self):
249 import fcntl
250 fcntl.ioctl(self.fd, 0x00002400, 0)
251 def disable(self):
252 import fcntl
253 fcntl.ioctl(self.fd, 0x00002401, 0)
255 class TracepointProvider(object):
256 def __init__(self):
257 path = os.path.join(sys_tracing, 'events', 'kvm')
258 fields = [f
259 for f in os.listdir(path)
260 if os.path.isdir(os.path.join(path, f))]
261 extra = []
262 for f in fields:
263 if f in filters:
264 subfield, values = filters[f]
265 for name, number in values.iteritems():
266 extra.append(f + '(' + name + ')')
267 fields += extra
268 self.select(fields)
269 def fields(self):
270 return self._fields
271 def select(self, _fields):
272 self._fields = _fields
273 cpure = r'cpu([0-9]+)'
274 self.cpus = [int(re.match(cpure, x).group(1))
275 for x in os.listdir('/sys/devices/system/cpu')
276 if re.match(cpure, x)]
277 import resource
278 nfiles = len(self.cpus) * 1000
279 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
280 events = []
281 self.group_leaders = []
282 for cpu in self.cpus:
283 group = Group(cpu)
284 for name in _fields:
285 tracepoint = name
286 filter = None
287 m = re.match(r'(.*)\((.*)\)', name)
288 if m:
289 tracepoint, sub = m.groups()
290 filter = '%s==%d\0' % (filters[tracepoint][0],
291 filters[tracepoint][1][sub])
292 event = group.add_event(name, event_set = 'kvm',
293 tracepoint = tracepoint,
294 filter = filter)
295 self.group_leaders.append(group)
296 def read(self):
297 from collections import defaultdict
298 ret = defaultdict(int)
299 for group in self.group_leaders:
300 for name, val in group.read().iteritems():
301 ret[name] += val
302 return ret
304 class Stats:
305 def __init__(self, provider, fields = None):
306 def wanted(key):
307 import re
308 if not fields:
309 return True
310 return re.match(fields, key) != None
311 self.provider = provider
312 self.values = dict([(key, None)
313 for key in provider.fields()
314 if wanted(key)])
315 self.provider.select(self.values.keys())
316 def get(self):
317 new = self.provider.read()
318 for key in self.provider.fields():
319 oldval = self.values[key]
320 newval = new[key]
321 newdelta = None
322 if oldval is not None:
323 newdelta = newval - oldval[0]
324 self.values[key] = (newval, newdelta)
325 return self.values
327 if not os.access('/sys/kernel/debug', os.F_OK):
328 print 'Please enable CONFIG_DEBUG_FS in your kernel'
329 sys.exit(1)
330 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
331 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
332 print "and ensure the kvm modules are loaded"
333 sys.exit(1)
335 label_width = 40
336 number_width = 10
338 def tui(screen, stats):
339 curses.use_default_colors()
340 curses.noecho()
341 def refresh(sleeptime):
342 screen.erase()
343 screen.addstr(0, 0, 'kvm statistics')
344 row = 2
345 s = stats.get()
346 def sortkey(x):
347 if s[x][1]:
348 return (-s[x][1], -s[x][0])
349 else:
350 return (0, -s[x][0])
351 for key in sorted(s.keys(), key = sortkey):
352 if row >= screen.getmaxyx()[0]:
353 break
354 values = s[key]
355 if not values[0] and not values[1]:
356 break
357 col = 1
358 screen.addstr(row, col, key)
359 col += label_width
360 screen.addstr(row, col, '%10d' % (values[0],))
361 col += number_width
362 if values[1] is not None:
363 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
364 row += 1
365 screen.refresh()
367 sleeptime = 0.25
368 while True:
369 refresh(sleeptime)
370 curses.halfdelay(int(sleeptime * 10))
371 sleeptime = 3
372 try:
373 c = screen.getkey()
374 if c == 'q':
375 break
376 except KeyboardInterrupt:
377 break
378 except curses.error:
379 continue
381 def batch(stats):
382 s = stats.get()
383 time.sleep(1)
384 s = stats.get()
385 for key in sorted(s.keys()):
386 values = s[key]
387 print '%-22s%10d%10d' % (key, values[0], values[1])
389 def log(stats):
390 keys = sorted(stats.get().iterkeys())
391 def banner():
392 for k in keys:
393 print '%10s' % k[0:9],
394 print
395 def statline():
396 s = stats.get()
397 for k in keys:
398 print ' %9d' % s[k][1],
399 print
400 line = 0
401 banner_repeat = 20
402 while True:
403 time.sleep(1)
404 if line % banner_repeat == 0:
405 banner()
406 statline()
407 line += 1
409 options = optparse.OptionParser()
410 options.add_option('-1', '--once', '--batch',
411 action = 'store_true',
412 default = False,
413 dest = 'once',
414 help = 'run in batch mode for one second',
416 options.add_option('-l', '--log',
417 action = 'store_true',
418 default = False,
419 dest = 'log',
420 help = 'run in logging mode (like vmstat)',
422 options.add_option('-f', '--fields',
423 action = 'store',
424 default = None,
425 dest = 'fields',
426 help = 'fields to display (regex)',
428 (options, args) = options.parse_args(sys.argv)
430 try:
431 provider = TracepointProvider()
432 except:
433 provider = DebugfsProvider()
435 stats = Stats(provider, fields = options.fields)
437 if options.log:
438 log(stats)
439 elif not options.once:
440 import curses.wrapper
441 curses.wrapper(tui, stats)
442 else:
443 batch(stats)