3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
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.
24 from collections
import defaultdict
28 'EXTERNAL_INTERRUPT': 1,
30 'PENDING_INTERRUPT': 7,
54 'MWAIT_INSTRUCTION': 36,
55 'MONITOR_INSTRUCTION': 39,
56 'PAUSE_INSTRUCTION': 40,
57 'MCE_DURING_VMENTRY': 41,
58 'TPR_BELOW_THRESHOLD': 43,
99 'CR0_SEL_WRITE': 0x065,
123 'TASK_SWITCH': 0x07d,
124 'FERR_FREEZE': 0x07e,
143 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
144 AARCH64_EXIT_REASONS
= {
182 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
183 USERSPACE_EXIT_REASONS
= {
191 'IRQ_WINDOW_OPEN': 7,
201 'INTERNAL_ERROR': 17,
212 'vmx': VMX_EXIT_REASONS
,
213 'svm': SVM_EXIT_REASONS
,
216 SC_PERF_EVT_OPEN
= None
220 'SET_FILTER' : 0x40082406,
221 'ENABLE' : 0x00002400,
222 'DISABLE' : 0x00002401,
223 'RESET' : 0x00002403,
227 global SC_PERF_EVT_OPEN
230 SC_PERF_EVT_OPEN
= 298
231 EXIT_REASONS
= X86_EXIT_REASONS
[flag
]
234 global SC_PERF_EVT_OPEN
236 SC_PERF_EVT_OPEN
= 331
239 global SC_PERF_EVT_OPEN
242 SC_PERF_EVT_OPEN
= 319
244 IOCTL_NUMBERS
['ENABLE'] = 0x20002400
245 IOCTL_NUMBERS
['DISABLE'] = 0x20002401
246 IOCTL_NUMBERS
['SET_FILTER'] = 0x80002406 |
(ctypes
.sizeof(ctypes
.c_char_p
)
250 global SC_PERF_EVT_OPEN
253 SC_PERF_EVT_OPEN
= 241
254 EXIT_REASONS
= AARCH64_EXIT_REASONS
256 def detect_platform():
257 machine
= os
.uname()[4]
259 if machine
.startswith('ppc'):
261 elif machine
.startswith('aarch64'):
263 elif machine
.startswith('s390'):
266 for line
in file('/proc/cpuinfo').readlines():
267 if line
.startswith('flags'):
268 for flag
in line
.split():
269 if flag
in X86_EXIT_REASONS
:
275 """Returns os.walk() data for specified directory.
277 As it is only a wrapper it returns the same 3-tuple of (dirpath,
278 dirnames, filenames).
280 return next(os
.walk(path
))
283 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
285 filters
['kvm_exit'] = ('exit_reason', EXIT_REASONS
)
287 libc
= ctypes
.CDLL('libc.so.6', use_errno
=True)
288 syscall
= libc
.syscall
290 class perf_event_attr(ctypes
.Structure
):
291 _fields_
= [('type', ctypes
.c_uint32
),
292 ('size', ctypes
.c_uint32
),
293 ('config', ctypes
.c_uint64
),
294 ('sample_freq', ctypes
.c_uint64
),
295 ('sample_type', ctypes
.c_uint64
),
296 ('read_format', ctypes
.c_uint64
),
297 ('flags', ctypes
.c_uint64
),
298 ('wakeup_events', ctypes
.c_uint32
),
299 ('bp_type', ctypes
.c_uint32
),
300 ('bp_addr', ctypes
.c_uint64
),
301 ('bp_len', ctypes
.c_uint64
),
303 def _perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
304 return syscall(SC_PERF_EVT_OPEN
, ctypes
.pointer(attr
), ctypes
.c_int(pid
),
305 ctypes
.c_int(cpu
), ctypes
.c_int(group_fd
),
306 ctypes
.c_long(flags
))
308 PERF_TYPE_TRACEPOINT
= 2
309 PERF_FORMAT_GROUP
= 1 << 3
311 PATH_DEBUGFS_TRACING
= '/sys/kernel/debug/tracing'
312 PATH_DEBUGFS_KVM
= '/sys/kernel/debug/kvm'
315 def __init__(self
, cpu
):
317 self
.group_leader
= None
319 def add_event(self
, name
, event_set
, tracepoint
, tracefilter
=None):
320 self
.events
.append(Event(group
=self
,
321 name
=name
, event_set
=event_set
,
322 tracepoint
=tracepoint
,
323 tracefilter
=tracefilter
))
324 if len(self
.events
) == 1:
325 self
.file = os
.fdopen(self
.events
[0].fd
)
327 length
= 8 * (1 + len(self
.events
))
328 fmt
= 'xxxxxxxx' + 'q' * len(self
.events
)
329 return dict(zip([event
.name
for event
in self
.events
],
330 struct
.unpack(fmt
, self
.file.read(length
))))
333 def __init__(self
, group
, name
, event_set
, tracepoint
, tracefilter
=None):
335 attr
= perf_event_attr()
336 attr
.type = PERF_TYPE_TRACEPOINT
337 attr
.size
= ctypes
.sizeof(attr
)
338 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', event_set
,
340 id = int(file(id_path
).read())
342 attr
.sample_period
= 1
343 attr
.read_format
= PERF_FORMAT_GROUP
346 group_leader
= group
.events
[0].fd
347 fd
= _perf_event_open(attr
, -1, group
.cpu
, group_leader
, 0)
349 err
= ctypes
.get_errno()
350 raise OSError(err
, os
.strerror(err
),
351 'while calling sys_perf_event_open().')
353 fcntl
.ioctl(fd
, IOCTL_NUMBERS
['SET_FILTER'], tracefilter
)
356 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['ENABLE'], 0)
358 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['DISABLE'], 0)
360 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['RESET'], 0)
362 class TracepointProvider(object):
364 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
365 fields
= walkdir(path
)[1]
369 subfield
, values
= filters
[f
]
370 for name
, number
in values
.iteritems():
371 extra
.append(f
+ '(' + name
+ ')')
378 def _online_cpus(self
):
380 pattern
= r
'cpu([0-9]+)'
381 basedir
= '/sys/devices/system/cpu'
382 for entry
in os
.listdir(basedir
):
383 match
= re
.match(pattern
, entry
)
386 path
= os
.path
.join(basedir
, entry
, 'online')
387 if os
.path
.exists(path
) and open(path
).read().strip() != '1':
389 l
.append(int(match
.group(1)))
392 def _setup(self
, _fields
):
393 self
._fields
= _fields
394 cpus
= self
._online
_cpus
()
396 # The constant is needed as a buffer for python libs, std
397 # streams and other files that the script opens.
398 rlimit
= len(cpus
) * len(_fields
) + 50
400 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (rlimit
, rlimit
))
402 sys
.exit("NOFILE rlimit could not be raised to {0}".format(rlimit
))
405 self
.group_leaders
= []
411 m
= re
.match(r
'(.*)\((.*)\)', name
)
413 tracepoint
, sub
= m
.groups()
414 tracefilter
= '%s==%d\0' % (filters
[tracepoint
][0],
415 filters
[tracepoint
][1][sub
])
416 event
= group
.add_event(name
, event_set
='kvm',
417 tracepoint
=tracepoint
,
418 tracefilter
=tracefilter
)
419 self
.group_leaders
.append(group
)
420 def select(self
, fields
):
421 for group
in self
.group_leaders
:
422 for event
in group
.events
:
423 if event
.name
in fields
:
429 ret
= defaultdict(int)
430 for group
in self
.group_leaders
:
431 for name
, val
in group
.read().iteritems():
435 class DebugfsProvider(object):
437 self
._fields
= walkdir(PATH_DEBUGFS_KVM
)[2]
440 def select(self
, fields
):
441 self
._fields
= fields
444 return int(file(PATH_DEBUGFS_KVM
+ '/' + key
).read())
445 return dict([(key
, val(key
)) for key
in self
._fields
])
448 def __init__(self
, providers
, fields
=None):
449 self
.providers
= providers
450 self
.fields_filter
= fields
454 if not self
.fields_filter
:
456 return re
.match(self
.fields_filter
, key
) is not None
458 for d
in self
.providers
:
459 provider_fields
= [key
for key
in d
.fields() if wanted(key
)]
460 for key
in provider_fields
:
461 self
.values
[key
] = None
462 d
.select(provider_fields
)
463 def set_fields_filter(self
, fields_filter
):
464 self
.fields_filter
= fields_filter
467 for d
in self
.providers
:
469 for key
in d
.fields():
470 oldval
= self
.values
.get(key
, (0, 0))
473 if oldval
is not None:
474 newdelta
= newval
- oldval
[0]
475 self
.values
[key
] = (newval
, newdelta
)
481 def tui(screen
, stats
):
482 curses
.use_default_colors()
485 fields_filter
= stats
.fields_filter
486 def update_drilldown():
487 if not fields_filter
:
489 stats
.set_fields_filter(None)
491 stats
.set_fields_filter(r
'^[^\(]*$')
493 def refresh(sleeptime
):
495 screen
.addstr(0, 0, 'kvm statistics')
496 screen
.addstr(2, 1, 'Event')
497 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
- len('Total'), 'Total')
498 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 8 - len('Current'), 'Current')
503 return (-s
[x
][1], -s
[x
][0])
506 for key
in sorted(s
.keys(), key
=sortkey
):
507 if row
>= screen
.getmaxyx()[0]:
510 if not values
[0] and not values
[1]:
513 screen
.addstr(row
, col
, key
)
515 screen
.addstr(row
, col
, '%10d' % (values
[0],))
517 if values
[1] is not None:
518 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
525 curses
.halfdelay(int(sleeptime
* 10))
530 drilldown
= not drilldown
534 except KeyboardInterrupt:
543 for key
in sorted(s
.keys()):
545 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
548 keys
= sorted(stats
.get().iterkeys())
551 print '%10s' % k
[0:9],
556 print ' %9d' % s
[k
][1],
562 if line
% banner_repeat
== 0:
568 optparser
= optparse
.OptionParser()
569 optparser
.add_option('-1', '--once', '--batch',
573 help='run in batch mode for one second',
575 optparser
.add_option('-l', '--log',
579 help='run in logging mode (like vmstat)',
581 optparser
.add_option('-t', '--tracepoints',
585 help='retrieve statistics from tracepoints',
587 optparser
.add_option('-d', '--debugfs',
591 help='retrieve statistics from debugfs',
593 optparser
.add_option('-f', '--fields',
597 help='fields to display (regex)',
599 (options
, _
) = optparser
.parse_args(sys
.argv
)
602 def get_providers(options
):
605 if options
.tracepoints
:
606 providers
.append(TracepointProvider())
608 providers
.append(DebugfsProvider())
609 if len(providers
) == 0:
610 providers
.append(TracepointProvider())
615 if not os
.path
.exists('/sys/kernel/debug'):
616 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
619 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
620 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
621 "readable by the current user:\n"
622 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
623 "Also ensure, that the kvm modules are loaded.\n")
626 if not os
.path
.exists(PATH_DEBUGFS_TRACING
):
627 sys
.stderr
.write("Please make {0} readable by the current user.\n"
628 .format(PATH_DEBUGFS_TRACING
))
634 options
= get_options()
635 providers
= get_providers(options
)
636 stats
= Stats(providers
, fields
=options
.fields
)
640 elif not options
.once
:
641 curses
.wrapper(tui
, stats
)
645 if __name__
== "__main__":