PPC: Fix compile with profiling enabled
[qemu/agraf.git] / scripts / kvm / kvm_stat
blob762544b197bff0c0565e852a6363c951fbe44877
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, os, time, optparse
17 class 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])
30 vmx_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',
70 svm_exit_reasons = {
71 0x000: 'READ_CR0',
72 0x003: 'READ_CR3',
73 0x004: 'READ_CR4',
74 0x008: 'READ_CR8',
75 0x010: 'WRITE_CR0',
76 0x013: 'WRITE_CR3',
77 0x014: 'WRITE_CR4',
78 0x018: 'WRITE_CR8',
79 0x020: 'READ_DR0',
80 0x021: 'READ_DR1',
81 0x022: 'READ_DR2',
82 0x023: 'READ_DR3',
83 0x024: 'READ_DR4',
84 0x025: 'READ_DR5',
85 0x026: 'READ_DR6',
86 0x027: 'READ_DR7',
87 0x030: 'WRITE_DR0',
88 0x031: 'WRITE_DR1',
89 0x032: 'WRITE_DR2',
90 0x033: 'WRITE_DR3',
91 0x034: 'WRITE_DR4',
92 0x035: 'WRITE_DR5',
93 0x036: 'WRITE_DR6',
94 0x037: 'WRITE_DR7',
95 0x040: 'EXCP_BASE',
96 0x060: 'INTR',
97 0x061: 'NMI',
98 0x062: 'SMI',
99 0x063: 'INIT',
100 0x064: 'VINTR',
101 0x065: 'CR0_SEL_WRITE',
102 0x066: 'IDTR_READ',
103 0x067: 'GDTR_READ',
104 0x068: 'LDTR_READ',
105 0x069: 'TR_READ',
106 0x06a: 'IDTR_WRITE',
107 0x06b: 'GDTR_WRITE',
108 0x06c: 'LDTR_WRITE',
109 0x06d: 'TR_WRITE',
110 0x06e: 'RDTSC',
111 0x06f: 'RDPMC',
112 0x070: 'PUSHF',
113 0x071: 'POPF',
114 0x072: 'CPUID',
115 0x073: 'RSM',
116 0x074: 'IRET',
117 0x075: 'SWINT',
118 0x076: 'INVD',
119 0x077: 'PAUSE',
120 0x078: 'HLT',
121 0x079: 'INVLPG',
122 0x07a: 'INVLPGA',
123 0x07b: 'IOIO',
124 0x07c: 'MSR',
125 0x07d: 'TASK_SWITCH',
126 0x07e: 'FERR_FREEZE',
127 0x07f: 'SHUTDOWN',
128 0x080: 'VMRUN',
129 0x081: 'VMMCALL',
130 0x082: 'VMLOAD',
131 0x083: 'VMSAVE',
132 0x084: 'STGI',
133 0x085: 'CLGI',
134 0x086: 'SKINIT',
135 0x087: 'RDTSCP',
136 0x088: 'ICEBP',
137 0x089: 'WBINVD',
138 0x08a: 'MONITOR',
139 0x08b: 'MWAIT',
140 0x08c: 'MWAIT_COND',
141 0x400: 'NPF',
144 s390_exit_reasons = {
145 0x000: 'UNKNOWN',
146 0x001: 'EXCEPTION',
147 0x002: 'IO',
148 0x003: 'HYPERCALL',
149 0x004: 'DEBUG',
150 0x005: 'HLT',
151 0x006: 'MMIO',
152 0x007: 'IRQ_WINDOW_OPEN',
153 0x008: 'SHUTDOWN',
154 0x009: 'FAIL_ENTRY',
155 0x010: 'INTR',
156 0x011: 'SET_TPR',
157 0x012: 'TPR_ACCESS',
158 0x013: 'S390_SIEIC',
159 0x014: 'S390_RESET',
160 0x015: 'DCR',
161 0x016: 'NMI',
162 0x017: 'INTERNAL_ERROR',
163 0x018: 'OSI',
164 0x019: 'PAPR_HCALL',
167 vendor_exit_reasons = {
168 'vmx': vmx_exit_reasons,
169 'svm': svm_exit_reasons,
170 'IBM/S390': s390_exit_reasons,
173 syscall_numbers = {
174 'IBM/S390': 331,
177 sc_perf_evt_open = 298
179 exit_reasons = None
181 for line in file('/proc/cpuinfo').readlines():
182 if line.startswith('flags') or line.startswith('vendor_id'):
183 for flag in line.split():
184 if flag in vendor_exit_reasons:
185 exit_reasons = vendor_exit_reasons[flag]
186 if flag in syscall_numbers:
187 sc_perf_evt_open = syscall_numbers[flag]
188 filters = {
189 'kvm_exit': ('exit_reason', exit_reasons)
192 def invert(d):
193 return dict((x[1], x[0]) for x in d.iteritems())
195 for f in filters:
196 filters[f] = (filters[f][0], invert(filters[f][1]))
198 import ctypes, struct, array
200 libc = ctypes.CDLL('libc.so.6')
201 syscall = libc.syscall
202 class perf_event_attr(ctypes.Structure):
203 _fields_ = [('type', ctypes.c_uint32),
204 ('size', ctypes.c_uint32),
205 ('config', ctypes.c_uint64),
206 ('sample_freq', ctypes.c_uint64),
207 ('sample_type', ctypes.c_uint64),
208 ('read_format', ctypes.c_uint64),
209 ('flags', ctypes.c_uint64),
210 ('wakeup_events', ctypes.c_uint32),
211 ('bp_type', ctypes.c_uint32),
212 ('bp_addr', ctypes.c_uint64),
213 ('bp_len', ctypes.c_uint64),
215 def _perf_event_open(attr, pid, cpu, group_fd, flags):
216 return syscall(sc_perf_evt_open, ctypes.pointer(attr), ctypes.c_int(pid),
217 ctypes.c_int(cpu), ctypes.c_int(group_fd),
218 ctypes.c_long(flags))
220 PERF_TYPE_HARDWARE = 0
221 PERF_TYPE_SOFTWARE = 1
222 PERF_TYPE_TRACEPOINT = 2
223 PERF_TYPE_HW_CACHE = 3
224 PERF_TYPE_RAW = 4
225 PERF_TYPE_BREAKPOINT = 5
227 PERF_SAMPLE_IP = 1 << 0
228 PERF_SAMPLE_TID = 1 << 1
229 PERF_SAMPLE_TIME = 1 << 2
230 PERF_SAMPLE_ADDR = 1 << 3
231 PERF_SAMPLE_READ = 1 << 4
232 PERF_SAMPLE_CALLCHAIN = 1 << 5
233 PERF_SAMPLE_ID = 1 << 6
234 PERF_SAMPLE_CPU = 1 << 7
235 PERF_SAMPLE_PERIOD = 1 << 8
236 PERF_SAMPLE_STREAM_ID = 1 << 9
237 PERF_SAMPLE_RAW = 1 << 10
239 PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
240 PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
241 PERF_FORMAT_ID = 1 << 2
242 PERF_FORMAT_GROUP = 1 << 3
244 import re
246 sys_tracing = '/sys/kernel/debug/tracing'
248 class Group(object):
249 def __init__(self, cpu):
250 self.events = []
251 self.group_leader = None
252 self.cpu = cpu
253 def add_event(self, name, event_set, tracepoint, filter = None):
254 self.events.append(Event(group = self,
255 name = name, event_set = event_set,
256 tracepoint = tracepoint, filter = filter))
257 if len(self.events) == 1:
258 self.file = os.fdopen(self.events[0].fd)
259 def read(self):
260 bytes = 8 * (1 + len(self.events))
261 fmt = 'xxxxxxxx' + 'q' * len(self.events)
262 return dict(zip([event.name for event in self.events],
263 struct.unpack(fmt, self.file.read(bytes))))
265 class Event(object):
266 def __init__(self, group, name, event_set, tracepoint, filter = None):
267 self.name = name
268 attr = perf_event_attr()
269 attr.type = PERF_TYPE_TRACEPOINT
270 attr.size = ctypes.sizeof(attr)
271 id_path = os.path.join(sys_tracing, 'events', event_set,
272 tracepoint, 'id')
273 id = int(file(id_path).read())
274 attr.config = id
275 attr.sample_type = (PERF_SAMPLE_RAW
276 | PERF_SAMPLE_TIME
277 | PERF_SAMPLE_CPU)
278 attr.sample_period = 1
279 attr.read_format = PERF_FORMAT_GROUP
280 group_leader = -1
281 if group.events:
282 group_leader = group.events[0].fd
283 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
284 if fd == -1:
285 raise Exception('perf_event_open failed')
286 if filter:
287 import fcntl
288 fcntl.ioctl(fd, 0x40082406, filter)
289 self.fd = fd
290 def enable(self):
291 import fcntl
292 fcntl.ioctl(self.fd, 0x00002400, 0)
293 def disable(self):
294 import fcntl
295 fcntl.ioctl(self.fd, 0x00002401, 0)
297 class TracepointProvider(object):
298 def __init__(self):
299 path = os.path.join(sys_tracing, 'events', 'kvm')
300 fields = [f
301 for f in os.listdir(path)
302 if os.path.isdir(os.path.join(path, f))]
303 extra = []
304 for f in fields:
305 if f in filters:
306 subfield, values = filters[f]
307 for name, number in values.iteritems():
308 extra.append(f + '(' + name + ')')
309 fields += extra
310 self._setup(fields)
311 self.select(fields)
312 def fields(self):
313 return self._fields
314 def _setup(self, _fields):
315 self._fields = _fields
316 cpure = r'cpu([0-9]+)'
317 self.cpus = [int(re.match(cpure, x).group(1))
318 for x in os.listdir('/sys/devices/system/cpu')
319 if re.match(cpure, x)]
320 import resource
321 nfiles = len(self.cpus) * 1000
322 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
323 events = []
324 self.group_leaders = []
325 for cpu in self.cpus:
326 group = Group(cpu)
327 for name in _fields:
328 tracepoint = name
329 filter = None
330 m = re.match(r'(.*)\((.*)\)', name)
331 if m:
332 tracepoint, sub = m.groups()
333 filter = '%s==%d\0' % (filters[tracepoint][0],
334 filters[tracepoint][1][sub])
335 event = group.add_event(name, event_set = 'kvm',
336 tracepoint = tracepoint,
337 filter = filter)
338 self.group_leaders.append(group)
339 def select(self, fields):
340 for group in self.group_leaders:
341 for event in group.events:
342 if event.name in fields:
343 event.enable()
344 else:
345 event.disable()
346 def read(self):
347 from collections import defaultdict
348 ret = defaultdict(int)
349 for group in self.group_leaders:
350 for name, val in group.read().iteritems():
351 ret[name] += val
352 return ret
354 class Stats:
355 def __init__(self, provider, fields = None):
356 self.provider = provider
357 self.fields_filter = fields
358 self._update()
359 def _update(self):
360 def wanted(key):
361 import re
362 if not self.fields_filter:
363 return True
364 return re.match(self.fields_filter, key) is not None
365 self.values = dict([(key, None)
366 for key in provider.fields()
367 if wanted(key)])
368 self.provider.select(self.values.keys())
369 def set_fields_filter(self, fields_filter):
370 self.fields_filter = fields_filter
371 self._update()
372 def get(self):
373 new = self.provider.read()
374 for key in self.provider.fields():
375 oldval = self.values.get(key, (0, 0))
376 newval = new[key]
377 newdelta = None
378 if oldval is not None:
379 newdelta = newval - oldval[0]
380 self.values[key] = (newval, newdelta)
381 return self.values
383 if not os.access('/sys/kernel/debug', os.F_OK):
384 print 'Please enable CONFIG_DEBUG_FS in your kernel'
385 sys.exit(1)
386 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
387 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
388 print "and ensure the kvm modules are loaded"
389 sys.exit(1)
391 label_width = 40
392 number_width = 10
394 def tui(screen, stats):
395 curses.use_default_colors()
396 curses.noecho()
397 drilldown = False
398 fields_filter = stats.fields_filter
399 def update_drilldown():
400 if not fields_filter:
401 if drilldown:
402 stats.set_fields_filter(None)
403 else:
404 stats.set_fields_filter(r'^[^\(]*$')
405 update_drilldown()
406 def refresh(sleeptime):
407 screen.erase()
408 screen.addstr(0, 0, 'kvm statistics')
409 row = 2
410 s = stats.get()
411 def sortkey(x):
412 if s[x][1]:
413 return (-s[x][1], -s[x][0])
414 else:
415 return (0, -s[x][0])
416 for key in sorted(s.keys(), key = sortkey):
417 if row >= screen.getmaxyx()[0]:
418 break
419 values = s[key]
420 if not values[0] and not values[1]:
421 break
422 col = 1
423 screen.addstr(row, col, key)
424 col += label_width
425 screen.addstr(row, col, '%10d' % (values[0],))
426 col += number_width
427 if values[1] is not None:
428 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
429 row += 1
430 screen.refresh()
432 sleeptime = 0.25
433 while True:
434 refresh(sleeptime)
435 curses.halfdelay(int(sleeptime * 10))
436 sleeptime = 3
437 try:
438 c = screen.getkey()
439 if c == 'x':
440 drilldown = not drilldown
441 update_drilldown()
442 if c == 'q':
443 break
444 except KeyboardInterrupt:
445 break
446 except curses.error:
447 continue
449 def batch(stats):
450 s = stats.get()
451 time.sleep(1)
452 s = stats.get()
453 for key in sorted(s.keys()):
454 values = s[key]
455 print '%-22s%10d%10d' % (key, values[0], values[1])
457 def log(stats):
458 keys = sorted(stats.get().iterkeys())
459 def banner():
460 for k in keys:
461 print '%10s' % k[0:9],
462 print
463 def statline():
464 s = stats.get()
465 for k in keys:
466 print ' %9d' % s[k][1],
467 print
468 line = 0
469 banner_repeat = 20
470 while True:
471 time.sleep(1)
472 if line % banner_repeat == 0:
473 banner()
474 statline()
475 line += 1
477 options = optparse.OptionParser()
478 options.add_option('-1', '--once', '--batch',
479 action = 'store_true',
480 default = False,
481 dest = 'once',
482 help = 'run in batch mode for one second',
484 options.add_option('-l', '--log',
485 action = 'store_true',
486 default = False,
487 dest = 'log',
488 help = 'run in logging mode (like vmstat)',
490 options.add_option('-f', '--fields',
491 action = 'store',
492 default = None,
493 dest = 'fields',
494 help = 'fields to display (regex)',
496 (options, args) = options.parse_args(sys.argv)
498 try:
499 provider = TracepointProvider()
500 except:
501 provider = DebugfsProvider()
503 stats = Stats(provider, fields = options.fields)
505 if options.log:
506 log(stats)
507 elif not options.once:
508 import curses.wrapper
509 curses.wrapper(tui, stats)
510 else:
511 batch(stats)