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
.base
= '/sys/kernel/debug/kvm'
29 self
._fields
= walkdir(self
.base
)[2]
32 def select(self
, fields
):
36 return int(file(self
.base
+ '/' + key
).read())
37 return dict([(key
, val(key
)) for key
in self
._fields
])
41 1: 'EXTERNAL_INTERRUPT',
43 7: 'PENDING_INTERRUPT',
67 36: 'MWAIT_INSTRUCTION',
68 39: 'MONITOR_INSTRUCTION',
69 40: 'PAUSE_INSTRUCTION',
70 41: 'MCE_DURING_VMENTRY',
71 43: 'TPR_BELOW_THRESHOLD',
112 0x065: 'CR0_SEL_WRITE',
136 0x07d: 'TASK_SWITCH',
137 0x07e: 'FERR_FREEZE',
156 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
157 AARCH64_EXIT_REASONS
= {
195 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
196 USERSPACE_EXIT_REASONS
= {
204 7: 'IRQ_WINDOW_OPEN',
214 17: 'INTERNAL_ERROR',
225 'vmx': VMX_EXIT_REASONS
,
226 'svm': SVM_EXIT_REASONS
,
229 SC_PERF_EVT_OPEN
= None
233 'SET_FILTER' : 0x40082406,
234 'ENABLE' : 0x00002400,
235 'DISABLE' : 0x00002401,
236 'RESET' : 0x00002403,
240 global SC_PERF_EVT_OPEN
243 SC_PERF_EVT_OPEN
= 298
244 EXIT_REASONS
= X86_EXIT_REASONS
[flag
]
247 global SC_PERF_EVT_OPEN
249 SC_PERF_EVT_OPEN
= 331
252 global SC_PERF_EVT_OPEN
255 SC_PERF_EVT_OPEN
= 319
257 IOCTL_NUMBERS
['ENABLE'] = 0x20002400
258 IOCTL_NUMBERS
['DISABLE'] = 0x20002401
259 IOCTL_NUMBERS
['SET_FILTER'] = 0x80002406 |
(ctypes
.sizeof(ctypes
.c_char_p
)
263 global SC_PERF_EVT_OPEN
266 SC_PERF_EVT_OPEN
= 241
267 EXIT_REASONS
= AARCH64_EXIT_REASONS
269 def detect_platform():
270 if os
.uname()[4].startswith('ppc'):
273 elif os
.uname()[4].startswith('aarch64'):
277 for line
in file('/proc/cpuinfo').readlines():
278 if line
.startswith('flags'):
279 for flag
in line
.split():
280 if flag
in X86_EXIT_REASONS
:
283 elif line
.startswith('vendor_id'):
284 for flag
in line
.split():
285 if flag
== 'IBM/S390':
293 """Returns os.walk() data for specified directory.
295 As it is only a wrapper it returns the same 3-tuple of (dirpath,
296 dirnames, filenames).
298 return next(os
.walk(path
))
301 return dict((x
[1], x
[0]) for x
in d
.iteritems())
304 filters
['kvm_userspace_exit'] = ('reason', invert(USERSPACE_EXIT_REASONS
))
306 filters
['kvm_exit'] = ('exit_reason', invert(EXIT_REASONS
))
308 libc
= ctypes
.CDLL('libc.so.6')
309 syscall
= libc
.syscall
310 get_errno
= libc
.__errno
_location
311 get_errno
.restype
= ctypes
.POINTER(ctypes
.c_int
)
313 class perf_event_attr(ctypes
.Structure
):
314 _fields_
= [('type', ctypes
.c_uint32
),
315 ('size', ctypes
.c_uint32
),
316 ('config', ctypes
.c_uint64
),
317 ('sample_freq', ctypes
.c_uint64
),
318 ('sample_type', ctypes
.c_uint64
),
319 ('read_format', ctypes
.c_uint64
),
320 ('flags', ctypes
.c_uint64
),
321 ('wakeup_events', ctypes
.c_uint32
),
322 ('bp_type', ctypes
.c_uint32
),
323 ('bp_addr', ctypes
.c_uint64
),
324 ('bp_len', ctypes
.c_uint64
),
326 def _perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
327 return syscall(SC_PERF_EVT_OPEN
, ctypes
.pointer(attr
), ctypes
.c_int(pid
),
328 ctypes
.c_int(cpu
), ctypes
.c_int(group_fd
),
329 ctypes
.c_long(flags
))
331 PERF_TYPE_TRACEPOINT
= 2
332 PERF_FORMAT_GROUP
= 1 << 3
334 sys_tracing
= '/sys/kernel/debug/tracing'
337 def __init__(self
, cpu
):
339 self
.group_leader
= None
341 def add_event(self
, name
, event_set
, tracepoint
, filter = None):
342 self
.events
.append(Event(group
= self
,
343 name
= name
, event_set
= event_set
,
344 tracepoint
= tracepoint
, filter = filter))
345 if len(self
.events
) == 1:
346 self
.file = os
.fdopen(self
.events
[0].fd
)
348 bytes
= 8 * (1 + len(self
.events
))
349 fmt
= 'xxxxxxxx' + 'q' * len(self
.events
)
350 return dict(zip([event
.name
for event
in self
.events
],
351 struct
.unpack(fmt
, self
.file.read(bytes
))))
354 def __init__(self
, group
, name
, event_set
, tracepoint
, filter = None):
356 attr
= perf_event_attr()
357 attr
.type = PERF_TYPE_TRACEPOINT
358 attr
.size
= ctypes
.sizeof(attr
)
359 id_path
= os
.path
.join(sys_tracing
, 'events', event_set
,
361 id = int(file(id_path
).read())
363 attr
.sample_period
= 1
364 attr
.read_format
= PERF_FORMAT_GROUP
367 group_leader
= group
.events
[0].fd
368 fd
= _perf_event_open(attr
, -1, group
.cpu
, group_leader
, 0)
371 raise Exception('perf_event_open failed, errno = ' + err
.__str
__())
373 fcntl
.ioctl(fd
, IOCTL_NUMBERS
['SET_FILTER'], filter)
376 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['ENABLE'], 0)
378 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['DISABLE'], 0)
380 fcntl
.ioctl(self
.fd
, IOCTL_NUMBERS
['RESET'], 0)
382 class TracepointProvider(object):
384 path
= os
.path
.join(sys_tracing
, 'events', 'kvm')
385 fields
= walkdir(path
)[1]
389 subfield
, values
= filters
[f
]
390 for name
, number
in values
.iteritems():
391 extra
.append(f
+ '(' + name
+ ')')
398 def _online_cpus(self
):
400 pattern
= r
'cpu([0-9]+)'
401 basedir
= '/sys/devices/system/cpu'
402 for entry
in os
.listdir(basedir
):
403 match
= re
.match(pattern
, entry
)
406 path
= os
.path
.join(basedir
, entry
, 'online')
407 if os
.path
.exists(path
) and open(path
).read().strip() != '1':
409 l
.append(int(match
.group(1)))
412 def _setup(self
, _fields
):
413 self
._fields
= _fields
414 cpus
= self
._online
_cpus
()
415 nfiles
= len(cpus
) * 1000
416 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (nfiles
, nfiles
))
418 self
.group_leaders
= []
424 m
= re
.match(r
'(.*)\((.*)\)', name
)
426 tracepoint
, sub
= m
.groups()
427 filter = '%s==%d\0' % (filters
[tracepoint
][0],
428 filters
[tracepoint
][1][sub
])
429 event
= group
.add_event(name
, event_set
= 'kvm',
430 tracepoint
= tracepoint
,
432 self
.group_leaders
.append(group
)
433 def select(self
, fields
):
434 for group
in self
.group_leaders
:
435 for event
in group
.events
:
436 if event
.name
in fields
:
442 ret
= defaultdict(int)
443 for group
in self
.group_leaders
:
444 for name
, val
in group
.read().iteritems():
449 def __init__(self
, providers
, fields
= None):
450 self
.providers
= providers
451 self
.fields_filter
= fields
455 if not self
.fields_filter
:
457 return re
.match(self
.fields_filter
, key
) is not None
460 provider_fields
= [key
for key
in d
.fields() if wanted(key
)]
461 for key
in provider_fields
:
462 self
.values
[key
] = None
463 d
.select(provider_fields
)
464 def set_fields_filter(self
, fields_filter
):
465 self
.fields_filter
= fields_filter
470 for key
in d
.fields():
471 oldval
= self
.values
.get(key
, (0, 0))
474 if oldval
is not None:
475 newdelta
= newval
- oldval
[0]
476 self
.values
[key
] = (newval
, newdelta
)
479 if not os
.access('/sys/kernel/debug', os
.F_OK
):
480 print 'Please enable CONFIG_DEBUG_FS in your kernel'
482 if not os
.access('/sys/kernel/debug/kvm', os
.F_OK
):
483 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
484 print "and ensure the kvm modules are loaded"
490 def tui(screen
, stats
):
491 curses
.use_default_colors()
494 fields_filter
= stats
.fields_filter
495 def update_drilldown():
496 if not fields_filter
:
498 stats
.set_fields_filter(None)
500 stats
.set_fields_filter(r
'^[^\(]*$')
502 def refresh(sleeptime
):
504 screen
.addstr(0, 0, 'kvm statistics')
505 screen
.addstr(2, 1, 'Event')
506 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
- len('Total'), 'Total')
507 screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 8 - len('Current'), 'Current')
512 return (-s
[x
][1], -s
[x
][0])
515 for key
in sorted(s
.keys(), key
= sortkey
):
516 if row
>= screen
.getmaxyx()[0]:
519 if not values
[0] and not values
[1]:
522 screen
.addstr(row
, col
, key
)
524 screen
.addstr(row
, col
, '%10d' % (values
[0],))
526 if values
[1] is not None:
527 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
534 curses
.halfdelay(int(sleeptime
* 10))
539 drilldown
= not drilldown
543 except KeyboardInterrupt:
552 for key
in sorted(s
.keys()):
554 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
557 keys
= sorted(stats
.get().iterkeys())
560 print '%10s' % k
[0:9],
565 print ' %9d' % s
[k
][1],
571 if line
% banner_repeat
== 0:
576 options
= optparse
.OptionParser()
577 options
.add_option('-1', '--once', '--batch',
578 action
= 'store_true',
581 help = 'run in batch mode for one second',
583 options
.add_option('-l', '--log',
584 action
= 'store_true',
587 help = 'run in logging mode (like vmstat)',
589 options
.add_option('-t', '--tracepoints',
590 action
= 'store_true',
592 dest
= 'tracepoints',
593 help = 'retrieve statistics from tracepoints',
595 options
.add_option('-d', '--debugfs',
596 action
= 'store_true',
599 help = 'retrieve statistics from debugfs',
601 options
.add_option('-f', '--fields',
605 help = 'fields to display (regex)',
607 (options
, args
) = options
.parse_args(sys
.argv
)
610 if options
.tracepoints
:
611 providers
.append(TracepointProvider())
613 providers
.append(DebugfsProvider())
615 if len(providers
) == 0:
617 providers
= [TracepointProvider()]
619 providers
= [DebugfsProvider()]
621 stats
= Stats(providers
, fields
= options
.fields
)
625 elif not options
.once
:
626 curses
.wrapper(tui
, stats
)