]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/kvm/kvm_stat/kvm_stat
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.
13 """The kvm_stat module outputs statistics about running KVM VMs
15 Three different ways of output formatting are available:
16 - as a top-like text ui
17 - in a key -> value format
18 - in an all keys, all values format
20 The data is sampled from the KVM's debugfs entries and its perf events.
34 from collections
import defaultdict
38 'EXTERNAL_INTERRUPT': 1,
40 'PENDING_INTERRUPT': 7,
64 'MWAIT_INSTRUCTION': 36,
65 'MONITOR_INSTRUCTION': 39,
66 'PAUSE_INSTRUCTION': 40,
67 'MCE_DURING_VMENTRY': 41,
68 'TPR_BELOW_THRESHOLD': 43,
109 'CR0_SEL_WRITE': 0x065,
133 'TASK_SWITCH': 0x07d,
134 'FERR_FREEZE': 0x07e,
153 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
154 AARCH64_EXIT_REASONS
= {
192 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
193 USERSPACE_EXIT_REASONS
= {
201 'IRQ_WINDOW_OPEN': 7,
211 'INTERNAL_ERROR': 17,
222 'SET_FILTER': 0x40082406,
223 'ENABLE': 0x00002400,
224 'DISABLE': 0x00002401,
230 """Encapsulates global architecture specific data.
232 Contains the performance event open syscall and ioctl numbers, as
233 well as the VM exit reasons for the architecture it runs on.
238 machine
= os
.uname()[4]
240 if machine
.startswith('ppc'):
242 elif machine
.startswith('aarch64'):
244 elif machine
.startswith('s390'):
248 for line
in open('/proc/cpuinfo'):
249 if not line
.startswith('flags'):
254 return ArchX86(VMX_EXIT_REASONS
)
256 return ArchX86(SVM_EXIT_REASONS
)
261 def __init__(self
, exit_reasons
):
262 self
.sc_perf_evt_open
= 298
263 self
.ioctl_numbers
= IOCTL_NUMBERS
264 self
.exit_reasons
= exit_reasons
269 self
.sc_perf_evt_open
= 319
270 self
.ioctl_numbers
= IOCTL_NUMBERS
271 self
.ioctl_numbers
['ENABLE'] = 0x20002400
272 self
.ioctl_numbers
['DISABLE'] = 0x20002401
273 self
.ioctl_numbers
['RESET'] = 0x20002403
275 # PPC comes in 32 and 64 bit and some generated ioctl
276 # numbers depend on the wordsize.
277 char_ptr_size
= ctypes
.sizeof(ctypes
.c_char_p
)
278 self
.ioctl_numbers
['SET_FILTER'] = 0x80002406 | char_ptr_size
<< 16
279 self
.exit_reasons
= {}
284 self
.sc_perf_evt_open
= 241
285 self
.ioctl_numbers
= IOCTL_NUMBERS
286 self
.exit_reasons
= AARCH64_EXIT_REASONS
289 class ArchS390(Arch
):
291 self
.sc_perf_evt_open
= 331
292 self
.ioctl_numbers
= IOCTL_NUMBERS
293 self
.exit_reasons
= None
295 ARCH
= Arch
.get_arch()
298 class perf_event_attr(ctypes
.Structure
):
299 """Struct that holds the necessary data to set up a trace event.
301 For an extensive explanation see perf_event_open(2) and
302 include/uapi/linux/perf_event.h, struct perf_event_attr
304 All fields that are not initialized in the constructor are 0.
307 _fields_
= [('type', ctypes
.c_uint32
),
308 ('size', ctypes
.c_uint32
),
309 ('config', ctypes
.c_uint64
),
310 ('sample_freq', ctypes
.c_uint64
),
311 ('sample_type', ctypes
.c_uint64
),
312 ('read_format', ctypes
.c_uint64
),
313 ('flags', ctypes
.c_uint64
),
314 ('wakeup_events', ctypes
.c_uint32
),
315 ('bp_type', ctypes
.c_uint32
),
316 ('bp_addr', ctypes
.c_uint64
),
317 ('bp_len', ctypes
.c_uint64
),
321 super(self
.__class
__, self
).__init
__()
322 self
.type = PERF_TYPE_TRACEPOINT
323 self
.size
= ctypes
.sizeof(self
)
324 self
.read_format
= PERF_FORMAT_GROUP
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'
335 """Represents a perf event group."""
340 def add_event(self
, event
):
341 self
.events
.append(event
)
344 """Returns a dict with 'event name: value' for all events in the
347 Values are read by reading from the file descriptor of the
348 event that is the group leader. See perf_event_open(2) for
351 Read format for the used event configuration is:
353 u64 nr; /* The number of events */
355 u64 value; /* The value of the event */
360 length
= 8 * (1 + len(self
.events
))
361 read_format
= 'xxxxxxxx' + 'Q' * len(self
.events
)
362 return dict(zip([event
.name
for event
in self
.events
],
363 struct
.unpack(read_format
,
364 os
.read(self
.events
[0].fd
, length
))))
368 """Represents a performance event and manages its life cycle."""
369 def __init__(self
, name
, group
, trace_cpu
, trace_pid
, trace_point
,
370 trace_filter
, trace_set
='kvm'):
371 self
.libc
= ctypes
.CDLL('libc.so.6', use_errno
=True)
372 self
.syscall
= self
.libc
.syscall
375 self
.setup_event(group
, trace_cpu
, trace_pid
, trace_point
,
376 trace_filter
, trace_set
)
379 """Closes the event's file descriptor.
381 As no python file object was created for the file descriptor,
382 python will not reference count the descriptor and will not
383 close it itself automatically, so we do it.
389 def perf_event_open(self
, attr
, pid
, cpu
, group_fd
, flags
):
390 """Wrapper for the sys_perf_evt_open() syscall.
392 Used to set up performance events, returns a file descriptor or -1
397 - struct perf_event_attr *
398 - pid or -1 to monitor all pids
399 - cpu number or -1 to monitor all cpus
400 - The file descriptor of the group leader or -1 to create a group.
404 return self
.syscall(ARCH
.sc_perf_evt_open
, ctypes
.pointer(attr
),
405 ctypes
.c_int(pid
), ctypes
.c_int(cpu
),
406 ctypes
.c_int(group_fd
), ctypes
.c_long(flags
))
408 def setup_event_attribute(self
, trace_set
, trace_point
):
409 """Returns an initialized ctype perf_event_attr struct."""
411 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', trace_set
,
414 event_attr
= perf_event_attr()
415 event_attr
.config
= int(open(id_path
).read())
418 def setup_event(self
, group
, trace_cpu
, trace_pid
, trace_point
,
419 trace_filter
, trace_set
):
420 """Sets up the perf event in Linux.
422 Issues the syscall to register the event in the kernel and
423 then sets the optional filter.
427 event_attr
= self
.setup_event_attribute(trace_set
, trace_point
)
429 # First event will be group leader.
432 # All others have to pass the leader's descriptor instead.
434 group_leader
= group
.events
[0].fd
436 fd
= self
.perf_event_open(event_attr
, trace_pid
,
437 trace_cpu
, group_leader
, 0)
439 err
= ctypes
.get_errno()
440 raise OSError(err
, os
.strerror(err
),
441 'while calling sys_perf_event_open().')
444 fcntl
.ioctl(fd
, ARCH
.ioctl_numbers
['SET_FILTER'],
450 """Enables the trace event in the kernel.
452 Enabling the group leader makes reading counters from it and the
453 events under it possible.
456 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['ENABLE'], 0)
459 """Disables the trace event in the kernel.
461 Disabling the group leader makes reading all counters under it
465 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['DISABLE'], 0)
468 """Resets the count of the trace event in the kernel."""
469 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['RESET'], 0)
472 class Provider(object):
473 """Encapsulates functionalities used by all providers."""
475 def is_field_wanted(fields_filter
, field
):
476 """Indicate whether field is valid according to fields_filter."""
477 if not fields_filter
:
479 return re
.match(fields_filter
, field
) is not None
483 """Returns os.walk() data for specified directory.
485 As it is only a wrapper it returns the same 3-tuple of (dirpath,
486 dirnames, filenames).
488 return next(os
.walk(path
))
491 class TracepointProvider(Provider
):
492 """Data provider for the stats class.
494 Manages the events/groups from which it acquires its data.
497 def __init__(self
, pid
, fields_filter
):
498 self
.group_leaders
= []
499 self
.filters
= self
.get_filters()
500 self
.update_fields(fields_filter
)
505 """Returns a dict of trace events, their filter ids and
506 the values that can be filtered.
508 Trace events can be filtered for special values by setting a
509 filter string via an ioctl. The string normally has the format
510 identifier==value. For each filter a new event will be created, to
511 be able to distinguish the events.
515 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
516 if ARCH
.exit_reasons
:
517 filters
['kvm_exit'] = ('exit_reason', ARCH
.exit_reasons
)
520 def get_available_fields(self
):
521 """Returns a list of available event's of format 'event name(filter
524 All available events have directories under
525 /sys/kernel/debug/tracing/events/ which export information
526 about the specific event. Therefore, listing the dirs gives us
527 a list of all available events.
529 Some events like the vm exit reasons can be filtered for
530 specific values. To take account for that, the routine below
531 creates special fields with the following format:
532 event name(filter name)
535 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
536 fields
= self
.walkdir(path
)[1]
539 if field
in self
.filters
:
540 filter_name_
, filter_dicts
= self
.filters
[field
]
541 for name
in filter_dicts
:
542 extra
.append(field
+ '(' + name
+ ')')
546 def update_fields(self
, fields_filter
):
547 """Refresh fields, applying fields_filter"""
548 self
._fields
= [field
for field
in self
.get_available_fields()
549 if self
.is_field_wanted(fields_filter
, field
)]
552 def get_online_cpus():
553 """Returns a list of cpu id integers."""
554 def parse_int_list(list_string
):
555 """Returns an int list from a string of comma separated integers and
558 members
= list_string
.split(',')
560 for member
in members
:
561 if '-' not in member
:
562 integers
.append(int(member
))
564 int_range
= member
.split('-')
565 integers
.extend(range(int(int_range
[0]),
566 int(int_range
[1]) + 1))
570 with
open('/sys/devices/system/cpu/online') as cpu_list
:
571 cpu_string
= cpu_list
.readline()
572 return parse_int_list(cpu_string
)
574 def setup_traces(self
):
575 """Creates all event and group objects needed to be able to retrieve
577 fields
= self
.get_available_fields()
579 # Fetch list of all threads of the monitored pid, as qemu
580 # starts a thread for each vcpu.
581 path
= os
.path
.join('/proc', str(self
._pid
), 'task')
582 groupids
= self
.walkdir(path
)[1]
584 groupids
= self
.get_online_cpus()
586 # The constant is needed as a buffer for python libs, std
587 # streams and other files that the script opens.
588 newlim
= len(groupids
) * len(fields
) + 50
590 softlim_
, hardlim
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)
593 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
594 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, newlim
))
596 # Raising the soft limit is sufficient.
597 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, hardlim
))
600 sys
.exit("NOFILE rlimit could not be raised to {0}".format(newlim
))
602 for groupid
in groupids
:
607 match
= re
.match(r
'(.*)\((.*)\)', name
)
609 tracepoint
, sub
= match
.groups()
610 tracefilter
= ('%s==%d\0' %
611 (self
.filters
[tracepoint
][0],
612 self
.filters
[tracepoint
][1][sub
]))
614 # From perf_event_open(2):
615 # pid > 0 and cpu == -1
616 # This measures the specified process/thread on any CPU.
618 # pid == -1 and cpu >= 0
619 # This measures all processes/threads on the specified CPU.
620 trace_cpu
= groupid
if self
._pid
== 0 else -1
621 trace_pid
= int(groupid
) if self
._pid
!= 0 else -1
623 group
.add_event(Event(name
=name
,
627 trace_point
=tracepoint
,
628 trace_filter
=tracefilter
))
630 self
.group_leaders
.append(group
)
637 def fields(self
, fields
):
638 """Enables/disables the (un)wanted events"""
639 self
._fields
= fields
640 for group
in self
.group_leaders
:
641 for index
, event
in enumerate(group
.events
):
642 if event
.name
in fields
:
646 # Do not disable the group leader.
647 # It would disable all of its events.
657 """Changes the monitored pid by setting new traces."""
659 # The garbage collector will get rid of all Event/Group
660 # objects and open files after removing the references.
661 self
.group_leaders
= []
663 self
.fields
= self
._fields
666 """Returns 'event name: current value' for all enabled events."""
667 ret
= defaultdict(int)
668 for group
in self
.group_leaders
:
669 for name
, val
in group
.read().iteritems():
670 if name
in self
._fields
:
675 """Reset all field counters"""
676 for group
in self
.group_leaders
:
677 for event
in group
.events
:
681 class DebugfsProvider(Provider
):
682 """Provides data from the files that KVM creates in the kvm debugfs
684 def __init__(self
, pid
, fields_filter
):
685 self
.update_fields(fields_filter
)
691 def get_available_fields(self
):
692 """"Returns a list of available fields.
694 The fields are all available KVM debugfs files
697 return self
.walkdir(PATH_DEBUGFS_KVM
)[2]
699 def update_fields(self
, fields_filter
):
700 """Refresh fields, applying fields_filter"""
701 self
._fields
= [field
for field
in self
.get_available_fields()
702 if self
.is_field_wanted(fields_filter
, field
)]
709 def fields(self
, fields
):
710 self
._fields
= fields
721 vms
= self
.walkdir(PATH_DEBUGFS_KVM
)[1]
725 self
.paths
= filter(lambda x
: "{}-".format(pid
) in x
, vms
)
732 def read(self
, reset
=0):
733 """Returns a dict with format:'file name / field -> current value'."""
736 # If no debugfs filtering support is available, then don't read.
743 for entry
in os
.walk(PATH_DEBUGFS_KVM
):
747 for field
in self
._fields
:
748 value
= self
.read_field(field
, path
)
751 self
._baseline
[key
] = value
752 if self
._baseline
.get(key
, -1) == -1:
753 self
._baseline
[key
] = value
754 results
[field
] = (results
.get(field
, 0) + value
-
755 self
._baseline
.get(key
, 0))
759 def read_field(self
, field
, path
):
760 """Returns the value of a single field from a specific VM."""
762 return int(open(os
.path
.join(PATH_DEBUGFS_KVM
,
770 """Reset field counters"""
776 """Manages the data providers and the data they provide.
778 It is used to set filters on the provider's data and collect all
782 def __init__(self
, options
):
783 self
.providers
= self
.get_providers(options
)
784 self
._pid
_filter
= options
.pid
785 self
._fields
_filter
= options
.fields
789 def get_providers(options
):
790 """Returns a list of data providers depending on the passed options."""
794 providers
.append(DebugfsProvider(options
.pid
, options
.fields
))
795 if options
.tracepoints
or not providers
:
796 providers
.append(TracepointProvider(options
.pid
, options
.fields
))
800 def update_provider_filters(self
):
801 """Propagates fields filters to providers."""
802 # As we reset the counters when updating the fields we can
803 # also clear the cache of old values.
805 for provider
in self
.providers
:
806 provider
.update_fields(self
._fields
_filter
)
810 for provider
in self
.providers
:
814 def fields_filter(self
):
815 return self
._fields
_filter
817 @fields_filter.setter
818 def fields_filter(self
, fields_filter
):
819 if fields_filter
!= self
._fields
_filter
:
820 self
._fields
_filter
= fields_filter
821 self
.update_provider_filters()
824 def pid_filter(self
):
825 return self
._pid
_filter
828 def pid_filter(self
, pid
):
829 if pid
!= self
._pid
_filter
:
830 self
._pid
_filter
= pid
832 for provider
in self
.providers
:
833 provider
.pid
= self
._pid
_filter
836 """Returns a dict with field -> (value, delta to last value) of all
838 for provider
in self
.providers
:
839 new
= provider
.read()
840 for key
in provider
.fields
:
841 oldval
= self
.values
.get(key
, (0, 0))[0]
842 newval
= new
.get(key
, 0)
843 newdelta
= newval
- oldval
844 self
.values
[key
] = (newval
, newdelta
)
849 MAX_GUEST_NAME_LEN
= 48
851 DEFAULT_REGEX
= r
'^[^\(]*$'
855 """Instruments curses to draw a nice text ui."""
856 def __init__(self
, stats
):
861 """Initialises curses for later use. Based on curses.wrapper
862 implementation from the Python standard library."""
863 self
.screen
= curses
.initscr()
867 # The try/catch works around a minor bit of
868 # over-conscientiousness in the curses module, the error
869 # return from C start_color() is ignorable.
875 # Hide cursor in extra statement as some monochrome terminals
876 # might support hiding but not colors.
882 curses
.use_default_colors()
885 def __exit__(self
, *exception
):
886 """Resets the terminal to its normal state. Based on curses.wrapper
887 implementation from the Python standard library."""
889 self
.screen
.keypad(0)
895 def get_pid_from_gname(gname
):
896 """Fuzzy function to convert guest name to QEMU process pid.
898 Returns a list of potential pids, can be empty if no match found.
899 Throws an exception on processing errors.
904 child
= subprocess
.Popen(['ps', '-A', '--format', 'pid,args'],
905 stdout
=subprocess
.PIPE
)
908 for line
in child
.stdout
:
909 line
= line
.lstrip().split(' ', 1)
910 # perform a sanity check before calling the more expensive
911 # function to possibly extract the guest name
912 if (' -name ' in line
[1] and
913 gname
== self
.get_gname_from_pid(line
[0])):
914 pids
.append(int(line
[0]))
920 def get_gname_from_pid(pid
):
921 """Returns the guest name for a QEMU process pid.
923 Extracts the guest name from the QEMU comma line by processing the
924 '-name' option. Will also handle names specified out of sequence.
929 line
= open('/proc/{}/cmdline'
930 .format(pid
), 'rb').read().split('\0')
931 parms
= line
[line
.index('-name') + 1].split(',')
933 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
934 # in # ['foo', '', 'bar'], which we revert here
935 idx
= parms
.index('')
936 parms
[idx
- 1] += ',' + parms
[idx
+ 1]
938 # the '-name' switch allows for two ways to specify the guest name,
939 # where the plain name overrides the name specified via 'guest='
944 if arg
[:6] == 'guest=':
946 except (ValueError, IOError, IndexError):
951 def update_drilldown(self
):
952 """Sets or removes a filter that only allows fields without braces."""
953 if not self
.stats
.fields_filter
:
954 self
.stats
.fields_filter
= DEFAULT_REGEX
956 elif self
.stats
.fields_filter
== DEFAULT_REGEX
:
957 self
.stats
.fields_filter
= None
959 def update_pid(self
, pid
):
960 """Propagates pid selection to stats object."""
961 self
.stats
.pid_filter
= pid
963 def refresh_header(self
, pid
=None):
964 """Refreshes the header."""
966 pid
= self
.stats
.pid_filter
968 gname
= self
.get_gname_from_pid(pid
)
970 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
971 if len(gname
) > MAX_GUEST_NAME_LEN
974 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
975 .format(pid
, gname
), curses
.A_BOLD
)
977 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
978 if self
.stats
.fields_filter
and self
.stats
.fields_filter \
980 regex
= self
.stats
.fields_filter
981 if len(regex
) > MAX_REGEX_LEN
:
982 regex
= regex
[:MAX_REGEX_LEN
] + '...'
983 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
984 self
.screen
.addstr(2, 1, '%-40s %10s%7s %7s' %
985 ('Event', 'Total', '%Total', 'Current'))
986 self
.screen
.addstr(4, 1, 'Collecting data...')
987 self
.screen
.refresh()
989 def refresh_body(self
, sleeptime
):
991 self
.screen
.move(row
, 0)
992 self
.screen
.clrtobot()
993 stats
= self
.stats
.get()
997 return (-stats
[x
][1], -stats
[x
][0])
999 return (0, -stats
[x
][0])
1001 for val
in stats
.values():
1003 for key
in sorted(stats
.keys(), key
=sortkey
):
1005 if row
>= self
.screen
.getmaxyx()[0]:
1008 if not values
[0] and not values
[1]:
1010 if values
[0] is not None:
1011 cur
= int(round(values
[1] / sleeptime
)) if values
[1] else ''
1012 self
.screen
.addstr(row
, 1, '%-40s %10d%7.1f %7s' %
1013 (key
, values
[0], values
[0] * 100 / total
,
1016 self
.screen
.refresh()
1018 def show_filter_selection(self
):
1019 """Draws filter selection mask.
1021 Asks for a valid regex and sets the fields filter accordingly.
1026 self
.screen
.addstr(0, 0,
1027 "Show statistics for events matching a regex.",
1029 self
.screen
.addstr(2, 0,
1030 "Current regex: {0}"
1031 .format(self
.stats
.fields_filter
))
1032 self
.screen
.addstr(3, 0, "New regex: ")
1034 regex
= self
.screen
.getstr()
1037 self
.stats
.fields_filter
= DEFAULT_REGEX
1038 self
.refresh_header()
1042 self
.stats
.fields_filter
= regex
1043 self
.refresh_header()
1048 def show_vm_selection_by_pid(self
):
1049 """Draws PID selection mask.
1051 Asks for a pid until a valid pid or 0 has been entered.
1057 self
.screen
.addstr(0, 0,
1058 'Show statistics for specific pid.',
1060 self
.screen
.addstr(1, 0,
1061 'This might limit the shown data to the trace '
1063 self
.screen
.addstr(5, 0, msg
)
1066 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1067 pid
= self
.screen
.getstr()
1073 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1075 msg
= '"' + str(pid
) + '": Not a running process'
1079 self
.refresh_header(pid
)
1080 self
.update_pid(pid
)
1084 msg
= '"' + str(pid
) + '": Not a valid pid'
1086 def show_vm_selection_by_guest_name(self
):
1087 """Draws guest selection mask.
1089 Asks for a guest name until a valid guest name or '' is entered.
1095 self
.screen
.addstr(0, 0,
1096 'Show statistics for specific guest.',
1098 self
.screen
.addstr(1, 0,
1099 'This might limit the shown data to the trace '
1101 self
.screen
.addstr(5, 0, msg
)
1103 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1104 gname
= self
.screen
.getstr()
1108 self
.refresh_header(0)
1114 pids
= self
.get_pid_from_gname(gname
)
1116 msg
= '"' + gname
+ '": Internal error while searching, ' \
1117 'use pid filter instead'
1120 msg
= '"' + gname
+ '": Not an active guest'
1123 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1126 self
.refresh_header(pids
[0])
1127 self
.update_pid(pids
[0])
1130 def show_stats(self
):
1131 """Refreshes the screen and processes user input."""
1132 sleeptime
= DELAY_INITIAL
1133 self
.refresh_header()
1134 start
= 0.0 # result based on init value never appears on screen
1136 self
.refresh_body(time
.time() - start
)
1137 curses
.halfdelay(int(sleeptime
* 10))
1139 sleeptime
= DELAY_REGULAR
1141 char
= self
.screen
.getkey()
1143 self
.update_drilldown()
1147 self
.stats
.fields_filter
= DEFAULT_REGEX
1148 self
.refresh_header(0)
1151 self
.show_filter_selection()
1152 sleeptime
= DELAY_INITIAL
1154 self
.show_vm_selection_by_guest_name()
1155 sleeptime
= DELAY_INITIAL
1157 self
.show_vm_selection_by_pid()
1158 sleeptime
= DELAY_INITIAL
1161 except KeyboardInterrupt:
1163 except curses
.error
:
1168 """Prints statistics in a key, value format."""
1173 for key
in sorted(s
.keys()):
1175 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1176 except KeyboardInterrupt:
1181 """Prints statistics as reiterating key block, multiple value blocks."""
1182 keys
= sorted(stats
.get().iterkeys())
1192 print ' %9d' % s
[k
][1],
1199 if line
% banner_repeat
== 0:
1203 except KeyboardInterrupt:
1208 """Returns processed program arguments."""
1209 description_text
= """
1210 This script displays various statistics about VMs running under KVM.
1211 The statistics are gathered from the KVM debugfs entries and / or the
1212 currently available perf traces.
1214 The monitoring takes additional cpu cycles and might affect the VM's
1219 /sys/kernel/debug/kvm
1220 /sys/kernel/debug/trace/events/*
1222 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1223 CAP_SYS_ADMIN and perf events are used.
1224 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1225 the large number of files that are possibly opened.
1227 Interactive Commands:
1229 f filter by regular expression
1230 g filter by guest name
1233 x toggle reporting of stats for individual child trace events
1235 Press any other key to refresh statistics immediately.
1238 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1239 def format_description(self
, description
):
1241 return description
+ "\n"
1245 def cb_guest_to_pid(option
, opt
, val
, parser
):
1247 pids
= Tui
.get_pid_from_gname(val
)
1249 raise optparse
.OptionValueError('Error while searching for guest '
1250 '"{}", use "-p" to specify a pid '
1251 'instead'.format(val
))
1253 raise optparse
.OptionValueError('No guest by the name "{}" '
1254 'found'.format(val
))
1256 raise optparse
.OptionValueError('Multiple processes found (pids: '
1257 '{}) - use "-p" to specify a pid '
1258 'instead'.format(" ".join(pids
)))
1259 parser
.values
.pid
= pids
[0]
1261 optparser
= optparse
.OptionParser(description
=description_text
,
1262 formatter
=PlainHelpFormatter())
1263 optparser
.add_option('-1', '--once', '--batch',
1264 action
='store_true',
1267 help='run in batch mode for one second',
1269 optparser
.add_option('-l', '--log',
1270 action
='store_true',
1273 help='run in logging mode (like vmstat)',
1275 optparser
.add_option('-t', '--tracepoints',
1276 action
='store_true',
1279 help='retrieve statistics from tracepoints',
1281 optparser
.add_option('-d', '--debugfs',
1282 action
='store_true',
1285 help='retrieve statistics from debugfs',
1287 optparser
.add_option('-f', '--fields',
1289 default
=DEFAULT_REGEX
,
1291 help='fields to display (regex)',
1293 optparser
.add_option('-p', '--pid',
1298 help='restrict statistics to pid',
1300 optparser
.add_option('-g', '--guest',
1305 help='restrict statistics to guest by name',
1306 callback
=cb_guest_to_pid
,
1308 (options
, _
) = optparser
.parse_args(sys
.argv
)
1312 def check_access(options
):
1313 """Exits if the current user can't access all needed directories."""
1314 if not os
.path
.exists('/sys/kernel/debug'):
1315 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1318 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1319 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1320 "readable by the current user:\n"
1321 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1322 "Also ensure, that the kvm modules are loaded.\n")
1325 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1326 not options
.debugfs
):
1327 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1328 "when using the option -t (default).\n"
1329 "If it is enabled, make {0} readable by the "
1331 .format(PATH_DEBUGFS_TRACING
))
1332 if options
.tracepoints
:
1335 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1336 options
.debugfs
= True
1343 options
= get_options()
1344 options
= check_access(options
)
1346 if (options
.pid
> 0 and
1347 not os
.path
.isdir(os
.path
.join('/proc/',
1348 str(options
.pid
)))):
1349 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1350 sys
.exit('Specified pid does not exist.')
1352 stats
= Stats(options
)
1356 elif not options
.once
:
1357 with
Tui(stats
) as tui
:
1362 if __name__
== "__main__":