]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/kvm/kvm_stat/kvm_stat
3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
9 # Avi Kivity <avi@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2. See
12 # the COPYING file in the top-level directory.
13 """The kvm_stat module outputs statistics about running KVM VMs
15 Three different ways of output formatting are available:
16 - as a top-like text ui
17 - in a key -> value format
18 - in an all keys, all values format
20 The data is sampled from the KVM's debugfs entries and its perf events.
34 from collections
import defaultdict
38 'EXTERNAL_INTERRUPT': 1,
40 'PENDING_INTERRUPT': 7,
64 'MWAIT_INSTRUCTION': 36,
65 'MONITOR_INSTRUCTION': 39,
66 'PAUSE_INSTRUCTION': 40,
67 'MCE_DURING_VMENTRY': 41,
68 'TPR_BELOW_THRESHOLD': 43,
109 'CR0_SEL_WRITE': 0x065,
133 'TASK_SWITCH': 0x07d,
134 'FERR_FREEZE': 0x07e,
153 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
154 AARCH64_EXIT_REASONS
= {
192 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
193 USERSPACE_EXIT_REASONS
= {
201 'IRQ_WINDOW_OPEN': 7,
211 'INTERNAL_ERROR': 17,
222 'SET_FILTER': 0x40082406,
223 'ENABLE': 0x00002400,
224 'DISABLE': 0x00002401,
230 """Encapsulates global architecture specific data.
232 Contains the performance event open syscall and ioctl numbers, as
233 well as the VM exit reasons for the architecture it runs on.
238 machine
= os
.uname()[4]
240 if machine
.startswith('ppc'):
242 elif machine
.startswith('aarch64'):
244 elif machine
.startswith('s390'):
248 for line
in open('/proc/cpuinfo'):
249 if not line
.startswith('flags'):
254 return ArchX86(VMX_EXIT_REASONS
)
256 return ArchX86(SVM_EXIT_REASONS
)
261 def __init__(self
, exit_reasons
):
262 self
.sc_perf_evt_open
= 298
263 self
.ioctl_numbers
= IOCTL_NUMBERS
264 self
.exit_reasons
= exit_reasons
269 self
.sc_perf_evt_open
= 319
270 self
.ioctl_numbers
= IOCTL_NUMBERS
271 self
.ioctl_numbers
['ENABLE'] = 0x20002400
272 self
.ioctl_numbers
['DISABLE'] = 0x20002401
273 self
.ioctl_numbers
['RESET'] = 0x20002403
275 # PPC comes in 32 and 64 bit and some generated ioctl
276 # numbers depend on the wordsize.
277 char_ptr_size
= ctypes
.sizeof(ctypes
.c_char_p
)
278 self
.ioctl_numbers
['SET_FILTER'] = 0x80002406 | char_ptr_size
<< 16
279 self
.exit_reasons
= {}
284 self
.sc_perf_evt_open
= 241
285 self
.ioctl_numbers
= IOCTL_NUMBERS
286 self
.exit_reasons
= AARCH64_EXIT_REASONS
289 class ArchS390(Arch
):
291 self
.sc_perf_evt_open
= 331
292 self
.ioctl_numbers
= IOCTL_NUMBERS
293 self
.exit_reasons
= None
295 ARCH
= Arch
.get_arch()
298 class perf_event_attr(ctypes
.Structure
):
299 """Struct that holds the necessary data to set up a trace event.
301 For an extensive explanation see perf_event_open(2) and
302 include/uapi/linux/perf_event.h, struct perf_event_attr
304 All fields that are not initialized in the constructor are 0.
307 _fields_
= [('type', ctypes
.c_uint32
),
308 ('size', ctypes
.c_uint32
),
309 ('config', ctypes
.c_uint64
),
310 ('sample_freq', ctypes
.c_uint64
),
311 ('sample_type', ctypes
.c_uint64
),
312 ('read_format', ctypes
.c_uint64
),
313 ('flags', ctypes
.c_uint64
),
314 ('wakeup_events', ctypes
.c_uint32
),
315 ('bp_type', ctypes
.c_uint32
),
316 ('bp_addr', ctypes
.c_uint64
),
317 ('bp_len', ctypes
.c_uint64
),
321 super(self
.__class
__, self
).__init
__()
322 self
.type = PERF_TYPE_TRACEPOINT
323 self
.size
= ctypes
.sizeof(self
)
324 self
.read_format
= PERF_FORMAT_GROUP
327 PERF_TYPE_TRACEPOINT
= 2
328 PERF_FORMAT_GROUP
= 1 << 3
330 PATH_DEBUGFS_TRACING
= '/sys/kernel/debug/tracing'
331 PATH_DEBUGFS_KVM
= '/sys/kernel/debug/kvm'
335 """Represents a perf event group."""
340 def add_event(self
, event
):
341 self
.events
.append(event
)
344 """Returns a dict with 'event name: value' for all events in the
347 Values are read by reading from the file descriptor of the
348 event that is the group leader. See perf_event_open(2) for
351 Read format for the used event configuration is:
353 u64 nr; /* The number of events */
355 u64 value; /* The value of the event */
360 length
= 8 * (1 + len(self
.events
))
361 read_format
= 'xxxxxxxx' + 'Q' * len(self
.events
)
362 return dict(zip([event
.name
for event
in self
.events
],
363 struct
.unpack(read_format
,
364 os
.read(self
.events
[0].fd
, length
))))
368 """Represents a performance event and manages its life cycle."""
369 def __init__(self
, name
, group
, trace_cpu
, trace_pid
, trace_point
,
370 trace_filter
, trace_set
='kvm'):
371 self
.libc
= ctypes
.CDLL('libc.so.6', use_errno
=True)
372 self
.syscall
= self
.libc
.syscall
375 self
.setup_event(group
, trace_cpu
, trace_pid
, trace_point
,
376 trace_filter
, trace_set
)
379 """Closes the event's file descriptor.
381 As no python file object was created for the file descriptor,
382 python will not reference count the descriptor and will not
383 close it itself automatically, so we do it.
389 def perf_event_open(self
, attr
, pid
, cpu
, group_fd
, flags
):
390 """Wrapper for the sys_perf_evt_open() syscall.
392 Used to set up performance events, returns a file descriptor or -1
397 - struct perf_event_attr *
398 - pid or -1 to monitor all pids
399 - cpu number or -1 to monitor all cpus
400 - The file descriptor of the group leader or -1 to create a group.
404 return self
.syscall(ARCH
.sc_perf_evt_open
, ctypes
.pointer(attr
),
405 ctypes
.c_int(pid
), ctypes
.c_int(cpu
),
406 ctypes
.c_int(group_fd
), ctypes
.c_long(flags
))
408 def setup_event_attribute(self
, trace_set
, trace_point
):
409 """Returns an initialized ctype perf_event_attr struct."""
411 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', trace_set
,
414 event_attr
= perf_event_attr()
415 event_attr
.config
= int(open(id_path
).read())
418 def setup_event(self
, group
, trace_cpu
, trace_pid
, trace_point
,
419 trace_filter
, trace_set
):
420 """Sets up the perf event in Linux.
422 Issues the syscall to register the event in the kernel and
423 then sets the optional filter.
427 event_attr
= self
.setup_event_attribute(trace_set
, trace_point
)
429 # First event will be group leader.
432 # All others have to pass the leader's descriptor instead.
434 group_leader
= group
.events
[0].fd
436 fd
= self
.perf_event_open(event_attr
, trace_pid
,
437 trace_cpu
, group_leader
, 0)
439 err
= ctypes
.get_errno()
440 raise OSError(err
, os
.strerror(err
),
441 'while calling sys_perf_event_open().')
444 fcntl
.ioctl(fd
, ARCH
.ioctl_numbers
['SET_FILTER'],
450 """Enables the trace event in the kernel.
452 Enabling the group leader makes reading counters from it and the
453 events under it possible.
456 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['ENABLE'], 0)
459 """Disables the trace event in the kernel.
461 Disabling the group leader makes reading all counters under it
465 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['DISABLE'], 0)
468 """Resets the count of the trace event in the kernel."""
469 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['RESET'], 0)
472 class Provider(object):
473 """Encapsulates functionalities used by all providers."""
475 def is_field_wanted(fields_filter
, field
):
476 """Indicate whether field is valid according to fields_filter."""
477 if not fields_filter
:
479 return re
.match(fields_filter
, field
) is not None
483 """Returns os.walk() data for specified directory.
485 As it is only a wrapper it returns the same 3-tuple of (dirpath,
486 dirnames, filenames).
488 return next(os
.walk(path
))
491 class TracepointProvider(Provider
):
492 """Data provider for the stats class.
494 Manages the events/groups from which it acquires its data.
497 def __init__(self
, pid
, fields_filter
):
498 self
.group_leaders
= []
499 self
.filters
= self
.get_filters()
500 self
.update_fields(fields_filter
)
505 """Returns a dict of trace events, their filter ids and
506 the values that can be filtered.
508 Trace events can be filtered for special values by setting a
509 filter string via an ioctl. The string normally has the format
510 identifier==value. For each filter a new event will be created, to
511 be able to distinguish the events.
515 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
516 if ARCH
.exit_reasons
:
517 filters
['kvm_exit'] = ('exit_reason', ARCH
.exit_reasons
)
520 def get_available_fields(self
):
521 """Returns a list of available event's of format 'event name(filter
524 All available events have directories under
525 /sys/kernel/debug/tracing/events/ which export information
526 about the specific event. Therefore, listing the dirs gives us
527 a list of all available events.
529 Some events like the vm exit reasons can be filtered for
530 specific values. To take account for that, the routine below
531 creates special fields with the following format:
532 event name(filter name)
535 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
536 fields
= self
.walkdir(path
)[1]
539 if field
in self
.filters
:
540 filter_name_
, filter_dicts
= self
.filters
[field
]
541 for name
in filter_dicts
:
542 extra
.append(field
+ '(' + name
+ ')')
546 def update_fields(self
, fields_filter
):
547 """Refresh fields, applying fields_filter"""
548 self
._fields
= [field
for field
in self
.get_available_fields()
549 if self
.is_field_wanted(fields_filter
, field
)]
552 def get_online_cpus():
553 """Returns a list of cpu id integers."""
554 def parse_int_list(list_string
):
555 """Returns an int list from a string of comma separated integers and
558 members
= list_string
.split(',')
560 for member
in members
:
561 if '-' not in member
:
562 integers
.append(int(member
))
564 int_range
= member
.split('-')
565 integers
.extend(range(int(int_range
[0]),
566 int(int_range
[1]) + 1))
570 with
open('/sys/devices/system/cpu/online') as cpu_list
:
571 cpu_string
= cpu_list
.readline()
572 return parse_int_list(cpu_string
)
574 def setup_traces(self
):
575 """Creates all event and group objects needed to be able to retrieve
577 fields
= self
.get_available_fields()
579 # Fetch list of all threads of the monitored pid, as qemu
580 # starts a thread for each vcpu.
581 path
= os
.path
.join('/proc', str(self
._pid
), 'task')
582 groupids
= self
.walkdir(path
)[1]
584 groupids
= self
.get_online_cpus()
586 # The constant is needed as a buffer for python libs, std
587 # streams and other files that the script opens.
588 newlim
= len(groupids
) * len(fields
) + 50
590 softlim_
, hardlim
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)
593 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
594 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, newlim
))
596 # Raising the soft limit is sufficient.
597 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, hardlim
))
600 sys
.exit("NOFILE rlimit could not be raised to {0}".format(newlim
))
602 for groupid
in groupids
:
607 match
= re
.match(r
'(.*)\((.*)\)', name
)
609 tracepoint
, sub
= match
.groups()
610 tracefilter
= ('%s==%d\0' %
611 (self
.filters
[tracepoint
][0],
612 self
.filters
[tracepoint
][1][sub
]))
614 # From perf_event_open(2):
615 # pid > 0 and cpu == -1
616 # This measures the specified process/thread on any CPU.
618 # pid == -1 and cpu >= 0
619 # This measures all processes/threads on the specified CPU.
620 trace_cpu
= groupid
if self
._pid
== 0 else -1
621 trace_pid
= int(groupid
) if self
._pid
!= 0 else -1
623 group
.add_event(Event(name
=name
,
627 trace_point
=tracepoint
,
628 trace_filter
=tracefilter
))
630 self
.group_leaders
.append(group
)
637 def fields(self
, fields
):
638 """Enables/disables the (un)wanted events"""
639 self
._fields
= fields
640 for group
in self
.group_leaders
:
641 for index
, event
in enumerate(group
.events
):
642 if event
.name
in fields
:
646 # Do not disable the group leader.
647 # It would disable all of its events.
657 """Changes the monitored pid by setting new traces."""
659 # The garbage collector will get rid of all Event/Group
660 # objects and open files after removing the references.
661 self
.group_leaders
= []
663 self
.fields
= self
._fields
666 """Returns 'event name: current value' for all enabled events."""
667 ret
= defaultdict(int)
668 for group
in self
.group_leaders
:
669 for name
, val
in group
.read().iteritems():
670 if name
in self
._fields
:
675 """Reset all field counters"""
676 for group
in self
.group_leaders
:
677 for event
in group
.events
:
681 class DebugfsProvider(Provider
):
682 """Provides data from the files that KVM creates in the kvm debugfs
684 def __init__(self
, pid
, fields_filter
, 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):
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 results
[field
] = (results
.get(field
, 0) + value
-
766 self
._baseline
.get(key
, 0))
770 def read_field(self
, field
, path
):
771 """Returns the value of a single field from a specific VM."""
773 return int(open(os
.path
.join(PATH_DEBUGFS_KVM
,
781 """Reset field counters"""
786 """Reset field counters"""
792 """Manages the data providers and the data they provide.
794 It is used to set filters on the provider's data and collect all
798 def __init__(self
, options
):
799 self
.providers
= self
.get_providers(options
)
800 self
._pid
_filter
= options
.pid
801 self
._fields
_filter
= options
.fields
805 def get_providers(options
):
806 """Returns a list of data providers depending on the passed options."""
810 providers
.append(DebugfsProvider(options
.pid
, options
.fields
,
811 options
.dbgfs_include_past
))
812 if options
.tracepoints
or not providers
:
813 providers
.append(TracepointProvider(options
.pid
, options
.fields
))
817 def update_provider_filters(self
):
818 """Propagates fields filters to providers."""
819 # As we reset the counters when updating the fields we can
820 # also clear the cache of old values.
822 for provider
in self
.providers
:
823 provider
.update_fields(self
._fields
_filter
)
827 for provider
in self
.providers
:
831 def fields_filter(self
):
832 return self
._fields
_filter
834 @fields_filter.setter
835 def fields_filter(self
, fields_filter
):
836 if fields_filter
!= self
._fields
_filter
:
837 self
._fields
_filter
= fields_filter
838 self
.update_provider_filters()
841 def pid_filter(self
):
842 return self
._pid
_filter
845 def pid_filter(self
, pid
):
846 if pid
!= self
._pid
_filter
:
847 self
._pid
_filter
= pid
849 for provider
in self
.providers
:
850 provider
.pid
= self
._pid
_filter
853 """Returns a dict with field -> (value, delta to last value) of all
855 for provider
in self
.providers
:
856 new
= provider
.read()
857 for key
in provider
.fields
:
858 oldval
= self
.values
.get(key
, (0, 0))[0]
859 newval
= new
.get(key
, 0)
860 newdelta
= newval
- oldval
861 self
.values
[key
] = (newval
, newdelta
)
865 MAX_GUEST_NAME_LEN
= 48
867 DEFAULT_REGEX
= r
'^[^\(]*$'
872 """Instruments curses to draw a nice text ui."""
873 def __init__(self
, stats
):
876 self
._delay
_initial
= 0.25
877 self
._delay
_regular
= DELAY_DEFAULT
878 self
._sorting
= SORT_DEFAULT
881 """Initialises curses for later use. Based on curses.wrapper
882 implementation from the Python standard library."""
883 self
.screen
= curses
.initscr()
887 # The try/catch works around a minor bit of
888 # over-conscientiousness in the curses module, the error
889 # return from C start_color() is ignorable.
895 # Hide cursor in extra statement as some monochrome terminals
896 # might support hiding but not colors.
902 curses
.use_default_colors()
905 def __exit__(self
, *exception
):
906 """Resets the terminal to its normal state. Based on curses.wrapper
907 implementation from the Python standard library."""
909 self
.screen
.keypad(0)
914 def get_all_gnames(self
):
915 """Returns a list of (pid, gname) tuples of all running guests"""
918 child
= subprocess
.Popen(['ps', '-A', '--format', 'pid,args'],
919 stdout
=subprocess
.PIPE
)
922 for line
in child
.stdout
:
923 line
= line
.lstrip().split(' ', 1)
924 # perform a sanity check before calling the more expensive
925 # function to possibly extract the guest name
926 if ' -name ' in line
[1]:
927 res
.append((line
[0], self
.get_gname_from_pid(line
[0])))
932 def print_all_gnames(self
, row
):
933 """Print a list of all running guests along with their pids."""
934 self
.screen
.addstr(row
, 2, '%8s %-60s' %
935 ('Pid', 'Guest Name (fuzzy list, might be '
940 for line
in self
.get_all_gnames():
941 self
.screen
.addstr(row
, 2, '%8s %-60s' % (line
[0], line
[1]))
943 if row
>= self
.screen
.getmaxyx()[0]:
946 self
.screen
.addstr(row
+ 1, 2, 'Not available')
948 def get_pid_from_gname(self
, gname
):
949 """Fuzzy function to convert guest name to QEMU process pid.
951 Returns a list of potential pids, can be empty if no match found.
952 Throws an exception on processing errors.
956 for line
in self
.get_all_gnames():
958 pids
.append(int(line
[0]))
963 def get_gname_from_pid(pid
):
964 """Returns the guest name for a QEMU process pid.
966 Extracts the guest name from the QEMU comma line by processing the
967 '-name' option. Will also handle names specified out of sequence.
972 line
= open('/proc/{}/cmdline'
973 .format(pid
), 'rb').read().split('\0')
974 parms
= line
[line
.index('-name') + 1].split(',')
976 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
977 # in # ['foo', '', 'bar'], which we revert here
978 idx
= parms
.index('')
979 parms
[idx
- 1] += ',' + parms
[idx
+ 1]
981 # the '-name' switch allows for two ways to specify the guest name,
982 # where the plain name overrides the name specified via 'guest='
987 if arg
[:6] == 'guest=':
989 except (ValueError, IOError, IndexError):
994 def update_drilldown(self
):
995 """Sets or removes a filter that only allows fields without braces."""
996 if not self
.stats
.fields_filter
:
997 self
.stats
.fields_filter
= DEFAULT_REGEX
999 elif self
.stats
.fields_filter
== DEFAULT_REGEX
:
1000 self
.stats
.fields_filter
= None
1002 def update_pid(self
, pid
):
1003 """Propagates pid selection to stats object."""
1004 self
.stats
.pid_filter
= pid
1006 def refresh_header(self
, pid
=None):
1007 """Refreshes the header."""
1009 pid
= self
.stats
.pid_filter
1011 gname
= self
.get_gname_from_pid(pid
)
1013 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
1014 if len(gname
) > MAX_GUEST_NAME_LEN
1017 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1018 .format(pid
, gname
), curses
.A_BOLD
)
1020 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
1021 if self
.stats
.fields_filter
and self
.stats
.fields_filter \
1023 regex
= self
.stats
.fields_filter
1024 if len(regex
) > MAX_REGEX_LEN
:
1025 regex
= regex
[:MAX_REGEX_LEN
] + '...'
1026 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
1027 self
.screen
.addstr(2, 1, '%-40s %10s%7s %8s' %
1028 ('Event', 'Total', '%Total', 'CurAvg/s'),
1030 self
.screen
.addstr(4, 1, 'Collecting data...')
1031 self
.screen
.refresh()
1033 def refresh_body(self
, sleeptime
):
1035 self
.screen
.move(row
, 0)
1036 self
.screen
.clrtobot()
1037 stats
= self
.stats
.get()
1040 # sort by current events if available
1042 return (-stats
[x
][1], -stats
[x
][0])
1044 return (0, -stats
[x
][0])
1048 return (0, -stats
[x
][0])
1050 for val
in stats
.values():
1052 if self
._sorting
== SORT_DEFAULT
:
1053 sortkey
= sortCurAvg
1056 for key
in sorted(stats
.keys(), key
=sortkey
):
1058 if row
>= self
.screen
.getmaxyx()[0]:
1061 if not values
[0] and not values
[1]:
1063 if values
[0] is not None:
1064 cur
= int(round(values
[1] / sleeptime
)) if values
[1] else ''
1065 self
.screen
.addstr(row
, 1, '%-40s %10d%7.1f %8s' %
1066 (key
, values
[0], values
[0] * 100 / total
,
1070 self
.screen
.addstr(4, 1, 'No matching events reported yet')
1071 self
.screen
.refresh()
1073 def show_help_interactive(self
):
1074 """Display help with list of interactive commands"""
1075 msg
= (' c clear filter',
1076 ' f filter by regular expression',
1077 ' g filter by guest name',
1078 ' h display interactive commands reference',
1079 ' o toggle sorting order (Total vs CurAvg/s)',
1083 ' s set update interval',
1084 ' x toggle reporting of stats for individual child trace'
1086 'Any other key refreshes statistics immediately')
1089 self
.screen
.addstr(0, 0, "Interactive commands reference",
1091 self
.screen
.addstr(2, 0, "Press any key to exit", curses
.A_STANDOUT
)
1094 self
.screen
.addstr(row
, 0, line
)
1096 self
.screen
.getkey()
1097 self
.refresh_header()
1099 def show_filter_selection(self
):
1100 """Draws filter selection mask.
1102 Asks for a valid regex and sets the fields filter accordingly.
1107 self
.screen
.addstr(0, 0,
1108 "Show statistics for events matching a regex.",
1110 self
.screen
.addstr(2, 0,
1111 "Current regex: {0}"
1112 .format(self
.stats
.fields_filter
))
1113 self
.screen
.addstr(3, 0, "New regex: ")
1115 regex
= self
.screen
.getstr()
1118 self
.stats
.fields_filter
= DEFAULT_REGEX
1119 self
.refresh_header()
1123 self
.stats
.fields_filter
= regex
1124 self
.refresh_header()
1129 def show_vm_selection_by_pid(self
):
1130 """Draws PID selection mask.
1132 Asks for a pid until a valid pid or 0 has been entered.
1138 self
.screen
.addstr(0, 0,
1139 'Show statistics for specific pid.',
1141 self
.screen
.addstr(1, 0,
1142 'This might limit the shown data to the trace '
1144 self
.screen
.addstr(5, 0, msg
)
1145 self
.print_all_gnames(7)
1148 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1149 pid
= self
.screen
.getstr()
1155 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1157 msg
= '"' + str(pid
) + '": Not a running process'
1161 self
.refresh_header(pid
)
1162 self
.update_pid(pid
)
1165 msg
= '"' + str(pid
) + '": Not a valid pid'
1167 def show_set_update_interval(self
):
1168 """Draws update interval selection mask."""
1172 self
.screen
.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1173 DELAY_DEFAULT
, curses
.A_BOLD
)
1174 self
.screen
.addstr(4, 0, msg
)
1175 self
.screen
.addstr(2, 0, 'Change delay from %.1fs to ' %
1176 self
._delay
_regular
)
1178 val
= self
.screen
.getstr()
1185 msg
= '"' + str(val
) + '": Value must be >=0.1'
1188 msg
= '"' + str(val
) + '": Value must be <=25.5'
1191 delay
= DELAY_DEFAULT
1192 self
._delay
_regular
= delay
1196 msg
= '"' + str(val
) + '": Invalid value'
1197 self
.refresh_header()
1199 def show_vm_selection_by_guest_name(self
):
1200 """Draws guest selection mask.
1202 Asks for a guest name until a valid guest name or '' is entered.
1208 self
.screen
.addstr(0, 0,
1209 'Show statistics for specific guest.',
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)
1217 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1218 gname
= self
.screen
.getstr()
1222 self
.refresh_header(0)
1228 pids
= self
.get_pid_from_gname(gname
)
1230 msg
= '"' + gname
+ '": Internal error while searching, ' \
1231 'use pid filter instead'
1234 msg
= '"' + gname
+ '": Not an active guest'
1237 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1240 self
.refresh_header(pids
[0])
1241 self
.update_pid(pids
[0])
1244 def show_stats(self
):
1245 """Refreshes the screen and processes user input."""
1246 sleeptime
= self
._delay
_initial
1247 self
.refresh_header()
1248 start
= 0.0 # result based on init value never appears on screen
1250 self
.refresh_body(time
.time() - start
)
1251 curses
.halfdelay(int(sleeptime
* 10))
1253 sleeptime
= self
._delay
_regular
1255 char
= self
.screen
.getkey()
1257 self
.stats
.fields_filter
= DEFAULT_REGEX
1258 self
.refresh_header(0)
1262 self
.show_filter_selection()
1264 sleeptime
= self
._delay
_initial
1267 self
.show_vm_selection_by_guest_name()
1269 sleeptime
= self
._delay
_initial
1271 self
.show_help_interactive()
1273 self
._sorting
= not self
._sorting
1276 self
.show_vm_selection_by_pid()
1278 sleeptime
= self
._delay
_initial
1285 self
.show_set_update_interval()
1287 sleeptime
= self
._delay
_initial
1289 self
.update_drilldown()
1290 # prevents display of current values on next refresh
1292 except KeyboardInterrupt:
1294 except curses
.error
:
1299 """Prints statistics in a key, value format."""
1304 for key
in sorted(s
.keys()):
1306 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1307 except KeyboardInterrupt:
1312 """Prints statistics as reiterating key block, multiple value blocks."""
1313 keys
= sorted(stats
.get().iterkeys())
1323 print ' %9d' % s
[k
][1],
1330 if line
% banner_repeat
== 0:
1334 except KeyboardInterrupt:
1339 """Returns processed program arguments."""
1340 description_text
= """
1341 This script displays various statistics about VMs running under KVM.
1342 The statistics are gathered from the KVM debugfs entries and / or the
1343 currently available perf traces.
1345 The monitoring takes additional cpu cycles and might affect the VM's
1350 /sys/kernel/debug/kvm
1351 /sys/kernel/debug/trace/events/*
1353 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1354 CAP_SYS_ADMIN and perf events are used.
1355 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1356 the large number of files that are possibly opened.
1358 Interactive Commands:
1360 f filter by regular expression
1361 g filter by guest name
1362 h display interactive commands reference
1363 o toggle sorting order (Total vs CurAvg/s)
1367 s set update interval
1368 x toggle reporting of stats for individual child trace events
1369 Press any other key to refresh statistics immediately.
1372 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1373 def format_description(self
, description
):
1375 return description
+ "\n"
1379 def cb_guest_to_pid(option
, opt
, val
, parser
):
1381 pids
= Tui
.get_pid_from_gname(val
)
1383 raise optparse
.OptionValueError('Error while searching for guest '
1384 '"{}", use "-p" to specify a pid '
1385 'instead'.format(val
))
1387 raise optparse
.OptionValueError('No guest by the name "{}" '
1388 'found'.format(val
))
1390 raise optparse
.OptionValueError('Multiple processes found (pids: '
1391 '{}) - use "-p" to specify a pid '
1392 'instead'.format(" ".join(pids
)))
1393 parser
.values
.pid
= pids
[0]
1395 optparser
= optparse
.OptionParser(description
=description_text
,
1396 formatter
=PlainHelpFormatter())
1397 optparser
.add_option('-1', '--once', '--batch',
1398 action
='store_true',
1401 help='run in batch mode for one second',
1403 optparser
.add_option('-i', '--debugfs-include-past',
1404 action
='store_true',
1406 dest
='dbgfs_include_past',
1407 help='include all available data on past events for '
1410 optparser
.add_option('-l', '--log',
1411 action
='store_true',
1414 help='run in logging mode (like vmstat)',
1416 optparser
.add_option('-t', '--tracepoints',
1417 action
='store_true',
1420 help='retrieve statistics from tracepoints',
1422 optparser
.add_option('-d', '--debugfs',
1423 action
='store_true',
1426 help='retrieve statistics from debugfs',
1428 optparser
.add_option('-f', '--fields',
1430 default
=DEFAULT_REGEX
,
1432 help='fields to display (regex)',
1434 optparser
.add_option('-p', '--pid',
1439 help='restrict statistics to pid',
1441 optparser
.add_option('-g', '--guest',
1446 help='restrict statistics to guest by name',
1447 callback
=cb_guest_to_pid
,
1449 (options
, _
) = optparser
.parse_args(sys
.argv
)
1453 def check_access(options
):
1454 """Exits if the current user can't access all needed directories."""
1455 if not os
.path
.exists('/sys/kernel/debug'):
1456 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1459 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1460 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1461 "readable by the current user:\n"
1462 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1463 "Also ensure, that the kvm modules are loaded.\n")
1466 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1467 not options
.debugfs
):
1468 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1469 "when using the option -t (default).\n"
1470 "If it is enabled, make {0} readable by the "
1472 .format(PATH_DEBUGFS_TRACING
))
1473 if options
.tracepoints
:
1476 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1477 options
.debugfs
= True
1484 options
= get_options()
1485 options
= check_access(options
)
1487 if (options
.pid
> 0 and
1488 not os
.path
.isdir(os
.path
.join('/proc/',
1489 str(options
.pid
)))):
1490 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1491 sys
.exit('Specified pid does not exist.')
1493 stats
= Stats(options
)
1497 elif not options
.once
:
1498 with
Tui(stats
) as tui
:
1503 if __name__
== "__main__":