3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
11 # This work is licensed under the terms of the GNU GPL, version 2. See
12 # the COPYING file in the top-level directory.
15 import sys, os, time, optparse
17 class DebugfsProvider(object):
19 self.base = '/sys/kernel/debug/kvm'
20 self._fields = os.listdir(self.base)
23 def select(self, fields):
27 return int(file(self.base + '/' + key).read())
28 return dict([(key, val(key)) for key in self._fields])
32 1: 'EXTERNAL_INTERRUPT',
34 7: 'PENDING_INTERRUPT',
58 36: 'MWAIT_INSTRUCTION',
59 39: 'MONITOR_INSTRUCTION',
60 40: 'PAUSE_INSTRUCTION',
61 41: 'MCE_DURING_VMENTRY',
62 43: 'TPR_BELOW_THRESHOLD',
101 0x065: 'CR0_SEL_WRITE',
125 0x07d: 'TASK_SWITCH',
126 0x07e: 'FERR_FREEZE',
144 s390_exit_reasons = {
152 0x007: 'IRQ_WINDOW_OPEN',
162 0x017: 'INTERNAL_ERROR',
167 vendor_exit_reasons = {
168 'vmx': vmx_exit_reasons,
169 'svm': svm_exit_reasons,
170 'IBM/S390': s390_exit_reasons,
175 for line in file('/proc/cpuinfo').readlines():
176 if line.startswith('flags') or line.startswith('vendor_id'):
177 for flag in line.split():
178 if flag in vendor_exit_reasons:
179 exit_reasons = vendor_exit_reasons[flag]
182 'kvm_exit': ('exit_reason', exit_reasons)
186 return dict((x[1], x[0]) for x in d.iteritems())
189 filters[f] = (filters[f][0], invert(filters[f][1]))
191 import ctypes, struct, array
193 libc = ctypes.CDLL('libc.so.6')
194 syscall = libc.syscall
195 class perf_event_attr(ctypes.Structure):
196 _fields_ = [('type', ctypes.c_uint32),
197 ('size', ctypes.c_uint32),
198 ('config', ctypes.c_uint64),
199 ('sample_freq', ctypes.c_uint64),
200 ('sample_type', ctypes.c_uint64),
201 ('read_format', ctypes.c_uint64),
202 ('flags', ctypes.c_uint64),
203 ('wakeup_events', ctypes.c_uint32),
204 ('bp_type', ctypes.c_uint32),
205 ('bp_addr', ctypes.c_uint64),
206 ('bp_len', ctypes.c_uint64),
208 def _perf_event_open(attr, pid, cpu, group_fd, flags):
209 return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
210 ctypes.c_int(cpu), ctypes.c_int(group_fd),
211 ctypes.c_long(flags))
213 PERF_TYPE_HARDWARE = 0
214 PERF_TYPE_SOFTWARE = 1
215 PERF_TYPE_TRACEPOINT = 2
216 PERF_TYPE_HW_CACHE = 3
218 PERF_TYPE_BREAKPOINT = 5
220 PERF_SAMPLE_IP = 1 << 0
221 PERF_SAMPLE_TID = 1 << 1
222 PERF_SAMPLE_TIME = 1 << 2
223 PERF_SAMPLE_ADDR = 1 << 3
224 PERF_SAMPLE_READ = 1 << 4
225 PERF_SAMPLE_CALLCHAIN = 1 << 5
226 PERF_SAMPLE_ID = 1 << 6
227 PERF_SAMPLE_CPU = 1 << 7
228 PERF_SAMPLE_PERIOD = 1 << 8
229 PERF_SAMPLE_STREAM_ID = 1 << 9
230 PERF_SAMPLE_RAW = 1 << 10
232 PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
233 PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
234 PERF_FORMAT_ID = 1 << 2
235 PERF_FORMAT_GROUP = 1 << 3
239 sys_tracing = '/sys/kernel/debug/tracing'
242 def __init__(self, cpu):
244 self.group_leader = None
246 def add_event(self, name, event_set, tracepoint, filter = None):
247 self.events.append(Event(group = self,
248 name = name, event_set = event_set,
249 tracepoint = tracepoint, filter = filter))
250 if len(self.events) == 1:
251 self.file = os.fdopen(self.events[0].fd)
253 bytes = 8 * (1 + len(self.events))
254 fmt = 'xxxxxxxx' + 'q' * len(self.events)
255 return dict(zip([event.name for event in self.events],
256 struct.unpack(fmt, self.file.read(bytes))))
259 def __init__(self, group, name, event_set, tracepoint, filter = None):
261 attr = perf_event_attr()
262 attr.type = PERF_TYPE_TRACEPOINT
263 attr.size = ctypes.sizeof(attr)
264 id_path = os.path.join(sys_tracing, 'events', event_set,
266 id = int(file(id_path).read())
268 attr.sample_type = (PERF_SAMPLE_RAW
271 attr.sample_period = 1
272 attr.read_format = PERF_FORMAT_GROUP
275 group_leader = group.events[0].fd
276 fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
278 raise Exception('perf_event_open failed')
281 fcntl.ioctl(fd, 0x40082406, filter)
285 fcntl.ioctl(self.fd, 0x00002400, 0)
288 fcntl.ioctl(self.fd, 0x00002401, 0)
290 class TracepointProvider(object):
292 path = os.path.join(sys_tracing, 'events', 'kvm')
294 for f in os.listdir(path)
295 if os.path.isdir(os.path.join(path, f))]
299 subfield, values = filters[f]
300 for name, number in values.iteritems():
301 extra.append(f + '(' + name + ')')
307 def _setup(self, _fields):
308 self._fields = _fields
309 cpure = r'cpu([0-9]+)'
310 self.cpus = [int(re.match(cpure, x).group(1))
311 for x in os.listdir('/sys/devices/system/cpu')
312 if re.match(cpure, x)]
314 nfiles = len(self.cpus) * 1000
315 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
317 self.group_leaders = []
318 for cpu in self.cpus:
323 m = re.match(r'(.*)\((.*)\)', name)
325 tracepoint, sub = m.groups()
326 filter = '%s==%d\0' % (filters[tracepoint][0],
327 filters[tracepoint][1][sub])
328 event = group.add_event(name, event_set = 'kvm',
329 tracepoint = tracepoint,
331 self.group_leaders.append(group)
332 def select(self, fields):
333 for group in self.group_leaders:
334 for event in group.events:
335 if event.name in fields:
340 from collections import defaultdict
341 ret = defaultdict(int)
342 for group in self.group_leaders:
343 for name, val in group.read().iteritems():
348 def __init__(self, provider, fields = None):
349 self.provider = provider
350 self.fields_filter = fields
355 if not self.fields_filter:
357 return re.match(self.fields_filter, key) is not None
358 self.values = dict([(key, None)
359 for key in provider.fields()
361 self.provider.select(self.values.keys())
362 def set_fields_filter(self, fields_filter):
363 self.fields_filter = fields_filter
366 new = self.provider.read()
367 for key in self.provider.fields():
368 oldval = self.values.get(key, (0, 0))
371 if oldval is not None:
372 newdelta = newval - oldval[0]
373 self.values[key] = (newval, newdelta)
376 if not os.access('/sys/kernel/debug', os.F_OK):
377 print 'Please enable CONFIG_DEBUG_FS in your kernel'
379 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
380 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
381 print "and ensure the kvm modules are loaded"
387 def tui(screen, stats):
388 curses.use_default_colors()
391 fields_filter = stats.fields_filter
392 def update_drilldown():
393 if not fields_filter:
395 stats.set_fields_filter(None)
397 stats.set_fields_filter(r'^[^\(]*$')
399 def refresh(sleeptime):
401 screen.addstr(0, 0, 'kvm statistics')
406 return (-s[x][1], -s[x][0])
409 for key in sorted(s.keys(), key = sortkey):
410 if row >= screen.getmaxyx()[0]:
413 if not values[0] and not values[1]:
416 screen.addstr(row, col, key)
418 screen.addstr(row, col, '%10d' % (values[0],))
420 if values[1] is not None:
421 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
428 curses.halfdelay(int(sleeptime * 10))
433 drilldown = not drilldown
437 except KeyboardInterrupt:
446 for key in sorted(s.keys()):
448 print '%-22s%10d%10d' % (key, values[0], values[1])
451 keys = sorted(stats.get().iterkeys())
454 print '%10s' % k[0:9],
459 print ' %9d' % s[k][1],
465 if line % banner_repeat == 0:
470 options = optparse.OptionParser()
471 options.add_option('-1', '--once', '--batch',
472 action = 'store_true',
475 help = 'run in batch mode for one second',
477 options.add_option('-l', '--log',
478 action = 'store_true',
481 help = 'run in logging mode (like vmstat)',
483 options.add_option('-f', '--fields',
487 help = 'fields to display (regex)',
489 (options, args) = options.parse_args(sys.argv)
492 provider = TracepointProvider()
494 provider = DebugfsProvider()
496 stats = Stats(provider, fields = options.fields)
500 elif not options.once:
501 import curses.wrapper
502 curses.wrapper(tui, stats)