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 or fields_filter == "help":
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
665 def read(self, by_guest=0):
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, include_past):
685 self.update_fields(fields_filter)
693 def get_available_fields(self):
694 """"Returns a list of available fields.
696 The fields are all available KVM debugfs files
699 return self.walkdir(PATH_DEBUGFS_KVM)[2]
701 def update_fields(self, fields_filter):
702 """Refresh fields, applying fields_filter"""
703 self._fields = [field for field in self.get_available_fields()
704 if self.is_field_wanted(fields_filter, field)]
711 def fields(self, fields):
712 self._fields = fields
723 vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
727 self.paths = filter(lambda x: "{}-".format(pid) in x, vms)
734 def read(self, reset=0, by_guest=0):
735 """Returns a dict with format:'file name / field -> current value'.
739 1 reset field counts to 0
740 2 restore the original field counts
745 # If no debugfs filtering support is available, then don't read.
752 for entry in os.walk(PATH_DEBUGFS_KVM):
756 for field in self._fields:
757 value = self.read_field(field, path)
760 self._baseline[key] = value
762 self._baseline[key] = 0
763 if self._baseline.get(key, -1) == -1:
764 self._baseline[key] = value
765 increment = (results.get(field, 0) + value -
766 self._baseline.get(key, 0))
768 pid = key.split('-')[0]
770 results[pid] += increment
772 results[pid] = increment
774 results[field] = increment
778 def read_field(self, field, path):
779 """Returns the value of a single field from a specific VM."""
781 return int(open(os.path.join(PATH_DEBUGFS_KVM,
789 """Reset field counters"""
794 """Reset field counters"""
800 """Manages the data providers and the data they provide.
802 It is used to set filters on the provider's data and collect all
806 def __init__(self, options):
807 self.providers = self.get_providers(options)
808 self._pid_filter = options.pid
809 self._fields_filter = options.fields
813 def get_providers(options):
814 """Returns a list of data providers depending on the passed options."""
818 providers.append(DebugfsProvider(options.pid, options.fields,
819 options.dbgfs_include_past))
820 if options.tracepoints or not providers:
821 providers.append(TracepointProvider(options.pid, options.fields))
825 def update_provider_filters(self):
826 """Propagates fields filters to providers."""
827 # As we reset the counters when updating the fields we can
828 # also clear the cache of old values.
830 for provider in self.providers:
831 provider.update_fields(self._fields_filter)
835 for provider in self.providers:
839 def fields_filter(self):
840 return self._fields_filter
842 @fields_filter.setter
843 def fields_filter(self, fields_filter):
844 if fields_filter != self._fields_filter:
845 self._fields_filter = fields_filter
846 self.update_provider_filters()
849 def pid_filter(self):
850 return self._pid_filter
853 def pid_filter(self, pid):
854 if pid != self._pid_filter:
855 self._pid_filter = pid
857 for provider in self.providers:
858 provider.pid = self._pid_filter
860 def get(self, by_guest=0):
861 """Returns a dict with field -> (value, delta to last value) of all
863 for provider in self.providers:
864 new = provider.read(by_guest=by_guest)
865 for key in new if by_guest else provider.fields:
866 oldval = self.values.get(key, (0, 0))[0]
867 newval = new.get(key, 0)
868 newdelta = newval - oldval
869 self.values[key] = (newval, newdelta)
872 def toggle_display_guests(self, to_pid):
873 """Toggle between collection of stats by individual event and by
876 Events reported by DebugfsProvider change when switching to/from
877 reading by guest values. Hence we have to remove the excess event
878 names from self.values.
881 if any(isinstance(ins, TracepointProvider) for ins in self.providers):
884 for provider in self.providers:
885 if isinstance(provider, DebugfsProvider):
886 for key in provider.fields:
887 if key in self.values.keys():
890 oldvals = self.values.copy()
894 # Update oldval (see get())
899 MAX_GUEST_NAME_LEN = 48
901 DEFAULT_REGEX = r'^[^\(]*$'
906 """Instruments curses to draw a nice text ui."""
907 def __init__(self, stats):
910 self._delay_initial = 0.25
911 self._delay_regular = DELAY_DEFAULT
912 self._sorting = SORT_DEFAULT
913 self._display_guests = 0
916 """Initialises curses for later use. Based on curses.wrapper
917 implementation from the Python standard library."""
918 self.screen = curses.initscr()
922 # The try/catch works around a minor bit of
923 # over-conscientiousness in the curses module, the error
924 # return from C start_color() is ignorable.
930 # Hide cursor in extra statement as some monochrome terminals
931 # might support hiding but not colors.
937 curses.use_default_colors()
940 def __exit__(self, *exception):
941 """Resets the terminal to its normal state. Based on curses.wrapper
942 implementation from the Python standard library."""
944 self.screen.keypad(0)
949 def get_all_gnames(self):
950 """Returns a list of (pid, gname) tuples of all running guests"""
953 child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
954 stdout=subprocess.PIPE)
957 for line in child.stdout:
958 line = line.lstrip().split(' ', 1)
959 # perform a sanity check before calling the more expensive
960 # function to possibly extract the guest name
961 if ' -name ' in line[1]:
962 res.append((line[0], self.get_gname_from_pid(line[0])))
967 def print_all_gnames(self, row):
968 """Print a list of all running guests along with their pids."""
969 self.screen.addstr(row, 2, '%8s %-60s' %
970 ('Pid', 'Guest Name (fuzzy list, might be '
975 for line in self.get_all_gnames():
976 self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1]))
978 if row >= self.screen.getmaxyx()[0]:
981 self.screen.addstr(row + 1, 2, 'Not available')
983 def get_pid_from_gname(self, gname):
984 """Fuzzy function to convert guest name to QEMU process pid.
986 Returns a list of potential pids, can be empty if no match found.
987 Throws an exception on processing errors.
991 for line in self.get_all_gnames():
993 pids.append(int(line[0]))
998 def get_gname_from_pid(pid):
999 """Returns the guest name for a QEMU process pid.
1001 Extracts the guest name from the QEMU comma line by processing the
1002 '-name' option. Will also handle names specified out of sequence.
1007 line = open('/proc/{}/cmdline'
1008 .format(pid), 'rb').read().split('\0')
1009 parms = line[line.index('-name') + 1].split(',')
1011 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1012 # in # ['foo', '', 'bar'], which we revert here
1013 idx = parms.index('')
1014 parms[idx - 1] += ',' + parms[idx + 1]
1015 del parms[idx:idx+2]
1016 # the '-name' switch allows for two ways to specify the guest name,
1017 # where the plain name overrides the name specified via 'guest='
1022 if arg[:6] == 'guest=':
1024 except (ValueError, IOError, IndexError):
1029 def update_drilldown(self):
1030 """Sets or removes a filter that only allows fields without braces."""
1031 if not self.stats.fields_filter:
1032 self.stats.fields_filter = DEFAULT_REGEX
1034 elif self.stats.fields_filter == DEFAULT_REGEX:
1035 self.stats.fields_filter = None
1037 def update_pid(self, pid):
1038 """Propagates pid selection to stats object."""
1039 self.stats.pid_filter = pid
1041 def refresh_header(self, pid=None):
1042 """Refreshes the header."""
1044 pid = self.stats.pid_filter
1046 gname = self.get_gname_from_pid(pid)
1048 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1049 if len(gname) > MAX_GUEST_NAME_LEN
1052 self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1053 .format(pid, gname), curses.A_BOLD)
1055 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1056 if self.stats.fields_filter and self.stats.fields_filter \
1058 regex = self.stats.fields_filter
1059 if len(regex) > MAX_REGEX_LEN:
1060 regex = regex[:MAX_REGEX_LEN] + '...'
1061 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1062 if self._display_guests:
1063 col_name = 'Guest Name'
1066 self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1067 (col_name, 'Total', '%Total', 'CurAvg/s'),
1069 self.screen.addstr(4, 1, 'Collecting data...')
1070 self.screen.refresh()
1072 def refresh_body(self, sleeptime):
1074 self.screen.move(row, 0)
1075 self.screen.clrtobot()
1076 stats = self.stats.get(self._display_guests)
1079 # sort by current events if available
1081 return (-stats[x][1], -stats[x][0])
1083 return (0, -stats[x][0])
1087 return (0, -stats[x][0])
1089 for val in stats.values():
1091 if self._sorting == SORT_DEFAULT:
1092 sortkey = sortCurAvg
1095 for key in sorted(stats.keys(), key=sortkey):
1097 if row >= self.screen.getmaxyx()[0]:
1100 if not values[0] and not values[1]:
1102 if values[0] is not None:
1103 cur = int(round(values[1] / sleeptime)) if values[1] else ''
1104 if self._display_guests:
1105 key = self.get_gname_from_pid(key)
1106 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
1107 (key, values[0], values[0] * 100 / total,
1111 self.screen.addstr(4, 1, 'No matching events reported yet')
1112 self.screen.refresh()
1114 def show_msg(self, text):
1115 """Display message centered text and exit on key press"""
1116 hint = 'Press any key to continue'
1119 (x, term_width) = self.screen.getmaxyx()
1122 start = (term_width - len(line)) / 2
1123 self.screen.addstr(row, start, line)
1125 self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint,
1127 self.screen.getkey()
1129 def show_help_interactive(self):
1130 """Display help with list of interactive commands"""
1131 msg = (' b toggle events by guests (debugfs only, honors'
1134 ' f filter by regular expression',
1135 ' g filter by guest name',
1136 ' h display interactive commands reference',
1137 ' o toggle sorting order (Total vs CurAvg/s)',
1141 ' s set update interval',
1142 ' x toggle reporting of stats for individual child trace'
1144 'Any other key refreshes statistics immediately')
1147 self.screen.addstr(0, 0, "Interactive commands reference",
1149 self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1152 self.screen.addstr(row, 0, line)
1154 self.screen.getkey()
1155 self.refresh_header()
1157 def show_filter_selection(self):
1158 """Draws filter selection mask.
1160 Asks for a valid regex and sets the fields filter accordingly.
1165 self.screen.addstr(0, 0,
1166 "Show statistics for events matching a regex.",
1168 self.screen.addstr(2, 0,
1169 "Current regex: {0}"
1170 .format(self.stats.fields_filter))
1171 self.screen.addstr(3, 0, "New regex: ")
1173 regex = self.screen.getstr()
1176 self.stats.fields_filter = DEFAULT_REGEX
1177 self.refresh_header()
1181 self.stats.fields_filter = regex
1182 self.refresh_header()
1187 def show_vm_selection_by_pid(self):
1188 """Draws PID selection mask.
1190 Asks for a pid until a valid pid or 0 has been entered.
1196 self.screen.addstr(0, 0,
1197 'Show statistics for specific pid.',
1199 self.screen.addstr(1, 0,
1200 'This might limit the shown data to the trace '
1202 self.screen.addstr(5, 0, msg)
1203 self.print_all_gnames(7)
1206 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1207 pid = self.screen.getstr()
1213 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1215 msg = '"' + str(pid) + '": Not a running process'
1219 self.refresh_header(pid)
1220 self.update_pid(pid)
1223 msg = '"' + str(pid) + '": Not a valid pid'
1225 def show_set_update_interval(self):
1226 """Draws update interval selection mask."""
1230 self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1231 DELAY_DEFAULT, curses.A_BOLD)
1232 self.screen.addstr(4, 0, msg)
1233 self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1234 self._delay_regular)
1236 val = self.screen.getstr()
1243 msg = '"' + str(val) + '": Value must be >=0.1'
1246 msg = '"' + str(val) + '": Value must be <=25.5'
1249 delay = DELAY_DEFAULT
1250 self._delay_regular = delay
1254 msg = '"' + str(val) + '": Invalid value'
1255 self.refresh_header()
1257 def show_vm_selection_by_guest_name(self):
1258 """Draws guest selection mask.
1260 Asks for a guest name until a valid guest name or '' is entered.
1266 self.screen.addstr(0, 0,
1267 'Show statistics for specific guest.',
1269 self.screen.addstr(1, 0,
1270 'This might limit the shown data to the trace '
1272 self.screen.addstr(5, 0, msg)
1273 self.print_all_gnames(7)
1275 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1276 gname = self.screen.getstr()
1280 self.refresh_header(0)
1286 pids = self.get_pid_from_gname(gname)
1288 msg = '"' + gname + '": Internal error while searching, ' \
1289 'use pid filter instead'
1292 msg = '"' + gname + '": Not an active guest'
1295 msg = '"' + gname + '": Multiple matches found, use pid ' \
1298 self.refresh_header(pids[0])
1299 self.update_pid(pids[0])
1302 def show_stats(self):
1303 """Refreshes the screen and processes user input."""
1304 sleeptime = self._delay_initial
1305 self.refresh_header()
1306 start = 0.0 # result based on init value never appears on screen
1308 self.refresh_body(time.time() - start)
1309 curses.halfdelay(int(sleeptime * 10))
1311 sleeptime = self._delay_regular
1313 char = self.screen.getkey()
1315 self._display_guests = not self._display_guests
1316 if self.stats.toggle_display_guests(self._display_guests):
1317 self.show_msg(['Command not available with tracepoints'
1318 ' enabled', 'Restart with debugfs only '
1319 '(see option \'-d\') and try again!'])
1320 self._display_guests = not self._display_guests
1321 self.refresh_header()
1323 self.stats.fields_filter = DEFAULT_REGEX
1324 self.refresh_header(0)
1328 self.show_filter_selection()
1330 sleeptime = self._delay_initial
1333 self.show_vm_selection_by_guest_name()
1335 sleeptime = self._delay_initial
1337 self.show_help_interactive()
1339 self._sorting = not self._sorting
1342 self.show_vm_selection_by_pid()
1344 sleeptime = self._delay_initial
1351 self.show_set_update_interval()
1353 sleeptime = self._delay_initial
1355 self.update_drilldown()
1356 # prevents display of current values on next refresh
1358 except KeyboardInterrupt:
1360 except curses.error:
1365 """Prints statistics in a key, value format."""
1370 for key in sorted(s.keys()):
1372 print '%-42s%10d%10d' % (key, values[0], values[1])
1373 except KeyboardInterrupt:
1378 """Prints statistics as reiterating key block, multiple value blocks."""
1379 keys = sorted(stats.get().iterkeys())
1389 print ' %9d' % s[k][1],
1396 if line % banner_repeat == 0:
1400 except KeyboardInterrupt:
1405 """Returns processed program arguments."""
1406 description_text = """
1407 This script displays various statistics about VMs running under KVM.
1408 The statistics are gathered from the KVM debugfs entries and / or the
1409 currently available perf traces.
1411 The monitoring takes additional cpu cycles and might affect the VM's
1419 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1420 CAP_SYS_ADMIN and perf events are used.
1421 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1422 the large number of files that are possibly opened.
1424 Interactive Commands:
1425 b toggle events by guests (debugfs only, honors filters)
1427 f filter by regular expression
1428 g filter by guest name
1429 h display interactive commands reference
1430 o toggle sorting order (Total vs CurAvg/s)
1434 s set update interval
1435 x toggle reporting of stats for individual child trace events
1436 Press any other key to refresh statistics immediately.
1437 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1439 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1440 def format_description(self, description):
1442 return description + "\n"
1446 def cb_guest_to_pid(option, opt, val, parser):
1448 pids = Tui.get_pid_from_gname(val)
1450 raise optparse.OptionValueError('Error while searching for guest '
1451 '"{}", use "-p" to specify a pid '
1452 'instead'.format(val))
1454 raise optparse.OptionValueError('No guest by the name "{}" '
1455 'found'.format(val))
1457 raise optparse.OptionValueError('Multiple processes found (pids: '
1458 '{}) - use "-p" to specify a pid '
1459 'instead'.format(" ".join(pids)))
1460 parser.values.pid = pids[0]
1462 optparser = optparse.OptionParser(description=description_text,
1463 formatter=PlainHelpFormatter())
1464 optparser.add_option('-1', '--once', '--batch',
1465 action='store_true',
1468 help='run in batch mode for one second',
1470 optparser.add_option('-i', '--debugfs-include-past',
1471 action='store_true',
1473 dest='dbgfs_include_past',
1474 help='include all available data on past events for '
1477 optparser.add_option('-l', '--log',
1478 action='store_true',
1481 help='run in logging mode (like vmstat)',
1483 optparser.add_option('-t', '--tracepoints',
1484 action='store_true',
1487 help='retrieve statistics from tracepoints',
1489 optparser.add_option('-d', '--debugfs',
1490 action='store_true',
1493 help='retrieve statistics from debugfs',
1495 optparser.add_option('-f', '--fields',
1497 default=DEFAULT_REGEX,
1499 help='''fields to display (regex)
1500 "-f help" for a list of available events''',
1502 optparser.add_option('-p', '--pid',
1507 help='restrict statistics to pid',
1509 optparser.add_option('-g', '--guest',
1514 help='restrict statistics to guest by name',
1515 callback=cb_guest_to_pid,
1517 (options, _) = optparser.parse_args(sys.argv)
1521 def check_access(options):
1522 """Exits if the current user can't access all needed directories."""
1523 if not os.path.exists('/sys/kernel/debug'):
1524 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1527 if not os.path.exists(PATH_DEBUGFS_KVM):
1528 sys.stderr.write("Please make sure, that debugfs is mounted and "
1529 "readable by the current user:\n"
1530 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1531 "Also ensure, that the kvm modules are loaded.\n")
1534 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1535 not options.debugfs):
1536 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1537 "when using the option -t (default).\n"
1538 "If it is enabled, make {0} readable by the "
1540 .format(PATH_DEBUGFS_TRACING))
1541 if options.tracepoints:
1544 sys.stderr.write("Falling back to debugfs statistics!\n")
1545 options.debugfs = True
1552 options = get_options()
1553 options = check_access(options)
1555 if (options.pid > 0 and
1556 not os.path.isdir(os.path.join('/proc/',
1557 str(options.pid)))):
1558 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1559 sys.exit('Specified pid does not exist.')
1561 stats = Stats(options)
1563 if options.fields == "help":
1566 for key in s.keys():
1567 if key.find('(') != -1:
1568 key = key[0:key.find('(')]
1569 if event_list.find('\n' + key + '\n') == -1:
1570 event_list += key + '\n'
1571 sys.stdout.write(event_list)
1576 elif not options.once:
1577 with Tui(stats) as tui:
1582 if __name__ == "__main__":