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.
22 from __future__ import print_function
36 from collections import defaultdict, namedtuple
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 'SET_FILTER': 0x40082406,
225 'ENABLE': 0x00002400,
226 'DISABLE': 0x00002401,
230 ENCODING = locale.getpreferredencoding(False)
231 TRACE_FILTER = re.compile(r'^[^\(]*$')
235 """Encapsulates global architecture specific data.
237 Contains the performance event open syscall and ioctl numbers, as
238 well as the VM exit reasons for the architecture it runs on.
243 machine = os.uname()[4]
245 if machine.startswith('ppc'):
247 elif machine.startswith('aarch64'):
249 elif machine.startswith('s390'):
253 for line in open('/proc/cpuinfo'):
254 if not line.startswith('flags'):
259 return ArchX86(VMX_EXIT_REASONS)
261 return ArchX86(SVM_EXIT_REASONS)
264 def tracepoint_is_child(self, field):
265 if (TRACE_FILTER.match(field)):
267 return field.split('(', 1)[0]
271 def __init__(self, exit_reasons):
272 self.sc_perf_evt_open = 298
273 self.ioctl_numbers = IOCTL_NUMBERS
274 self.exit_reasons = exit_reasons
276 def debugfs_is_child(self, field):
277 """ Returns name of parent if 'field' is a child, None otherwise """
283 self.sc_perf_evt_open = 319
284 self.ioctl_numbers = IOCTL_NUMBERS
285 self.ioctl_numbers['ENABLE'] = 0x20002400
286 self.ioctl_numbers['DISABLE'] = 0x20002401
287 self.ioctl_numbers['RESET'] = 0x20002403
289 # PPC comes in 32 and 64 bit and some generated ioctl
290 # numbers depend on the wordsize.
291 char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
292 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
293 self.exit_reasons = {}
295 def debugfs_is_child(self, field):
296 """ Returns name of parent if 'field' is a child, None otherwise """
302 self.sc_perf_evt_open = 241
303 self.ioctl_numbers = IOCTL_NUMBERS
304 self.exit_reasons = AARCH64_EXIT_REASONS
306 def debugfs_is_child(self, field):
307 """ Returns name of parent if 'field' is a child, None otherwise """
311 class ArchS390(Arch):
313 self.sc_perf_evt_open = 331
314 self.ioctl_numbers = IOCTL_NUMBERS
315 self.exit_reasons = None
317 def debugfs_is_child(self, field):
318 """ Returns name of parent if 'field' is a child, None otherwise """
319 if field.startswith('instruction_'):
320 return 'exit_instruction'
323 ARCH = Arch.get_arch()
326 class perf_event_attr(ctypes.Structure):
327 """Struct that holds the necessary data to set up a trace event.
329 For an extensive explanation see perf_event_open(2) and
330 include/uapi/linux/perf_event.h, struct perf_event_attr
332 All fields that are not initialized in the constructor are 0.
335 _fields_ = [('type', ctypes.c_uint32),
336 ('size', ctypes.c_uint32),
337 ('config', ctypes.c_uint64),
338 ('sample_freq', ctypes.c_uint64),
339 ('sample_type', ctypes.c_uint64),
340 ('read_format', ctypes.c_uint64),
341 ('flags', ctypes.c_uint64),
342 ('wakeup_events', ctypes.c_uint32),
343 ('bp_type', ctypes.c_uint32),
344 ('bp_addr', ctypes.c_uint64),
345 ('bp_len', ctypes.c_uint64),
349 super(self.__class__, self).__init__()
350 self.type = PERF_TYPE_TRACEPOINT
351 self.size = ctypes.sizeof(self)
352 self.read_format = PERF_FORMAT_GROUP
355 PERF_TYPE_TRACEPOINT = 2
356 PERF_FORMAT_GROUP = 1 << 3
360 """Represents a perf event group."""
365 def add_event(self, event):
366 self.events.append(event)
369 """Returns a dict with 'event name: value' for all events in the
372 Values are read by reading from the file descriptor of the
373 event that is the group leader. See perf_event_open(2) for
376 Read format for the used event configuration is:
378 u64 nr; /* The number of events */
380 u64 value; /* The value of the event */
385 length = 8 * (1 + len(self.events))
386 read_format = 'xxxxxxxx' + 'Q' * len(self.events)
387 return dict(zip([event.name for event in self.events],
388 struct.unpack(read_format,
389 os.read(self.events[0].fd, length))))
393 """Represents a performance event and manages its life cycle."""
394 def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
395 trace_filter, trace_set='kvm'):
396 self.libc = ctypes.CDLL('libc.so.6', use_errno=True)
397 self.syscall = self.libc.syscall
400 self._setup_event(group, trace_cpu, trace_pid, trace_point,
401 trace_filter, trace_set)
404 """Closes the event's file descriptor.
406 As no python file object was created for the file descriptor,
407 python will not reference count the descriptor and will not
408 close it itself automatically, so we do it.
414 def _perf_event_open(self, attr, pid, cpu, group_fd, flags):
415 """Wrapper for the sys_perf_evt_open() syscall.
417 Used to set up performance events, returns a file descriptor or -1
422 - struct perf_event_attr *
423 - pid or -1 to monitor all pids
424 - cpu number or -1 to monitor all cpus
425 - The file descriptor of the group leader or -1 to create a group.
429 return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
430 ctypes.c_int(pid), ctypes.c_int(cpu),
431 ctypes.c_int(group_fd), ctypes.c_long(flags))
433 def _setup_event_attribute(self, trace_set, trace_point):
434 """Returns an initialized ctype perf_event_attr struct."""
436 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
439 event_attr = perf_event_attr()
440 event_attr.config = int(open(id_path).read())
443 def _setup_event(self, group, trace_cpu, trace_pid, trace_point,
444 trace_filter, trace_set):
445 """Sets up the perf event in Linux.
447 Issues the syscall to register the event in the kernel and
448 then sets the optional filter.
452 event_attr = self._setup_event_attribute(trace_set, trace_point)
454 # First event will be group leader.
457 # All others have to pass the leader's descriptor instead.
459 group_leader = group.events[0].fd
461 fd = self._perf_event_open(event_attr, trace_pid,
462 trace_cpu, group_leader, 0)
464 err = ctypes.get_errno()
465 raise OSError(err, os.strerror(err),
466 'while calling sys_perf_event_open().')
469 fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
475 """Enables the trace event in the kernel.
477 Enabling the group leader makes reading counters from it and the
478 events under it possible.
481 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
484 """Disables the trace event in the kernel.
486 Disabling the group leader makes reading all counters under it
490 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
493 """Resets the count of the trace event in the kernel."""
494 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
497 class Provider(object):
498 """Encapsulates functionalities used by all providers."""
499 def __init__(self, pid):
500 self.child_events = False
504 def is_field_wanted(fields_filter, field):
505 """Indicate whether field is valid according to fields_filter."""
506 if not fields_filter:
508 return re.match(fields_filter, field) is not None
512 """Returns os.walk() data for specified directory.
514 As it is only a wrapper it returns the same 3-tuple of (dirpath,
515 dirnames, filenames).
517 return next(os.walk(path))
520 class TracepointProvider(Provider):
521 """Data provider for the stats class.
523 Manages the events/groups from which it acquires its data.
526 def __init__(self, pid, fields_filter):
527 self.group_leaders = []
528 self.filters = self._get_filters()
529 self.update_fields(fields_filter)
530 super(TracepointProvider, self).__init__(pid)
534 """Returns a dict of trace events, their filter ids and
535 the values that can be filtered.
537 Trace events can be filtered for special values by setting a
538 filter string via an ioctl. The string normally has the format
539 identifier==value. For each filter a new event will be created, to
540 be able to distinguish the events.
544 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
545 if ARCH.exit_reasons:
546 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
549 def _get_available_fields(self):
550 """Returns a list of available events of format 'event name(filter
553 All available events have directories under
554 /sys/kernel/debug/tracing/events/ which export information
555 about the specific event. Therefore, listing the dirs gives us
556 a list of all available events.
558 Some events like the vm exit reasons can be filtered for
559 specific values. To take account for that, the routine below
560 creates special fields with the following format:
561 event name(filter name)
564 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
565 fields = self.walkdir(path)[1]
568 if field in self.filters:
569 filter_name_, filter_dicts = self.filters[field]
570 for name in filter_dicts:
571 extra.append(field + '(' + name + ')')
575 def update_fields(self, fields_filter):
576 """Refresh fields, applying fields_filter"""
577 self.fields = [field for field in self._get_available_fields()
578 if self.is_field_wanted(fields_filter, field) or
579 ARCH.tracepoint_is_child(field)]
582 def _get_online_cpus():
583 """Returns a list of cpu id integers."""
584 def parse_int_list(list_string):
585 """Returns an int list from a string of comma separated integers and
588 members = list_string.split(',')
590 for member in members:
591 if '-' not in member:
592 integers.append(int(member))
594 int_range = member.split('-')
595 integers.extend(range(int(int_range[0]),
596 int(int_range[1]) + 1))
600 with open('/sys/devices/system/cpu/online') as cpu_list:
601 cpu_string = cpu_list.readline()
602 return parse_int_list(cpu_string)
604 def _setup_traces(self):
605 """Creates all event and group objects needed to be able to retrieve
607 fields = self._get_available_fields()
609 # Fetch list of all threads of the monitored pid, as qemu
610 # starts a thread for each vcpu.
611 path = os.path.join('/proc', str(self._pid), 'task')
612 groupids = self.walkdir(path)[1]
614 groupids = self._get_online_cpus()
616 # The constant is needed as a buffer for python libs, std
617 # streams and other files that the script opens.
618 newlim = len(groupids) * len(fields) + 50
620 softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
623 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
624 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
626 # Raising the soft limit is sufficient.
627 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
630 sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
632 for groupid in groupids:
637 match = re.match(r'(.*)\((.*)\)', name)
639 tracepoint, sub = match.groups()
640 tracefilter = ('%s==%d\0' %
641 (self.filters[tracepoint][0],
642 self.filters[tracepoint][1][sub]))
644 # From perf_event_open(2):
645 # pid > 0 and cpu == -1
646 # This measures the specified process/thread on any CPU.
648 # pid == -1 and cpu >= 0
649 # This measures all processes/threads on the specified CPU.
650 trace_cpu = groupid if self._pid == 0 else -1
651 trace_pid = int(groupid) if self._pid != 0 else -1
653 group.add_event(Event(name=name,
657 trace_point=tracepoint,
658 trace_filter=tracefilter))
660 self.group_leaders.append(group)
667 def fields(self, fields):
668 """Enables/disables the (un)wanted events"""
669 self._fields = fields
670 for group in self.group_leaders:
671 for index, event in enumerate(group.events):
672 if event.name in fields:
676 # Do not disable the group leader.
677 # It would disable all of its events.
687 """Changes the monitored pid by setting new traces."""
689 # The garbage collector will get rid of all Event/Group
690 # objects and open files after removing the references.
691 self.group_leaders = []
693 self.fields = self._fields
695 def read(self, by_guest=0):
696 """Returns 'event name: current value' for all enabled events."""
697 ret = defaultdict(int)
698 for group in self.group_leaders:
699 for name, val in group.read().items():
700 if name not in self._fields:
702 parent = ARCH.tracepoint_is_child(name)
709 """Reset all field counters"""
710 for group in self.group_leaders:
711 for event in group.events:
715 class DebugfsProvider(Provider):
716 """Provides data from the files that KVM creates in the kvm debugfs
718 def __init__(self, pid, fields_filter, include_past):
719 self.update_fields(fields_filter)
723 super(DebugfsProvider, self).__init__(pid)
727 def _get_available_fields(self):
728 """"Returns a list of available fields.
730 The fields are all available KVM debugfs files
733 return self.walkdir(PATH_DEBUGFS_KVM)[2]
735 def update_fields(self, fields_filter):
736 """Refresh fields, applying fields_filter"""
737 self._fields = [field for field in self._get_available_fields()
738 if self.is_field_wanted(fields_filter, field) or
739 ARCH.debugfs_is_child(field)]
746 def fields(self, fields):
747 self._fields = fields
758 vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
762 self.paths = list(filter(lambda x: "{}-".format(pid) in x, vms))
768 def _verify_paths(self):
769 """Remove invalid paths"""
770 for path in self.paths:
771 if not os.path.exists(os.path.join(PATH_DEBUGFS_KVM, path)):
772 self.paths.remove(path)
775 def read(self, reset=0, by_guest=0):
776 """Returns a dict with format:'file name / field -> current value'.
780 1 reset field counts to 0
781 2 restore the original field counts
786 # If no debugfs filtering support is available, then don't read.
794 for entry in os.walk(PATH_DEBUGFS_KVM):
798 for field in self._fields:
799 value = self._read_field(field, path)
802 self._baseline[key] = value
804 self._baseline[key] = 0
805 if self._baseline.get(key, -1) == -1:
806 self._baseline[key] = value
807 parent = ARCH.debugfs_is_child(field)
809 field = field + ' ' + parent
812 field = key.split('-')[0] # set 'field' to 'pid'
813 increment = value - self._baseline.get(key, 0)
815 results[field] += increment
817 results[field] = increment
821 def _read_field(self, field, path):
822 """Returns the value of a single field from a specific VM."""
824 return int(open(os.path.join(PATH_DEBUGFS_KVM,
832 """Reset field counters"""
837 """Reset field counters"""
842 EventStat = namedtuple('EventStat', ['value', 'delta'])
846 """Manages the data providers and the data they provide.
848 It is used to set filters on the provider's data and collect all
852 def __init__(self, options):
853 self.providers = self._get_providers(options)
854 self._pid_filter = options.pid
855 self._fields_filter = options.fields
857 self._child_events = False
859 def _get_providers(self, options):
860 """Returns a list of data providers depending on the passed options."""
864 providers.append(DebugfsProvider(options.pid, options.fields,
865 options.dbgfs_include_past))
866 if options.tracepoints or not providers:
867 providers.append(TracepointProvider(options.pid, options.fields))
871 def _update_provider_filters(self):
872 """Propagates fields filters to providers."""
873 # As we reset the counters when updating the fields we can
874 # also clear the cache of old values.
876 for provider in self.providers:
877 provider.update_fields(self._fields_filter)
881 for provider in self.providers:
885 def fields_filter(self):
886 return self._fields_filter
888 @fields_filter.setter
889 def fields_filter(self, fields_filter):
890 if fields_filter != self._fields_filter:
891 self._fields_filter = fields_filter
892 self._update_provider_filters()
895 def pid_filter(self):
896 return self._pid_filter
899 def pid_filter(self, pid):
900 if pid != self._pid_filter:
901 self._pid_filter = pid
903 for provider in self.providers:
904 provider.pid = self._pid_filter
907 def child_events(self):
908 return self._child_events
911 def child_events(self, val):
912 self._child_events = val
913 for provider in self.providers:
914 provider.child_events = val
916 def get(self, by_guest=0):
917 """Returns a dict with field -> (value, delta to last value) of all
920 * plain: 'key' is event name
921 * child-parent: 'key' is in format '<child> <parent>'
922 * pid: 'key' is the pid of the guest, and the record contains the
923 aggregated event data
924 These formats are generated by the providers, and handled in class TUI.
926 for provider in self.providers:
927 new = provider.read(by_guest=by_guest)
929 oldval = self.values.get(key, EventStat(0, 0)).value
930 newval = new.get(key, 0)
931 newdelta = newval - oldval
932 self.values[key] = EventStat(newval, newdelta)
935 def toggle_display_guests(self, to_pid):
936 """Toggle between collection of stats by individual event and by
939 Events reported by DebugfsProvider change when switching to/from
940 reading by guest values. Hence we have to remove the excess event
941 names from self.values.
944 if any(isinstance(ins, TracepointProvider) for ins in self.providers):
947 for provider in self.providers:
948 if isinstance(provider, DebugfsProvider):
949 for key in provider.fields:
950 if key in self.values.keys():
953 oldvals = self.values.copy()
957 # Update oldval (see get())
963 MAX_GUEST_NAME_LEN = 48
969 """Instruments curses to draw a nice text ui."""
970 def __init__(self, stats):
973 self._delay_initial = 0.25
974 self._delay_regular = DELAY_DEFAULT
975 self._sorting = SORT_DEFAULT
976 self._display_guests = 0
979 """Initialises curses for later use. Based on curses.wrapper
980 implementation from the Python standard library."""
981 self.screen = curses.initscr()
985 # The try/catch works around a minor bit of
986 # over-conscientiousness in the curses module, the error
987 # return from C start_color() is ignorable.
993 # Hide cursor in extra statement as some monochrome terminals
994 # might support hiding but not colors.
1000 curses.use_default_colors()
1003 def __exit__(self, *exception):
1004 """Resets the terminal to its normal state. Based on curses.wrapper
1005 implementation from the Python standard library."""
1007 self.screen.keypad(0)
1013 def get_all_gnames():
1014 """Returns a list of (pid, gname) tuples of all running guests"""
1017 child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
1018 stdout=subprocess.PIPE)
1021 for line in child.stdout:
1022 line = line.decode(ENCODING).lstrip().split(' ', 1)
1023 # perform a sanity check before calling the more expensive
1024 # function to possibly extract the guest name
1025 if ' -name ' in line[1]:
1026 res.append((line[0], Tui.get_gname_from_pid(line[0])))
1027 child.stdout.close()
1031 def _print_all_gnames(self, row):
1032 """Print a list of all running guests along with their pids."""
1033 self.screen.addstr(row, 2, '%8s %-60s' %
1034 ('Pid', 'Guest Name (fuzzy list, might be '
1039 for line in self.get_all_gnames():
1040 self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1]))
1042 if row >= self.screen.getmaxyx()[0]:
1045 self.screen.addstr(row + 1, 2, 'Not available')
1048 def get_pid_from_gname(gname):
1049 """Fuzzy function to convert guest name to QEMU process pid.
1051 Returns a list of potential pids, can be empty if no match found.
1052 Throws an exception on processing errors.
1056 for line in Tui.get_all_gnames():
1057 if gname == line[1]:
1058 pids.append(int(line[0]))
1063 def get_gname_from_pid(pid):
1064 """Returns the guest name for a QEMU process pid.
1066 Extracts the guest name from the QEMU comma line by processing the
1067 '-name' option. Will also handle names specified out of sequence.
1072 line = open('/proc/{}/cmdline'
1073 .format(pid), 'r').read().split('\0')
1074 parms = line[line.index('-name') + 1].split(',')
1076 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1077 # in # ['foo', '', 'bar'], which we revert here
1078 idx = parms.index('')
1079 parms[idx - 1] += ',' + parms[idx + 1]
1080 del parms[idx:idx+2]
1081 # the '-name' switch allows for two ways to specify the guest name,
1082 # where the plain name overrides the name specified via 'guest='
1087 if arg[:6] == 'guest=':
1089 except (ValueError, IOError, IndexError):
1094 def _update_pid(self, pid):
1095 """Propagates pid selection to stats object."""
1096 self.screen.addstr(4, 1, 'Updating pid filter...')
1097 self.screen.refresh()
1098 self.stats.pid_filter = pid
1100 def _refresh_header(self, pid=None):
1101 """Refreshes the header."""
1103 pid = self.stats.pid_filter
1105 gname = self.get_gname_from_pid(pid)
1108 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1109 if len(gname) > MAX_GUEST_NAME_LEN
1112 self._headline = 'kvm statistics - pid {0} {1}'.format(pid, gname)
1114 self._headline = 'kvm statistics - summary'
1115 self.screen.addstr(0, 0, self._headline, curses.A_BOLD)
1116 if self.stats.fields_filter:
1117 regex = self.stats.fields_filter
1118 if len(regex) > MAX_REGEX_LEN:
1119 regex = regex[:MAX_REGEX_LEN] + '...'
1120 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1121 if self._display_guests:
1122 col_name = 'Guest Name'
1125 self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1126 (col_name, 'Total', '%Total', 'CurAvg/s'),
1128 self.screen.addstr(4, 1, 'Collecting data...')
1129 self.screen.refresh()
1131 def _refresh_body(self, sleeptime):
1132 def insert_child(sorted_items, child, values, parent):
1133 num = len(sorted_items)
1134 for i in range(0, num):
1135 # only add child if parent is present
1136 if parent.startswith(sorted_items[i][0]):
1137 sorted_items.insert(i + 1, (' ' + child, values))
1139 def get_sorted_events(self, stats):
1140 """ separate parent and child events """
1141 if self._sorting == SORT_DEFAULT:
1143 # sort by (delta value, overall value)
1145 return (v.delta, v.value)
1148 # sort by overall value
1154 # we can't rule out child events to appear prior to parents even
1155 # when sorted - separate out all children first, and add in later
1156 for key, values in sorted(stats.items(), key=sortkey,
1158 if values == (0, 0):
1160 if key.find(' ') != -1:
1161 if not self.stats.child_events:
1163 childs.insert(0, (key, values))
1165 sorted_items.append((key, values))
1166 if self.stats.child_events:
1167 for key, values in childs:
1168 (child, parent) = key.split(' ')
1169 insert_child(sorted_items, child, values, parent)
1173 if not self._is_running_guest(self.stats.pid_filter):
1175 try: # ...to identify the guest by name in case it's back
1176 pids = self.get_pid_from_gname(self._gname)
1178 self._refresh_header(pids[0])
1179 self._update_pid(pids[0])
1183 self._display_guest_dead()
1184 # leave final data on screen
1187 self.screen.move(row, 0)
1188 self.screen.clrtobot()
1189 stats = self.stats.get(self._display_guests)
1192 for key, values in stats.items():
1193 if self._display_guests:
1194 if self.get_gname_from_pid(key):
1195 total += values.value
1197 if not key.find(' ') != -1:
1198 total += values.value
1200 ctotal += values.value
1202 # we don't have any fields, or all non-child events are filtered
1208 guest_removed = False
1209 for key, values in get_sorted_events(self, stats):
1210 if row >= self.screen.getmaxyx()[0] - 1 or values == (0, 0):
1212 if self._display_guests:
1213 key = self.get_gname_from_pid(key)
1216 cur = int(round(values.delta / sleeptime)) if values.delta else 0
1218 guest_removed = True
1222 tcur += values.delta
1223 ptotal = values.value
1227 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % (key,
1229 values.value * 100 / float(ltotal), cur))
1233 self.screen.addstr(4, 1, 'Guest removed, updating...')
1235 self.screen.addstr(4, 1, 'No matching events reported yet')
1237 tavg = int(round(tcur / sleeptime)) if tcur > 0 else ''
1238 self.screen.addstr(row, 1, '%-40s %10d %8s' %
1239 ('Total', total, tavg), curses.A_BOLD)
1240 self.screen.refresh()
1242 def _display_guest_dead(self):
1243 marker = ' Guest is DEAD '
1244 y = min(len(self._headline), 80 - len(marker))
1245 self.screen.addstr(0, y, marker, curses.A_BLINK | curses.A_STANDOUT)
1247 def _show_msg(self, text):
1248 """Display message centered text and exit on key press"""
1249 hint = 'Press any key to continue'
1252 (x, term_width) = self.screen.getmaxyx()
1255 start = (term_width - len(line)) // 2
1256 self.screen.addstr(row, start, line)
1258 self.screen.addstr(row + 1, (term_width - len(hint)) // 2, hint,
1260 self.screen.getkey()
1262 def _show_help_interactive(self):
1263 """Display help with list of interactive commands"""
1264 msg = (' b toggle events by guests (debugfs only, honors'
1267 ' f filter by regular expression',
1268 ' g filter by guest name/PID',
1269 ' h display interactive commands reference',
1270 ' o toggle sorting order (Total vs CurAvg/s)',
1271 ' p filter by guest name/PID',
1274 ' s set update interval',
1275 ' x toggle reporting of stats for individual child trace'
1277 'Any other key refreshes statistics immediately')
1280 self.screen.addstr(0, 0, "Interactive commands reference",
1282 self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1285 self.screen.addstr(row, 0, line)
1287 self.screen.getkey()
1288 self._refresh_header()
1290 def _show_filter_selection(self):
1291 """Draws filter selection mask.
1293 Asks for a valid regex and sets the fields filter accordingly.
1299 self.screen.addstr(0, 0,
1300 "Show statistics for events matching a regex.",
1302 self.screen.addstr(2, 0,
1303 "Current regex: {0}"
1304 .format(self.stats.fields_filter))
1305 self.screen.addstr(5, 0, msg)
1306 self.screen.addstr(3, 0, "New regex: ")
1308 regex = self.screen.getstr().decode(ENCODING)
1311 self.stats.fields_filter = ''
1312 self._refresh_header()
1316 self.stats.fields_filter = regex
1317 self._refresh_header()
1320 msg = '"' + regex + '": Not a valid regular expression'
1323 def _show_set_update_interval(self):
1324 """Draws update interval selection mask."""
1328 self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1329 DELAY_DEFAULT, curses.A_BOLD)
1330 self.screen.addstr(4, 0, msg)
1331 self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1332 self._delay_regular)
1334 val = self.screen.getstr().decode(ENCODING)
1341 msg = '"' + str(val) + '": Value must be >=0.1'
1344 msg = '"' + str(val) + '": Value must be <=25.5'
1347 delay = DELAY_DEFAULT
1348 self._delay_regular = delay
1352 msg = '"' + str(val) + '": Invalid value'
1353 self._refresh_header()
1355 def _is_running_guest(self, pid):
1356 """Check if pid is still a running process."""
1359 return os.path.isdir(os.path.join('/proc/', str(pid)))
1361 def _show_vm_selection_by_guest(self):
1362 """Draws guest selection mask.
1364 Asks for a guest name or pid until a valid guest name or '' is entered.
1370 self.screen.addstr(0, 0,
1371 'Show statistics for specific guest or pid.',
1373 self.screen.addstr(1, 0,
1374 'This might limit the shown data to the trace '
1376 self.screen.addstr(5, 0, msg)
1377 self._print_all_gnames(7)
1380 self.screen.addstr(3, 0, "Guest or pid [ENTER exits]: ")
1381 guest = self.screen.getstr().decode(ENCODING)
1385 if not guest or guest == '0':
1388 if not self._is_running_guest(guest):
1389 msg = '"' + guest + '": Not a running process'
1395 pids = self.get_pid_from_gname(guest)
1397 msg = '"' + guest + '": Internal error while searching, ' \
1398 'use pid filter instead'
1401 msg = '"' + guest + '": Not an active guest'
1404 msg = '"' + guest + '": Multiple matches found, use pid ' \
1410 self._refresh_header(pid)
1411 self._update_pid(pid)
1413 def show_stats(self):
1414 """Refreshes the screen and processes user input."""
1415 sleeptime = self._delay_initial
1416 self._refresh_header()
1417 start = 0.0 # result based on init value never appears on screen
1419 self._refresh_body(time.time() - start)
1420 curses.halfdelay(int(sleeptime * 10))
1422 sleeptime = self._delay_regular
1424 char = self.screen.getkey()
1426 self._display_guests = not self._display_guests
1427 if self.stats.toggle_display_guests(self._display_guests):
1428 self._show_msg(['Command not available with '
1429 'tracepoints enabled', 'Restart with '
1430 'debugfs only (see option \'-d\') and '
1432 self._display_guests = not self._display_guests
1433 self._refresh_header()
1435 self.stats.fields_filter = ''
1436 self._refresh_header(0)
1440 self._show_filter_selection()
1442 sleeptime = self._delay_initial
1443 if char == 'g' or char == 'p':
1444 self._show_vm_selection_by_guest()
1445 sleeptime = self._delay_initial
1447 self._show_help_interactive()
1449 self._sorting = not self._sorting
1456 self._show_set_update_interval()
1458 sleeptime = self._delay_initial
1460 self.stats.child_events = not self.stats.child_events
1461 except KeyboardInterrupt:
1463 except curses.error:
1468 """Prints statistics in a key, value format."""
1473 for key, values in sorted(s.items()):
1474 print('%-42s%10d%10d' % (key.split(' ')[0], values.value,
1476 except KeyboardInterrupt:
1481 """Prints statistics as reiterating key block, multiple value blocks."""
1482 keys = sorted(stats.get().keys())
1486 print(key.split(' ')[0], end=' ')
1492 print(' %9d' % s[key].delta, end=' ')
1499 if line % banner_repeat == 0:
1503 except KeyboardInterrupt:
1508 """Returns processed program arguments."""
1509 description_text = """
1510 This script displays various statistics about VMs running under KVM.
1511 The statistics are gathered from the KVM debugfs entries and / or the
1512 currently available perf traces.
1514 The monitoring takes additional cpu cycles and might affect the VM's
1522 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1523 CAP_SYS_ADMIN and perf events are used.
1524 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1525 the large number of files that are possibly opened.
1527 Interactive Commands:
1528 b toggle events by guests (debugfs only, honors filters)
1530 f filter by regular expression
1531 g filter by guest name
1532 h display interactive commands reference
1533 o toggle sorting order (Total vs CurAvg/s)
1537 s set update interval
1538 x toggle reporting of stats for individual child trace events
1539 Press any other key to refresh statistics immediately.
1540 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1542 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1543 def format_description(self, description):
1545 return description + "\n"
1549 def cb_guest_to_pid(option, opt, val, parser):
1551 pids = Tui.get_pid_from_gname(val)
1553 sys.exit('Error while searching for guest "{}". Use "-p" to '
1554 'specify a pid instead?'.format(val))
1556 sys.exit('Error: No guest by the name "{}" found'.format(val))
1558 sys.exit('Error: Multiple processes found (pids: {}). Use "-p" '
1559 'to specify the desired pid'.format(" ".join(pids)))
1560 parser.values.pid = pids[0]
1562 optparser = optparse.OptionParser(description=description_text,
1563 formatter=PlainHelpFormatter())
1564 optparser.add_option('-1', '--once', '--batch',
1565 action='store_true',
1568 help='run in batch mode for one second',
1570 optparser.add_option('-i', '--debugfs-include-past',
1571 action='store_true',
1573 dest='dbgfs_include_past',
1574 help='include all available data on past events for '
1577 optparser.add_option('-l', '--log',
1578 action='store_true',
1581 help='run in logging mode (like vmstat)',
1583 optparser.add_option('-t', '--tracepoints',
1584 action='store_true',
1587 help='retrieve statistics from tracepoints',
1589 optparser.add_option('-d', '--debugfs',
1590 action='store_true',
1593 help='retrieve statistics from debugfs',
1595 optparser.add_option('-f', '--fields',
1599 help='''fields to display (regex)
1600 "-f help" for a list of available events''',
1602 optparser.add_option('-p', '--pid',
1607 help='restrict statistics to pid',
1609 optparser.add_option('-g', '--guest',
1614 help='restrict statistics to guest by name',
1615 callback=cb_guest_to_pid,
1617 options, unkn = optparser.parse_args(sys.argv)
1619 sys.exit('Error: Extra argument(s): ' + ' '.join(unkn[1:]))
1621 # verify that we were passed a valid regex up front
1622 re.compile(options.fields)
1624 sys.exit('Error: "' + options.fields + '" is not a valid regular '
1630 def check_access(options):
1631 """Exits if the current user can't access all needed directories."""
1632 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1633 not options.debugfs):
1634 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1635 "when using the option -t (default).\n"
1636 "If it is enabled, make {0} readable by the "
1638 .format(PATH_DEBUGFS_TRACING))
1639 if options.tracepoints:
1642 sys.stderr.write("Falling back to debugfs statistics!\n")
1643 options.debugfs = True
1649 def assign_globals():
1650 global PATH_DEBUGFS_KVM
1651 global PATH_DEBUGFS_TRACING
1654 for line in open('/proc/mounts'):
1655 if line.split(' ')[0] == 'debugfs':
1656 debugfs = line.split(' ')[1]
1659 sys.stderr.write("Please make sure that CONFIG_DEBUG_FS is enabled in "
1660 "your kernel, mounted and\nreadable by the current "
1662 "('mount -t debugfs debugfs /sys/kernel/debug')\n")
1665 PATH_DEBUGFS_KVM = os.path.join(debugfs, 'kvm')
1666 PATH_DEBUGFS_TRACING = os.path.join(debugfs, 'tracing')
1668 if not os.path.exists(PATH_DEBUGFS_KVM):
1669 sys.stderr.write("Please make sure that CONFIG_KVM is enabled in "
1670 "your kernel and that the modules are loaded.\n")
1676 options = get_options()
1677 options = check_access(options)
1679 if (options.pid > 0 and
1680 not os.path.isdir(os.path.join('/proc/',
1681 str(options.pid)))):
1682 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1683 sys.exit('Specified pid does not exist.')
1685 stats = Stats(options)
1687 if options.fields == 'help':
1688 stats.fields_filter = None
1690 for key in stats.get().keys():
1691 event_list.append(key.split('(', 1)[0])
1692 sys.stdout.write(' ' + '\n '.join(sorted(set(event_list))) + '\n')
1697 elif not options.once:
1698 with Tui(stats) as tui:
1703 if __name__ == "__main__":