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_reason_field = 'exit_reason'
265 self.exit_reasons = exit_reasons
270 self.sc_perf_evt_open = 319
271 self.ioctl_numbers = IOCTL_NUMBERS
272 self.ioctl_numbers['ENABLE'] = 0x20002400
273 self.ioctl_numbers['DISABLE'] = 0x20002401
274 self.ioctl_numbers['RESET'] = 0x20002403
276 # PPC comes in 32 and 64 bit and some generated ioctl
277 # numbers depend on the wordsize.
278 char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
279 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
280 self.exit_reason_field = 'exit_nr'
281 self.exit_reasons = {}
286 self.sc_perf_evt_open = 241
287 self.ioctl_numbers = IOCTL_NUMBERS
288 self.exit_reason_field = 'esr_ec'
289 self.exit_reasons = AARCH64_EXIT_REASONS
292 class ArchS390(Arch):
294 self.sc_perf_evt_open = 331
295 self.ioctl_numbers = IOCTL_NUMBERS
296 self.exit_reason_field = None
297 self.exit_reasons = None
299 ARCH = Arch.get_arch()
302 class perf_event_attr(ctypes.Structure):
303 """Struct that holds the necessary data to set up a trace event.
305 For an extensive explanation see perf_event_open(2) and
306 include/uapi/linux/perf_event.h, struct perf_event_attr
308 All fields that are not initialized in the constructor are 0.
311 _fields_ = [('type', ctypes.c_uint32),
312 ('size', ctypes.c_uint32),
313 ('config', ctypes.c_uint64),
314 ('sample_freq', ctypes.c_uint64),
315 ('sample_type', ctypes.c_uint64),
316 ('read_format', ctypes.c_uint64),
317 ('flags', ctypes.c_uint64),
318 ('wakeup_events', ctypes.c_uint32),
319 ('bp_type', ctypes.c_uint32),
320 ('bp_addr', ctypes.c_uint64),
321 ('bp_len', ctypes.c_uint64),
325 super(self.__class__, self).__init__()
326 self.type = PERF_TYPE_TRACEPOINT
327 self.size = ctypes.sizeof(self)
328 self.read_format = PERF_FORMAT_GROUP
331 PERF_TYPE_TRACEPOINT = 2
332 PERF_FORMAT_GROUP = 1 << 3
334 PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
335 PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
339 """Represents a perf event group."""
344 def add_event(self, event):
345 self.events.append(event)
348 """Returns a dict with 'event name: value' for all events in the
351 Values are read by reading from the file descriptor of the
352 event that is the group leader. See perf_event_open(2) for
355 Read format for the used event configuration is:
357 u64 nr; /* The number of events */
359 u64 value; /* The value of the event */
364 length = 8 * (1 + len(self.events))
365 read_format = 'xxxxxxxx' + 'Q' * len(self.events)
366 return dict(zip([event.name for event in self.events],
367 struct.unpack(read_format,
368 os.read(self.events[0].fd, length))))
372 """Represents a performance event and manages its life cycle."""
373 def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
374 trace_filter, trace_set='kvm'):
375 self.libc = ctypes.CDLL('libc.so.6', use_errno=True)
376 self.syscall = self.libc.syscall
379 self.setup_event(group, trace_cpu, trace_pid, trace_point,
380 trace_filter, trace_set)
383 """Closes the event's file descriptor.
385 As no python file object was created for the file descriptor,
386 python will not reference count the descriptor and will not
387 close it itself automatically, so we do it.
393 def perf_event_open(self, attr, pid, cpu, group_fd, flags):
394 """Wrapper for the sys_perf_evt_open() syscall.
396 Used to set up performance events, returns a file descriptor or -1
401 - struct perf_event_attr *
402 - pid or -1 to monitor all pids
403 - cpu number or -1 to monitor all cpus
404 - The file descriptor of the group leader or -1 to create a group.
408 return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
409 ctypes.c_int(pid), ctypes.c_int(cpu),
410 ctypes.c_int(group_fd), ctypes.c_long(flags))
412 def setup_event_attribute(self, trace_set, trace_point):
413 """Returns an initialized ctype perf_event_attr struct."""
415 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
418 event_attr = perf_event_attr()
419 event_attr.config = int(open(id_path).read())
422 def setup_event(self, group, trace_cpu, trace_pid, trace_point,
423 trace_filter, trace_set):
424 """Sets up the perf event in Linux.
426 Issues the syscall to register the event in the kernel and
427 then sets the optional filter.
431 event_attr = self.setup_event_attribute(trace_set, trace_point)
433 # First event will be group leader.
436 # All others have to pass the leader's descriptor instead.
438 group_leader = group.events[0].fd
440 fd = self.perf_event_open(event_attr, trace_pid,
441 trace_cpu, group_leader, 0)
443 err = ctypes.get_errno()
444 raise OSError(err, os.strerror(err),
445 'while calling sys_perf_event_open().')
448 fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
454 """Enables the trace event in the kernel.
456 Enabling the group leader makes reading counters from it and the
457 events under it possible.
460 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
463 """Disables the trace event in the kernel.
465 Disabling the group leader makes reading all counters under it
469 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
472 """Resets the count of the trace event in the kernel."""
473 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
476 class Provider(object):
477 """Encapsulates functionalities used by all providers."""
479 def is_field_wanted(fields_filter, field):
480 """Indicate whether field is valid according to fields_filter."""
481 if not fields_filter or fields_filter == "help":
483 return re.match(fields_filter, field) is not None
487 """Returns os.walk() data for specified directory.
489 As it is only a wrapper it returns the same 3-tuple of (dirpath,
490 dirnames, filenames).
492 return next(os.walk(path))
495 class TracepointProvider(Provider):
496 """Data provider for the stats class.
498 Manages the events/groups from which it acquires its data.
501 def __init__(self, pid, fields_filter):
502 self.group_leaders = []
503 self.filters = self.get_filters()
504 self.update_fields(fields_filter)
509 """Returns a dict of trace events, their filter ids and
510 the values that can be filtered.
512 Trace events can be filtered for special values by setting a
513 filter string via an ioctl. The string normally has the format
514 identifier==value. For each filter a new event will be created, to
515 be able to distinguish the events.
519 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
520 if ARCH.exit_reason_field and ARCH.exit_reasons:
521 filters['kvm_exit'] = (ARCH.exit_reason_field, ARCH.exit_reasons)
524 def get_available_fields(self):
525 """Returns a list of available event's of format 'event name(filter
528 All available events have directories under
529 /sys/kernel/debug/tracing/events/ which export information
530 about the specific event. Therefore, listing the dirs gives us
531 a list of all available events.
533 Some events like the vm exit reasons can be filtered for
534 specific values. To take account for that, the routine below
535 creates special fields with the following format:
536 event name(filter name)
539 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
540 fields = self.walkdir(path)[1]
543 if field in self.filters:
544 filter_name_, filter_dicts = self.filters[field]
545 for name in filter_dicts:
546 extra.append(field + '(' + name + ')')
550 def update_fields(self, fields_filter):
551 """Refresh fields, applying fields_filter"""
552 self._fields = [field for field in self.get_available_fields()
553 if self.is_field_wanted(fields_filter, field)]
556 def get_online_cpus():
557 """Returns a list of cpu id integers."""
558 def parse_int_list(list_string):
559 """Returns an int list from a string of comma separated integers and
562 members = list_string.split(',')
564 for member in members:
565 if '-' not in member:
566 integers.append(int(member))
568 int_range = member.split('-')
569 integers.extend(range(int(int_range[0]),
570 int(int_range[1]) + 1))
574 with open('/sys/devices/system/cpu/online') as cpu_list:
575 cpu_string = cpu_list.readline()
576 return parse_int_list(cpu_string)
578 def setup_traces(self):
579 """Creates all event and group objects needed to be able to retrieve
581 fields = self.get_available_fields()
583 # Fetch list of all threads of the monitored pid, as qemu
584 # starts a thread for each vcpu.
585 path = os.path.join('/proc', str(self._pid), 'task')
586 groupids = self.walkdir(path)[1]
588 groupids = self.get_online_cpus()
590 # The constant is needed as a buffer for python libs, std
591 # streams and other files that the script opens.
592 newlim = len(groupids) * len(fields) + 50
594 softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
597 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
598 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
600 # Raising the soft limit is sufficient.
601 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
604 sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
606 for groupid in groupids:
611 match = re.match(r'(.*)\((.*)\)', name)
613 tracepoint, sub = match.groups()
614 tracefilter = ('%s==%d\0' %
615 (self.filters[tracepoint][0],
616 self.filters[tracepoint][1][sub]))
618 # From perf_event_open(2):
619 # pid > 0 and cpu == -1
620 # This measures the specified process/thread on any CPU.
622 # pid == -1 and cpu >= 0
623 # This measures all processes/threads on the specified CPU.
624 trace_cpu = groupid if self._pid == 0 else -1
625 trace_pid = int(groupid) if self._pid != 0 else -1
627 group.add_event(Event(name=name,
631 trace_point=tracepoint,
632 trace_filter=tracefilter))
634 self.group_leaders.append(group)
641 def fields(self, fields):
642 """Enables/disables the (un)wanted events"""
643 self._fields = fields
644 for group in self.group_leaders:
645 for index, event in enumerate(group.events):
646 if event.name in fields:
650 # Do not disable the group leader.
651 # It would disable all of its events.
661 """Changes the monitored pid by setting new traces."""
663 # The garbage collector will get rid of all Event/Group
664 # objects and open files after removing the references.
665 self.group_leaders = []
667 self.fields = self._fields
669 def read(self, by_guest=0):
670 """Returns 'event name: current value' for all enabled events."""
671 ret = defaultdict(int)
672 for group in self.group_leaders:
673 for name, val in group.read().iteritems():
674 if name in self._fields:
679 """Reset all field counters"""
680 for group in self.group_leaders:
681 for event in group.events:
685 class DebugfsProvider(Provider):
686 """Provides data from the files that KVM creates in the kvm debugfs
688 def __init__(self, pid, fields_filter, include_past):
689 self.update_fields(fields_filter)
697 def get_available_fields(self):
698 """"Returns a list of available fields.
700 The fields are all available KVM debugfs files
703 return self.walkdir(PATH_DEBUGFS_KVM)[2]
705 def update_fields(self, fields_filter):
706 """Refresh fields, applying fields_filter"""
707 self._fields = [field for field in self.get_available_fields()
708 if self.is_field_wanted(fields_filter, field)]
715 def fields(self, fields):
716 self._fields = fields
727 vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
731 self.paths = list(filter(lambda x: "{}-".format(pid) in x, vms))
738 def _verify_paths(self):
739 """Remove invalid paths"""
740 for path in self.paths:
741 if not os.path.exists(os.path.join(PATH_DEBUGFS_KVM, path)):
742 self.paths.remove(path)
745 def read(self, reset=0, by_guest=0):
746 """Returns a dict with format:'file name / field -> current value'.
750 1 reset field counts to 0
751 2 restore the original field counts
756 # If no debugfs filtering support is available, then don't read.
764 for entry in os.walk(PATH_DEBUGFS_KVM):
768 for field in self._fields:
769 value = self.read_field(field, path)
772 self._baseline[key] = value
774 self._baseline[key] = 0
775 if self._baseline.get(key, -1) == -1:
776 self._baseline[key] = value
777 increment = (results.get(field, 0) + value -
778 self._baseline.get(key, 0))
780 pid = key.split('-')[0]
782 results[pid] += increment
784 results[pid] = increment
786 results[field] = increment
790 def read_field(self, field, path):
791 """Returns the value of a single field from a specific VM."""
793 return int(open(os.path.join(PATH_DEBUGFS_KVM,
801 """Reset field counters"""
806 """Reset field counters"""
812 """Manages the data providers and the data they provide.
814 It is used to set filters on the provider's data and collect all
818 def __init__(self, options):
819 self.providers = self.get_providers(options)
820 self._pid_filter = options.pid
821 self._fields_filter = options.fields
825 def get_providers(options):
826 """Returns a list of data providers depending on the passed options."""
830 providers.append(DebugfsProvider(options.pid, options.fields,
831 options.dbgfs_include_past))
832 if options.tracepoints or not providers:
833 providers.append(TracepointProvider(options.pid, options.fields))
837 def update_provider_filters(self):
838 """Propagates fields filters to providers."""
839 # As we reset the counters when updating the fields we can
840 # also clear the cache of old values.
842 for provider in self.providers:
843 provider.update_fields(self._fields_filter)
847 for provider in self.providers:
851 def fields_filter(self):
852 return self._fields_filter
854 @fields_filter.setter
855 def fields_filter(self, fields_filter):
856 if fields_filter != self._fields_filter:
857 self._fields_filter = fields_filter
858 self.update_provider_filters()
861 def pid_filter(self):
862 return self._pid_filter
865 def pid_filter(self, pid):
866 if pid != self._pid_filter:
867 self._pid_filter = pid
869 for provider in self.providers:
870 provider.pid = self._pid_filter
872 def get(self, by_guest=0):
873 """Returns a dict with field -> (value, delta to last value) of all
875 for provider in self.providers:
876 new = provider.read(by_guest=by_guest)
877 for key in new if by_guest else provider.fields:
878 oldval = self.values.get(key, (0, 0))[0]
879 newval = new.get(key, 0)
880 newdelta = newval - oldval
881 self.values[key] = (newval, newdelta)
884 def toggle_display_guests(self, to_pid):
885 """Toggle between collection of stats by individual event and by
888 Events reported by DebugfsProvider change when switching to/from
889 reading by guest values. Hence we have to remove the excess event
890 names from self.values.
893 if any(isinstance(ins, TracepointProvider) for ins in self.providers):
896 for provider in self.providers:
897 if isinstance(provider, DebugfsProvider):
898 for key in provider.fields:
899 if key in self.values.keys():
902 oldvals = self.values.copy()
906 # Update oldval (see get())
911 MAX_GUEST_NAME_LEN = 48
913 DEFAULT_REGEX = r'^[^\(]*$'
918 """Instruments curses to draw a nice text ui."""
919 def __init__(self, stats):
922 self._delay_initial = 0.25
923 self._delay_regular = DELAY_DEFAULT
924 self._sorting = SORT_DEFAULT
925 self._display_guests = 0
928 """Initialises curses for later use. Based on curses.wrapper
929 implementation from the Python standard library."""
930 self.screen = curses.initscr()
934 # The try/catch works around a minor bit of
935 # over-conscientiousness in the curses module, the error
936 # return from C start_color() is ignorable.
942 # Hide cursor in extra statement as some monochrome terminals
943 # might support hiding but not colors.
949 curses.use_default_colors()
952 def __exit__(self, *exception):
953 """Resets the terminal to its normal state. Based on curses.wrapper
954 implementation from the Python standard library."""
956 self.screen.keypad(0)
961 def get_all_gnames(self):
962 """Returns a list of (pid, gname) tuples of all running guests"""
965 child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
966 stdout=subprocess.PIPE)
969 for line in child.stdout:
970 line = line.lstrip().split(' ', 1)
971 # perform a sanity check before calling the more expensive
972 # function to possibly extract the guest name
973 if ' -name ' in line[1]:
974 res.append((line[0], self.get_gname_from_pid(line[0])))
979 def print_all_gnames(self, row):
980 """Print a list of all running guests along with their pids."""
981 self.screen.addstr(row, 2, '%8s %-60s' %
982 ('Pid', 'Guest Name (fuzzy list, might be '
987 for line in self.get_all_gnames():
988 self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1]))
990 if row >= self.screen.getmaxyx()[0]:
993 self.screen.addstr(row + 1, 2, 'Not available')
995 def get_pid_from_gname(self, gname):
996 """Fuzzy function to convert guest name to QEMU process pid.
998 Returns a list of potential pids, can be empty if no match found.
999 Throws an exception on processing errors.
1003 for line in self.get_all_gnames():
1004 if gname == line[1]:
1005 pids.append(int(line[0]))
1010 def get_gname_from_pid(pid):
1011 """Returns the guest name for a QEMU process pid.
1013 Extracts the guest name from the QEMU comma line by processing the
1014 '-name' option. Will also handle names specified out of sequence.
1019 line = open('/proc/{}/cmdline'
1020 .format(pid), 'rb').read().split('\0')
1021 parms = line[line.index('-name') + 1].split(',')
1023 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1024 # in # ['foo', '', 'bar'], which we revert here
1025 idx = parms.index('')
1026 parms[idx - 1] += ',' + parms[idx + 1]
1027 del parms[idx:idx+2]
1028 # the '-name' switch allows for two ways to specify the guest name,
1029 # where the plain name overrides the name specified via 'guest='
1034 if arg[:6] == 'guest=':
1036 except (ValueError, IOError, IndexError):
1041 def update_drilldown(self):
1042 """Sets or removes a filter that only allows fields without braces."""
1043 if not self.stats.fields_filter:
1044 self.stats.fields_filter = DEFAULT_REGEX
1046 elif self.stats.fields_filter == DEFAULT_REGEX:
1047 self.stats.fields_filter = None
1049 def update_pid(self, pid):
1050 """Propagates pid selection to stats object."""
1051 self.stats.pid_filter = pid
1053 def refresh_header(self, pid=None):
1054 """Refreshes the header."""
1056 pid = self.stats.pid_filter
1058 gname = self.get_gname_from_pid(pid)
1060 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1061 if len(gname) > MAX_GUEST_NAME_LEN
1064 self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1065 .format(pid, gname), curses.A_BOLD)
1067 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1068 if self.stats.fields_filter and self.stats.fields_filter \
1070 regex = self.stats.fields_filter
1071 if len(regex) > MAX_REGEX_LEN:
1072 regex = regex[:MAX_REGEX_LEN] + '...'
1073 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1074 if self._display_guests:
1075 col_name = 'Guest Name'
1078 self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1079 (col_name, 'Total', '%Total', 'CurAvg/s'),
1081 self.screen.addstr(4, 1, 'Collecting data...')
1082 self.screen.refresh()
1084 def refresh_body(self, sleeptime):
1086 self.screen.move(row, 0)
1087 self.screen.clrtobot()
1088 stats = self.stats.get(self._display_guests)
1091 # sort by current events if available
1093 return (-stats[x][1], -stats[x][0])
1095 return (0, -stats[x][0])
1099 return (0, -stats[x][0])
1101 for val in stats.values():
1103 if self._sorting == SORT_DEFAULT:
1104 sortkey = sortCurAvg
1107 for key in sorted(stats.keys(), key=sortkey):
1109 if row >= self.screen.getmaxyx()[0]:
1112 if not values[0] and not values[1]:
1114 if values[0] is not None:
1115 cur = int(round(values[1] / sleeptime)) if values[1] else ''
1116 if self._display_guests:
1117 key = self.get_gname_from_pid(key)
1118 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
1119 (key, values[0], values[0] * 100 / total,
1123 self.screen.addstr(4, 1, 'No matching events reported yet')
1124 self.screen.refresh()
1126 def show_msg(self, text):
1127 """Display message centered text and exit on key press"""
1128 hint = 'Press any key to continue'
1131 (x, term_width) = self.screen.getmaxyx()
1134 start = (term_width - len(line)) // 2
1135 self.screen.addstr(row, start, line)
1137 self.screen.addstr(row + 1, (term_width - len(hint)) // 2, hint,
1139 self.screen.getkey()
1141 def show_help_interactive(self):
1142 """Display help with list of interactive commands"""
1143 msg = (' b toggle events by guests (debugfs only, honors'
1146 ' f filter by regular expression',
1147 ' g filter by guest name',
1148 ' h display interactive commands reference',
1149 ' o toggle sorting order (Total vs CurAvg/s)',
1153 ' s set update interval',
1154 ' x toggle reporting of stats for individual child trace'
1156 'Any other key refreshes statistics immediately')
1159 self.screen.addstr(0, 0, "Interactive commands reference",
1161 self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1164 self.screen.addstr(row, 0, line)
1166 self.screen.getkey()
1167 self.refresh_header()
1169 def show_filter_selection(self):
1170 """Draws filter selection mask.
1172 Asks for a valid regex and sets the fields filter accordingly.
1177 self.screen.addstr(0, 0,
1178 "Show statistics for events matching a regex.",
1180 self.screen.addstr(2, 0,
1181 "Current regex: {0}"
1182 .format(self.stats.fields_filter))
1183 self.screen.addstr(3, 0, "New regex: ")
1185 regex = self.screen.getstr()
1188 self.stats.fields_filter = DEFAULT_REGEX
1189 self.refresh_header()
1193 self.stats.fields_filter = regex
1194 self.refresh_header()
1199 def show_vm_selection_by_pid(self):
1200 """Draws PID selection mask.
1202 Asks for a pid until a valid pid or 0 has been entered.
1208 self.screen.addstr(0, 0,
1209 'Show statistics for specific pid.',
1211 self.screen.addstr(1, 0,
1212 'This might limit the shown data to the trace '
1214 self.screen.addstr(5, 0, msg)
1215 self.print_all_gnames(7)
1218 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1219 pid = self.screen.getstr()
1225 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1227 msg = '"' + str(pid) + '": Not a running process'
1231 self.refresh_header(pid)
1232 self.update_pid(pid)
1235 msg = '"' + str(pid) + '": Not a valid pid'
1237 def show_set_update_interval(self):
1238 """Draws update interval selection mask."""
1242 self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1243 DELAY_DEFAULT, curses.A_BOLD)
1244 self.screen.addstr(4, 0, msg)
1245 self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1246 self._delay_regular)
1248 val = self.screen.getstr()
1255 msg = '"' + str(val) + '": Value must be >=0.1'
1258 msg = '"' + str(val) + '": Value must be <=25.5'
1261 delay = DELAY_DEFAULT
1262 self._delay_regular = delay
1266 msg = '"' + str(val) + '": Invalid value'
1267 self.refresh_header()
1269 def show_vm_selection_by_guest_name(self):
1270 """Draws guest selection mask.
1272 Asks for a guest name until a valid guest name or '' is entered.
1278 self.screen.addstr(0, 0,
1279 'Show statistics for specific guest.',
1281 self.screen.addstr(1, 0,
1282 'This might limit the shown data to the trace '
1284 self.screen.addstr(5, 0, msg)
1285 self.print_all_gnames(7)
1287 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1288 gname = self.screen.getstr()
1292 self.refresh_header(0)
1298 pids = self.get_pid_from_gname(gname)
1300 msg = '"' + gname + '": Internal error while searching, ' \
1301 'use pid filter instead'
1304 msg = '"' + gname + '": Not an active guest'
1307 msg = '"' + gname + '": Multiple matches found, use pid ' \
1310 self.refresh_header(pids[0])
1311 self.update_pid(pids[0])
1314 def show_stats(self):
1315 """Refreshes the screen and processes user input."""
1316 sleeptime = self._delay_initial
1317 self.refresh_header()
1318 start = 0.0 # result based on init value never appears on screen
1320 self.refresh_body(time.time() - start)
1321 curses.halfdelay(int(sleeptime * 10))
1323 sleeptime = self._delay_regular
1325 char = self.screen.getkey()
1327 self._display_guests = not self._display_guests
1328 if self.stats.toggle_display_guests(self._display_guests):
1329 self.show_msg(['Command not available with tracepoints'
1330 ' enabled', 'Restart with debugfs only '
1331 '(see option \'-d\') and try again!'])
1332 self._display_guests = not self._display_guests
1333 self.refresh_header()
1335 self.stats.fields_filter = DEFAULT_REGEX
1336 self.refresh_header(0)
1340 self.show_filter_selection()
1342 sleeptime = self._delay_initial
1345 self.show_vm_selection_by_guest_name()
1347 sleeptime = self._delay_initial
1349 self.show_help_interactive()
1351 self._sorting = not self._sorting
1354 self.show_vm_selection_by_pid()
1356 sleeptime = self._delay_initial
1363 self.show_set_update_interval()
1365 sleeptime = self._delay_initial
1367 self.update_drilldown()
1368 # prevents display of current values on next refresh
1370 except KeyboardInterrupt:
1372 except curses.error:
1377 """Prints statistics in a key, value format."""
1382 for key in sorted(s.keys()):
1384 print '%-42s%10d%10d' % (key, values[0], values[1])
1385 except KeyboardInterrupt:
1390 """Prints statistics as reiterating key block, multiple value blocks."""
1391 keys = sorted(stats.get().iterkeys())
1401 print ' %9d' % s[k][1],
1408 if line % banner_repeat == 0:
1412 except KeyboardInterrupt:
1417 """Returns processed program arguments."""
1418 description_text = """
1419 This script displays various statistics about VMs running under KVM.
1420 The statistics are gathered from the KVM debugfs entries and / or the
1421 currently available perf traces.
1423 The monitoring takes additional cpu cycles and might affect the VM's
1431 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1432 CAP_SYS_ADMIN and perf events are used.
1433 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1434 the large number of files that are possibly opened.
1436 Interactive Commands:
1437 b toggle events by guests (debugfs only, honors filters)
1439 f filter by regular expression
1440 g filter by guest name
1441 h display interactive commands reference
1442 o toggle sorting order (Total vs CurAvg/s)
1446 s set update interval
1447 x toggle reporting of stats for individual child trace events
1448 Press any other key to refresh statistics immediately.
1449 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1451 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1452 def format_description(self, description):
1454 return description + "\n"
1458 def cb_guest_to_pid(option, opt, val, parser):
1460 pids = Tui.get_pid_from_gname(val)
1462 raise optparse.OptionValueError('Error while searching for guest '
1463 '"{}", use "-p" to specify a pid '
1464 'instead'.format(val))
1466 raise optparse.OptionValueError('No guest by the name "{}" '
1467 'found'.format(val))
1469 raise optparse.OptionValueError('Multiple processes found (pids: '
1470 '{}) - use "-p" to specify a pid '
1471 'instead'.format(" ".join(pids)))
1472 parser.values.pid = pids[0]
1474 optparser = optparse.OptionParser(description=description_text,
1475 formatter=PlainHelpFormatter())
1476 optparser.add_option('-1', '--once', '--batch',
1477 action='store_true',
1480 help='run in batch mode for one second',
1482 optparser.add_option('-i', '--debugfs-include-past',
1483 action='store_true',
1485 dest='dbgfs_include_past',
1486 help='include all available data on past events for '
1489 optparser.add_option('-l', '--log',
1490 action='store_true',
1493 help='run in logging mode (like vmstat)',
1495 optparser.add_option('-t', '--tracepoints',
1496 action='store_true',
1499 help='retrieve statistics from tracepoints',
1501 optparser.add_option('-d', '--debugfs',
1502 action='store_true',
1505 help='retrieve statistics from debugfs',
1507 optparser.add_option('-f', '--fields',
1509 default=DEFAULT_REGEX,
1511 help='''fields to display (regex)
1512 "-f help" for a list of available events''',
1514 optparser.add_option('-p', '--pid',
1519 help='restrict statistics to pid',
1521 optparser.add_option('-g', '--guest',
1526 help='restrict statistics to guest by name',
1527 callback=cb_guest_to_pid,
1529 (options, _) = optparser.parse_args(sys.argv)
1533 def check_access(options):
1534 """Exits if the current user can't access all needed directories."""
1535 if not os.path.exists('/sys/kernel/debug'):
1536 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1539 if not os.path.exists(PATH_DEBUGFS_KVM):
1540 sys.stderr.write("Please make sure, that debugfs is mounted and "
1541 "readable by the current user:\n"
1542 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1543 "Also ensure, that the kvm modules are loaded.\n")
1546 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1547 not options.debugfs):
1548 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1549 "when using the option -t (default).\n"
1550 "If it is enabled, make {0} readable by the "
1552 .format(PATH_DEBUGFS_TRACING))
1553 if options.tracepoints:
1556 sys.stderr.write("Falling back to debugfs statistics!\n")
1557 options.debugfs = True
1564 options = get_options()
1565 options = check_access(options)
1567 if (options.pid > 0 and
1568 not os.path.isdir(os.path.join('/proc/',
1569 str(options.pid)))):
1570 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1571 sys.exit('Specified pid does not exist.')
1573 stats = Stats(options)
1575 if options.fields == "help":
1578 for key in s.keys():
1579 if key.find('(') != -1:
1580 key = key[0:key.find('(')]
1581 if event_list.find('\n' + key + '\n') == -1:
1582 event_list += key + '\n'
1583 sys.stdout.write(event_list)
1588 elif not options.once:
1589 with Tui(stats) as tui:
1594 if __name__ == "__main__":