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
26 class DebugfsProvider(object):
28 self
._fields
= walkdir(PATH_DEBUGFS_KVM
)[2]
31 def select(self
, fields
):
35 return int(file(PATH_DEBUGFS_KVM
+ '/' + key
).read())
36 return dict([(key
, val(key
)) for key
in self
._fields
])
40 'EXTERNAL_INTERRUPT': 1,
42 'PENDING_INTERRUPT': 7,
66 'MWAIT_INSTRUCTION': 36,
67 'MONITOR_INSTRUCTION': 39,
68 'PAUSE_INSTRUCTION': 40,
69 'MCE_DURING_VMENTRY': 41,
70 'TPR_BELOW_THRESHOLD': 43,
111 'CR0_SEL_WRITE': 0x065,
135 'TASK_SWITCH': 0x07d,
136 'FERR_FREEZE': 0x07e,
155 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
156 AARCH64_EXIT_REASONS
= {
194 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
195 USERSPACE_EXIT_REASONS
= {
203 'IRQ_WINDOW_OPEN': 7,
213 'INTERNAL_ERROR': 17,
224 'vmx': VMX_EXIT_REASONS
,
225 'svm': SVM_EXIT_REASONS
,
228 SC_PERF_EVT_OPEN
= None
232 'SET_FILTER' : 0x40082406,
233 'ENABLE' : 0x00002400,
234 'DISABLE' : 0x00002401,
235 'RESET' : 0x00002403,
239 global SC_PERF_EVT_OPEN
242 SC_PERF_EVT_OPEN
= 298
243 EXIT_REASONS
= X86_EXIT_REASONS
[flag
]
246 global SC_PERF_EVT_OPEN
248 SC_PERF_EVT_OPEN
= 331
251 global SC_PERF_EVT_OPEN
254 SC_PERF_EVT_OPEN
= 319
256 IOCTL_NUMBERS
['ENABLE'] = 0x20002400
257 IOCTL_NUMBERS
['DISABLE'] = 0x20002401
258 IOCTL_NUMBERS
['SET_FILTER'] = 0x80002406 |
(ctypes
.sizeof(ctypes
.c_char_p
)
262 global SC_PERF_EVT_OPEN
265 SC_PERF_EVT_OPEN
= 241
266 EXIT_REASONS
= AARCH64_EXIT_REASONS
268 def detect_platform():
269 if os
.uname()[4].startswith('ppc'):
272 elif os
.uname()[4].startswith('aarch64'):
276 for line
in file('/proc/cpuinfo').readlines():
277 if line
.startswith('flags'):
278 for flag
in line
.split():
279 if flag
in X86_EXIT_REASONS
:
282 elif line
.startswith('vendor_id'):
283 for flag
in line
.split():
284 if flag
== 'IBM/S390':
292 """Returns os.walk() data for specified directory.
294 As it is only a wrapper it returns the same 3-tuple of (dirpath,
295 dirnames, filenames).
297 return next(os
.walk(path
))
300 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
302 filters
['kvm_exit'] = ('exit_reason', EXIT_REASONS
)
304 libc
= ctypes
.CDLL('libc.so.6')
305 syscall
= libc
.syscall
306 get_errno
= libc
.__errno
_location
307 get_errno
.restype
= ctypes
.POINTER(ctypes
.c_int
)
309 class perf_event_attr(ctypes
.Structure
):
310 _fields_
= [('type', ctypes
.c_uint32
),
311 ('size', ctypes
.c_uint32
),
312 ('config', ctypes
.c_uint64
),
313 ('sample_freq', ctypes
.c_uint64
),
314 ('sample_type', ctypes
.c_uint64
),
315 ('read_format', ctypes
.c_uint64
),
316 ('flags', ctypes
.c_uint64
),
317 ('wakeup_events', ctypes
.c_uint32
),
318 ('bp_type', ctypes
.c_uint32
),
319 ('bp_addr', ctypes
.c_uint64
),
320 ('bp_len', ctypes
.c_uint64
),
322 def _perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
323 return syscall(SC_PERF_EVT_OPEN
, ctypes
.pointer(attr
), ctypes
.c_int(pid
),
324 ctypes
.c_int(cpu
), ctypes
.c_int(group_fd
),
325 ctypes
.c_long(flags
))
327 PERF_TYPE_TRACEPOINT
= 2
328 PERF_FORMAT_GROUP
= 1 << 3
330 PATH_DEBUGFS_TRACING
= '/sys/kernel/debug/tracing'
331 PATH_DEBUGFS_KVM
= '/sys/kernel/debug/kvm'
334 def __init__(self
, cpu
):
336 self
.group_leader
= None
338 def add_event(self
, name
, event_set
, tracepoint
, filter = None):
339 self
.events
.append(Event(group
= self
,
340 name
= name
, event_set
= event_set
,
341 tracepoint
= tracepoint
, filter = filter))
342 if len(self
.events
) == 1:
343 self
.file = os
.fdopen(self
.events
[0].fd
)
345 bytes
= 8 * (1 + len(self
.events
))
346 fmt
= 'xxxxxxxx' + 'q' * len(self
.events
)
347 return dict(zip([event
.name
for event
in self
.events
],
348 struct
.unpack(fmt
, self
.file.read(bytes
))))
351 def __init__(self
, group
, name
, event_set
, tracepoint
, filter = None):
353 attr
= perf_event_attr()
354 attr
.type = PERF_TYPE_TRACEPOINT
355 attr
.size
= ctypes
.sizeof(attr
)
356 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', event_set
,
358 id = int(file(id_path
).read())
360 attr
.sample_period
= 1
361 attr
.read_format
= PERF_FORMAT_GROUP
364 group_leader
= group
.events
[0].fd
365 fd
= _perf_event_open(attr
, -1, group
.cpu
, group_leader
, 0)
368 raise Exception('perf_event_open failed, errno = ' + err
.__str
__())
370 fcntl
.ioctl(fd
, IOCTL_NUMBERS
['SET_FILTER'], filter)
373 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['ENABLE'], 0)
375 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['DISABLE'], 0)
377 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['RESET'], 0)
379 class TracepointProvider(object):
381 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
382 fields
= walkdir(path
)[1]
386 subfield
, values
= filters
[f
]
387 for name
, number
in values
.iteritems():
388 extra
.append(f
+ '(' + name
+ ')')
395 def _online_cpus(self
):
397 pattern
= r
'cpu([0-9]+)'
398 basedir
= '/sys/devices/system/cpu'
399 for entry
in os
.listdir(basedir
):
400 match
= re
.match(pattern
, entry
)
403 path
= os
.path
.join(basedir
, entry
, 'online')
404 if os
.path
.exists(path
) and open(path
).read().strip() != '1':
406 l
.append(int(match
.group(1)))
409 def _setup(self
, _fields
):
410 self
._fields
= _fields
411 cpus
= self
._online
_cpus
()
412 nfiles
= len(cpus
) * 1000
413 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (nfiles
, nfiles
))
415 self
.group_leaders
= []
421 m
= re
.match(r
'(.*)\((.*)\)', name
)
423 tracepoint
, sub
= m
.groups()
424 filter = '%s==%d\0' % (filters
[tracepoint
][0],
425 filters
[tracepoint
][1][sub
])
426 event
= group
.add_event(name
, event_set
= 'kvm',
427 tracepoint
= tracepoint
,
429 self
.group_leaders
.append(group
)
430 def select(self
, fields
):
431 for group
in self
.group_leaders
:
432 for event
in group
.events
:
433 if event
.name
in fields
:
439 ret
= defaultdict(int)
440 for group
in self
.group_leaders
:
441 for name
, val
in group
.read().iteritems():
446 def __init__(self
, providers
, fields
= None):
447 self
.providers
= providers
448 self
.fields_filter
= fields
452 if not self
.fields_filter
:
454 return re
.match(self
.fields_filter
, key
) is not None
457 provider_fields
= [key
for key
in d
.fields() if wanted(key
)]
458 for key
in provider_fields
:
459 self
.values
[key
] = None
460 d
.select(provider_fields
)
461 def set_fields_filter(self
, fields_filter
):
462 self
.fields_filter
= fields_filter
467 for key
in d
.fields():
468 oldval
= self
.values
.get(key
, (0, 0))
471 if oldval
is not None:
472 newdelta
= newval
- oldval
[0]
473 self
.values
[key
] = (newval
, newdelta
)
476 if not os
.path
.exists('/sys/kernel/debug'):
477 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
479 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
480 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
481 "readable by the current user:\n"
482 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
483 "Also ensure, that the kvm modules are loaded.\n")
485 if not os
.path
.exists(PATH_DEBUGFS_TRACING
):
486 sys
.stderr
.write("Please make {0} readable by the current user.\n"
487 .format(PATH_DEBUGFS_TRACING
))
493 def tui(screen
, stats
):
494 curses
.use_default_colors()
497 fields_filter
= stats
.fields_filter
498 def update_drilldown():
499 if not fields_filter
:
501 stats
.set_fields_filter(None)
503 stats
.set_fields_filter(r
'^[^\(]*$')
505 def refresh(sleeptime
):
507 screen
.addstr(0, 0, 'kvm statistics')
508 screen
.addstr(2, 1, 'Event')
509 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
- len('Total'), 'Total')
510 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 8 - len('Current'), 'Current')
515 return (-s
[x
][1], -s
[x
][0])
518 for key
in sorted(s
.keys(), key
= sortkey
):
519 if row
>= screen
.getmaxyx()[0]:
522 if not values
[0] and not values
[1]:
525 screen
.addstr(row
, col
, key
)
527 screen
.addstr(row
, col
, '%10d' % (values
[0],))
529 if values
[1] is not None:
530 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
537 curses
.halfdelay(int(sleeptime
* 10))
542 drilldown
= not drilldown
546 except KeyboardInterrupt:
555 for key
in sorted(s
.keys()):
557 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
560 keys
= sorted(stats
.get().iterkeys())
563 print '%10s' % k
[0:9],
568 print ' %9d' % s
[k
][1],
574 if line
% banner_repeat
== 0:
579 options
= optparse
.OptionParser()
580 options
.add_option('-1', '--once', '--batch',
581 action
= 'store_true',
584 help = 'run in batch mode for one second',
586 options
.add_option('-l', '--log',
587 action
= 'store_true',
590 help = 'run in logging mode (like vmstat)',
592 options
.add_option('-t', '--tracepoints',
593 action
= 'store_true',
595 dest
= 'tracepoints',
596 help = 'retrieve statistics from tracepoints',
598 options
.add_option('-d', '--debugfs',
599 action
= 'store_true',
602 help = 'retrieve statistics from debugfs',
604 options
.add_option('-f', '--fields',
608 help = 'fields to display (regex)',
610 (options
, args
) = options
.parse_args(sys
.argv
)
613 if options
.tracepoints
:
614 providers
.append(TracepointProvider())
616 providers
.append(DebugfsProvider())
618 if len(providers
) == 0:
620 providers
= [TracepointProvider()]
622 providers
= [DebugfsProvider()]
624 stats
= Stats(providers
, fields
= options
.fields
)
628 elif not options
.once
:
629 curses
.wrapper(tui
, stats
)