]>
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()
299 """Returns os.walk() data for specified directory.
301 As it is only a wrapper it returns the same 3-tuple of (dirpath,
302 dirnames, filenames).
304 return next(os
.walk(path
))
307 def parse_int_list(list_string
):
308 """Returns an int list from a string of comma separated integers and
311 members
= list_string
.split(',')
313 for member
in members
:
314 if '-' not in member
:
315 integers
.append(int(member
))
317 int_range
= member
.split('-')
318 integers
.extend(range(int(int_range
[0]),
319 int(int_range
[1]) + 1))
324 def get_pid_from_gname(gname
):
325 """Fuzzy function to convert guest name to QEMU process pid.
327 Returns a list of potential pids, can be empty if no match found.
328 Throws an exception on processing errors.
333 child
= subprocess
.Popen(['ps', '-A', '--format', 'pid,args'],
334 stdout
=subprocess
.PIPE
)
337 for line
in child
.stdout
:
338 line
= line
.lstrip().split(' ', 1)
339 # perform a sanity check before calling the more expensive
340 # function to possibly extract the guest name
341 if ' -name ' in line
[1] and gname
== get_gname_from_pid(line
[0]):
342 pids
.append(int(line
[0]))
348 def get_gname_from_pid(pid
):
349 """Returns the guest name for a QEMU process pid.
351 Extracts the guest name from the QEMU comma line by processing the '-name'
352 option. Will also handle names specified out of sequence.
357 line
= open('/proc/{}/cmdline'.format(pid
), 'rb').read().split('\0')
358 parms
= line
[line
.index('-name') + 1].split(',')
360 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results in
361 # ['foo', '', 'bar'], which we revert here
362 idx
= parms
.index('')
363 parms
[idx
- 1] += ',' + parms
[idx
+ 1]
365 # the '-name' switch allows for two ways to specify the guest name,
366 # where the plain name overrides the name specified via 'guest='
371 if arg
[:6] == 'guest=':
373 except (ValueError, IOError, IndexError):
379 def get_online_cpus():
380 """Returns a list of cpu id integers."""
381 with
open('/sys/devices/system/cpu/online') as cpu_list
:
382 cpu_string
= cpu_list
.readline()
383 return parse_int_list(cpu_string
)
387 """Returns a dict of trace events, their filter ids and
388 the values that can be filtered.
390 Trace events can be filtered for special values by setting a
391 filter string via an ioctl. The string normally has the format
392 identifier==value. For each filter a new event will be created, to
393 be able to distinguish the events.
397 filters
['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS
)
398 if ARCH
.exit_reasons
:
399 filters
['kvm_exit'] = ('exit_reason', ARCH
.exit_reasons
)
402 libc
= ctypes
.CDLL('libc.so.6', use_errno
=True)
403 syscall
= libc
.syscall
406 class perf_event_attr(ctypes
.Structure
):
407 """Struct that holds the necessary data to set up a trace event.
409 For an extensive explanation see perf_event_open(2) and
410 include/uapi/linux/perf_event.h, struct perf_event_attr
412 All fields that are not initialized in the constructor are 0.
415 _fields_
= [('type', ctypes
.c_uint32
),
416 ('size', ctypes
.c_uint32
),
417 ('config', ctypes
.c_uint64
),
418 ('sample_freq', ctypes
.c_uint64
),
419 ('sample_type', ctypes
.c_uint64
),
420 ('read_format', ctypes
.c_uint64
),
421 ('flags', ctypes
.c_uint64
),
422 ('wakeup_events', ctypes
.c_uint32
),
423 ('bp_type', ctypes
.c_uint32
),
424 ('bp_addr', ctypes
.c_uint64
),
425 ('bp_len', ctypes
.c_uint64
),
429 super(self
.__class
__, self
).__init
__()
430 self
.type = PERF_TYPE_TRACEPOINT
431 self
.size
= ctypes
.sizeof(self
)
432 self
.read_format
= PERF_FORMAT_GROUP
435 def perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
436 """Wrapper for the sys_perf_evt_open() syscall.
438 Used to set up performance events, returns a file descriptor or -1
443 - struct perf_event_attr *
444 - pid or -1 to monitor all pids
445 - cpu number or -1 to monitor all cpus
446 - The file descriptor of the group leader or -1 to create a group.
450 return syscall(ARCH
.sc_perf_evt_open
, ctypes
.pointer(attr
),
451 ctypes
.c_int(pid
), ctypes
.c_int(cpu
),
452 ctypes
.c_int(group_fd
), ctypes
.c_long(flags
))
454 PERF_TYPE_TRACEPOINT
= 2
455 PERF_FORMAT_GROUP
= 1 << 3
457 PATH_DEBUGFS_TRACING
= '/sys/kernel/debug/tracing'
458 PATH_DEBUGFS_KVM
= '/sys/kernel/debug/kvm'
462 """Represents a perf event group."""
467 def add_event(self
, event
):
468 self
.events
.append(event
)
471 """Returns a dict with 'event name: value' for all events in the
474 Values are read by reading from the file descriptor of the
475 event that is the group leader. See perf_event_open(2) for
478 Read format for the used event configuration is:
480 u64 nr; /* The number of events */
482 u64 value; /* The value of the event */
487 length
= 8 * (1 + len(self
.events
))
488 read_format
= 'xxxxxxxx' + 'Q' * len(self
.events
)
489 return dict(zip([event
.name
for event
in self
.events
],
490 struct
.unpack(read_format
,
491 os
.read(self
.events
[0].fd
, length
))))
495 """Represents a performance event and manages its life cycle."""
496 def __init__(self
, name
, group
, trace_cpu
, trace_pid
, trace_point
,
497 trace_filter
, trace_set
='kvm'):
500 self
.setup_event(group
, trace_cpu
, trace_pid
, trace_point
,
501 trace_filter
, trace_set
)
504 """Closes the event's file descriptor.
506 As no python file object was created for the file descriptor,
507 python will not reference count the descriptor and will not
508 close it itself automatically, so we do it.
514 def setup_event_attribute(self
, trace_set
, trace_point
):
515 """Returns an initialized ctype perf_event_attr struct."""
517 id_path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', trace_set
,
520 event_attr
= perf_event_attr()
521 event_attr
.config
= int(open(id_path
).read())
524 def setup_event(self
, group
, trace_cpu
, trace_pid
, trace_point
,
525 trace_filter
, trace_set
):
526 """Sets up the perf event in Linux.
528 Issues the syscall to register the event in the kernel and
529 then sets the optional filter.
533 event_attr
= self
.setup_event_attribute(trace_set
, trace_point
)
535 # First event will be group leader.
538 # All others have to pass the leader's descriptor instead.
540 group_leader
= group
.events
[0].fd
542 fd
= perf_event_open(event_attr
, trace_pid
,
543 trace_cpu
, group_leader
, 0)
545 err
= ctypes
.get_errno()
546 raise OSError(err
, os
.strerror(err
),
547 'while calling sys_perf_event_open().')
550 fcntl
.ioctl(fd
, ARCH
.ioctl_numbers
['SET_FILTER'],
556 """Enables the trace event in the kernel.
558 Enabling the group leader makes reading counters from it and the
559 events under it possible.
562 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['ENABLE'], 0)
565 """Disables the trace event in the kernel.
567 Disabling the group leader makes reading all counters under it
571 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['DISABLE'], 0)
574 """Resets the count of the trace event in the kernel."""
575 fcntl
.ioctl(self
.fd
, ARCH
.ioctl_numbers
['RESET'], 0)
578 class TracepointProvider(object):
579 """Data provider for the stats class.
581 Manages the events/groups from which it acquires its data.
585 self
.group_leaders
= []
586 self
.filters
= get_filters()
587 self
._fields
= self
.get_available_fields()
590 def get_available_fields(self
):
591 """Returns a list of available event's of format 'event name(filter
594 All available events have directories under
595 /sys/kernel/debug/tracing/events/ which export information
596 about the specific event. Therefore, listing the dirs gives us
597 a list of all available events.
599 Some events like the vm exit reasons can be filtered for
600 specific values. To take account for that, the routine below
601 creates special fields with the following format:
602 event name(filter name)
605 path
= os
.path
.join(PATH_DEBUGFS_TRACING
, 'events', 'kvm')
606 fields
= walkdir(path
)[1]
609 if field
in self
.filters
:
610 filter_name_
, filter_dicts
= self
.filters
[field
]
611 for name
in filter_dicts
:
612 extra
.append(field
+ '(' + name
+ ')')
616 def setup_traces(self
):
617 """Creates all event and group objects needed to be able to retrieve
619 fields
= self
.get_available_fields()
621 # Fetch list of all threads of the monitored pid, as qemu
622 # starts a thread for each vcpu.
623 path
= os
.path
.join('/proc', str(self
._pid
), 'task')
624 groupids
= walkdir(path
)[1]
626 groupids
= get_online_cpus()
628 # The constant is needed as a buffer for python libs, std
629 # streams and other files that the script opens.
630 newlim
= len(groupids
) * len(fields
) + 50
632 softlim_
, hardlim
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)
635 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
636 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, newlim
))
638 # Raising the soft limit is sufficient.
639 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (newlim
, hardlim
))
642 sys
.exit("NOFILE rlimit could not be raised to {0}".format(newlim
))
644 for groupid
in groupids
:
649 match
= re
.match(r
'(.*)\((.*)\)', name
)
651 tracepoint
, sub
= match
.groups()
652 tracefilter
= ('%s==%d\0' %
653 (self
.filters
[tracepoint
][0],
654 self
.filters
[tracepoint
][1][sub
]))
656 # From perf_event_open(2):
657 # pid > 0 and cpu == -1
658 # This measures the specified process/thread on any CPU.
660 # pid == -1 and cpu >= 0
661 # This measures all processes/threads on the specified CPU.
662 trace_cpu
= groupid
if self
._pid
== 0 else -1
663 trace_pid
= int(groupid
) if self
._pid
!= 0 else -1
665 group
.add_event(Event(name
=name
,
669 trace_point
=tracepoint
,
670 trace_filter
=tracefilter
))
672 self
.group_leaders
.append(group
)
674 def available_fields(self
):
675 return self
.get_available_fields()
682 def fields(self
, fields
):
683 """Enables/disables the (un)wanted events"""
684 self
._fields
= fields
685 for group
in self
.group_leaders
:
686 for index
, event
in enumerate(group
.events
):
687 if event
.name
in fields
:
691 # Do not disable the group leader.
692 # It would disable all of its events.
702 """Changes the monitored pid by setting new traces."""
704 # The garbage collector will get rid of all Event/Group
705 # objects and open files after removing the references.
706 self
.group_leaders
= []
708 self
.fields
= self
._fields
711 """Returns 'event name: current value' for all enabled events."""
712 ret
= defaultdict(int)
713 for group
in self
.group_leaders
:
714 for name
, val
in group
.read().iteritems():
715 if name
in self
._fields
:
720 """Reset all field counters"""
721 for group
in self
.group_leaders
:
722 for event
in group
.events
:
726 class DebugfsProvider(object):
727 """Provides data from the files that KVM creates in the kvm debugfs
730 self
._fields
= self
.get_available_fields()
737 def get_available_fields(self
):
738 """"Returns a list of available fields.
740 The fields are all available KVM debugfs files
743 return walkdir(PATH_DEBUGFS_KVM
)[2]
750 def fields(self
, fields
):
751 self
._fields
= fields
763 vms
= walkdir(PATH_DEBUGFS_KVM
)[1]
767 self
.paths
= filter(lambda x
: "{}-".format(pid
) in x
, vms
)
774 def read(self
, reset
=0):
775 """Returns a dict with format:'file name / field -> current value'."""
778 # If no debugfs filtering support is available, then don't read.
785 for entry
in os
.walk(PATH_DEBUGFS_KVM
):
789 for field
in self
._fields
:
790 value
= self
.read_field(field
, path
)
793 self
._baseline
[key
] = value
794 if self
._baseline
.get(key
, -1) == -1:
795 self
._baseline
[key
] = value
796 results
[field
] = (results
.get(field
, 0) + value
-
797 self
._baseline
.get(key
, 0))
801 def read_field(self
, field
, path
):
802 """Returns the value of a single field from a specific VM."""
804 return int(open(os
.path
.join(PATH_DEBUGFS_KVM
,
812 """Reset field counters"""
818 """Manages the data providers and the data they provide.
820 It is used to set filters on the provider's data and collect all
824 def __init__(self
, providers
, pid
, fields
=None):
825 self
.providers
= providers
826 self
._pid
_filter
= pid
827 self
._fields
_filter
= fields
829 self
.update_provider_pid()
830 self
.update_provider_filters()
832 def update_provider_filters(self
):
833 """Propagates fields filters to providers."""
835 if not self
._fields
_filter
:
837 return re
.match(self
._fields
_filter
, key
) is not None
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_fields
= [key
for key
in provider
.get_available_fields()
845 provider
.fields
= provider_fields
847 def update_provider_pid(self
):
848 """Propagates pid filters to providers."""
849 for provider
in self
.providers
:
850 provider
.pid
= self
._pid
_filter
854 for provider
in self
.providers
:
858 def fields_filter(self
):
859 return self
._fields
_filter
861 @fields_filter.setter
862 def fields_filter(self
, fields_filter
):
863 if fields_filter
!= self
._fields
_filter
:
864 self
._fields
_filter
= fields_filter
865 self
.update_provider_filters()
868 def pid_filter(self
):
869 return self
._pid
_filter
872 def pid_filter(self
, pid
):
873 if pid
!= self
._pid
_filter
:
874 self
._pid
_filter
= pid
876 self
.update_provider_pid()
879 """Returns a dict with field -> (value, delta to last value) of all
881 for provider
in self
.providers
:
882 new
= provider
.read()
883 for key
in provider
.fields
:
884 oldval
= self
.values
.get(key
, (0, 0))[0]
885 newval
= new
.get(key
, 0)
886 newdelta
= newval
- oldval
887 self
.values
[key
] = (newval
, newdelta
)
894 MAX_GUEST_NAME_LEN
= 48
896 DEFAULT_REGEX
= r
'^[^\(]*$'
900 """Instruments curses to draw a nice text ui."""
901 def __init__(self
, stats
):
904 self
.update_drilldown()
907 """Initialises curses for later use. Based on curses.wrapper
908 implementation from the Python standard library."""
909 self
.screen
= curses
.initscr()
913 # The try/catch works around a minor bit of
914 # over-conscientiousness in the curses module, the error
915 # return from C start_color() is ignorable.
921 # Hide cursor in extra statement as some monochrome terminals
922 # might support hiding but not colors.
928 curses
.use_default_colors()
931 def __exit__(self
, *exception
):
932 """Resets the terminal to its normal state. Based on curses.wrappre
933 implementation from the Python standard library."""
935 self
.screen
.keypad(0)
940 def update_drilldown(self
):
941 """Sets or removes a filter that only allows fields without braces."""
942 if not self
.stats
.fields_filter
:
943 self
.stats
.fields_filter
= DEFAULT_REGEX
945 elif self
.stats
.fields_filter
== DEFAULT_REGEX
:
946 self
.stats
.fields_filter
= None
948 def update_pid(self
, pid
):
949 """Propagates pid selection to stats object."""
950 self
.stats
.pid_filter
= pid
952 def refresh_header(self
, pid
=None):
953 """Refreshes the header."""
955 pid
= self
.stats
.pid_filter
957 gname
= get_gname_from_pid(pid
)
959 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
960 if len(gname
) > MAX_GUEST_NAME_LEN
963 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
964 .format(pid
, gname
), curses
.A_BOLD
)
966 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
967 if self
.stats
.fields_filter
and self
.stats
.fields_filter \
969 regex
= self
.stats
.fields_filter
970 if len(regex
) > MAX_REGEX_LEN
:
971 regex
= regex
[:MAX_REGEX_LEN
] + '...'
972 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
973 self
.screen
.addstr(2, 1, 'Event')
974 self
.screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
-
975 len('Total'), 'Total')
976 self
.screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 7 -
977 len('%Total'), '%Total')
978 self
.screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 7 + 8 -
979 len('Current'), 'Current')
980 self
.screen
.addstr(4, 1, 'Collecting data...')
981 self
.screen
.refresh()
983 def refresh_body(self
, sleeptime
):
985 self
.screen
.move(row
, 0)
986 self
.screen
.clrtobot()
987 stats
= self
.stats
.get()
991 return (-stats
[x
][1], -stats
[x
][0])
993 return (0, -stats
[x
][0])
995 for val
in stats
.values():
997 for key
in sorted(stats
.keys(), key
=sortkey
):
999 if row
>= self
.screen
.getmaxyx()[0]:
1002 if not values
[0] and not values
[1]:
1005 self
.screen
.addstr(row
, col
, key
)
1007 self
.screen
.addstr(row
, col
, '%10d' % (values
[0],))
1009 self
.screen
.addstr(row
, col
, '%7.1f' % (values
[0] * 100 / total
,))
1011 if values
[1] is not None:
1012 self
.screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
1014 self
.screen
.refresh()
1016 def show_filter_selection(self
):
1017 """Draws filter selection mask.
1019 Asks for a valid regex and sets the fields filter accordingly.
1024 self
.screen
.addstr(0, 0,
1025 "Show statistics for events matching a regex.",
1027 self
.screen
.addstr(2, 0,
1028 "Current regex: {0}"
1029 .format(self
.stats
.fields_filter
))
1030 self
.screen
.addstr(3, 0, "New regex: ")
1032 regex
= self
.screen
.getstr()
1035 self
.stats
.fields_filter
= DEFAULT_REGEX
1036 self
.refresh_header()
1040 self
.stats
.fields_filter
= regex
1041 self
.refresh_header()
1046 def show_vm_selection_by_pid(self
):
1047 """Draws PID selection mask.
1049 Asks for a pid until a valid pid or 0 has been entered.
1055 self
.screen
.addstr(0, 0,
1056 'Show statistics for specific pid.',
1058 self
.screen
.addstr(1, 0,
1059 'This might limit the shown data to the trace '
1061 self
.screen
.addstr(5, 0, msg
)
1064 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1065 pid
= self
.screen
.getstr()
1071 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1073 msg
= '"' + str(pid
) + '": Not a running process'
1077 self
.refresh_header(pid
)
1078 self
.update_pid(pid
)
1082 msg
= '"' + str(pid
) + '": Not a valid pid'
1085 def show_vm_selection_by_guest_name(self
):
1086 """Draws guest selection mask.
1088 Asks for a guest name until a valid guest name or '' is entered.
1094 self
.screen
.addstr(0, 0,
1095 'Show statistics for specific guest.',
1097 self
.screen
.addstr(1, 0,
1098 'This might limit the shown data to the trace '
1100 self
.screen
.addstr(5, 0, msg
)
1102 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1103 gname
= self
.screen
.getstr()
1107 self
.refresh_header(0)
1113 pids
= get_pid_from_gname(gname
)
1115 msg
= '"' + gname
+ '": Internal error while searching, ' \
1116 'use pid filter instead'
1119 msg
= '"' + gname
+ '": Not an active guest'
1122 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1125 self
.refresh_header(pids
[0])
1126 self
.update_pid(pids
[0])
1129 def show_stats(self
):
1130 """Refreshes the screen and processes user input."""
1131 sleeptime
= DELAY_INITIAL
1132 self
.refresh_header()
1134 self
.refresh_body(sleeptime
)
1135 curses
.halfdelay(int(sleeptime
* 10))
1136 sleeptime
= DELAY_REGULAR
1138 char
= self
.screen
.getkey()
1140 self
.refresh_header()
1141 self
.update_drilldown()
1142 sleeptime
= DELAY_INITIAL
1146 self
.stats
.fields_filter
= DEFAULT_REGEX
1147 self
.refresh_header(0)
1149 sleeptime
= DELAY_INITIAL
1151 self
.show_filter_selection()
1152 sleeptime
= DELAY_INITIAL
1154 self
.show_vm_selection_by_guest_name()
1155 sleeptime
= DELAY_INITIAL
1157 self
.show_vm_selection_by_pid()
1158 sleeptime
= DELAY_INITIAL
1160 self
.refresh_header()
1162 sleeptime
= DELAY_INITIAL
1163 except KeyboardInterrupt:
1165 except curses
.error
:
1170 """Prints statistics in a key, value format."""
1175 for key
in sorted(s
.keys()):
1177 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1178 except KeyboardInterrupt:
1183 """Prints statistics as reiterating key block, multiple value blocks."""
1184 keys
= sorted(stats
.get().iterkeys())
1194 print ' %9d' % s
[k
][1],
1201 if line
% banner_repeat
== 0:
1205 except KeyboardInterrupt:
1210 """Returns processed program arguments."""
1211 description_text
= """
1212 This script displays various statistics about VMs running under KVM.
1213 The statistics are gathered from the KVM debugfs entries and / or the
1214 currently available perf traces.
1216 The monitoring takes additional cpu cycles and might affect the VM's
1221 /sys/kernel/debug/kvm
1222 /sys/kernel/debug/trace/events/*
1224 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1225 CAP_SYS_ADMIN and perf events are used.
1226 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1227 the large number of files that are possibly opened.
1229 Interactive Commands:
1231 f filter by regular expression
1232 g filter by guest name
1235 x toggle reporting of stats for individual child trace events
1237 Press any other key to refresh statistics immediately.
1240 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1241 def format_description(self
, description
):
1243 return description
+ "\n"
1247 def cb_guest_to_pid(option
, opt
, val
, parser
):
1249 pids
= get_pid_from_gname(val
)
1251 raise optparse
.OptionValueError('Error while searching for guest '
1252 '"{}", use "-p" to specify a pid '
1253 'instead'.format(val
))
1255 raise optparse
.OptionValueError('No guest by the name "{}" '
1256 'found'.format(val
))
1258 raise optparse
.OptionValueError('Multiple processes found (pids: '
1259 '{}) - use "-p" to specify a pid '
1260 'instead'.format(" ".join(pids
)))
1261 parser
.values
.pid
= pids
[0]
1263 optparser
= optparse
.OptionParser(description
=description_text
,
1264 formatter
=PlainHelpFormatter())
1265 optparser
.add_option('-1', '--once', '--batch',
1266 action
='store_true',
1269 help='run in batch mode for one second',
1271 optparser
.add_option('-l', '--log',
1272 action
='store_true',
1275 help='run in logging mode (like vmstat)',
1277 optparser
.add_option('-t', '--tracepoints',
1278 action
='store_true',
1281 help='retrieve statistics from tracepoints',
1283 optparser
.add_option('-d', '--debugfs',
1284 action
='store_true',
1287 help='retrieve statistics from debugfs',
1289 optparser
.add_option('-f', '--fields',
1293 help='fields to display (regex)',
1295 optparser
.add_option('-p', '--pid',
1300 help='restrict statistics to pid',
1302 optparser
.add_option('-g', '--guest',
1307 help='restrict statistics to guest by name',
1308 callback
=cb_guest_to_pid
,
1310 (options
, _
) = optparser
.parse_args(sys
.argv
)
1314 def get_providers(options
):
1315 """Returns a list of data providers depending on the passed options."""
1318 if options
.tracepoints
:
1319 providers
.append(TracepointProvider())
1321 providers
.append(DebugfsProvider())
1322 if len(providers
) == 0:
1323 providers
.append(TracepointProvider())
1328 def check_access(options
):
1329 """Exits if the current user can't access all needed directories."""
1330 if not os
.path
.exists('/sys/kernel/debug'):
1331 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1334 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1335 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1336 "readable by the current user:\n"
1337 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1338 "Also ensure, that the kvm modules are loaded.\n")
1341 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1342 not options
.debugfs
):
1343 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1344 "when using the option -t (default).\n"
1345 "If it is enabled, make {0} readable by the "
1347 .format(PATH_DEBUGFS_TRACING
))
1348 if options
.tracepoints
:
1351 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1352 options
.debugfs
= True
1359 options
= get_options()
1360 options
= check_access(options
)
1362 if (options
.pid
> 0 and
1363 not os
.path
.isdir(os
.path
.join('/proc/',
1364 str(options
.pid
)))):
1365 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1366 sys
.exit('Specified pid does not exist.')
1368 providers
= get_providers(options
)
1369 stats
= Stats(providers
, options
.pid
, fields
=options
.fields
)
1373 elif not options
.once
:
1374 with
Tui(stats
) as tui
:
1379 if __name__
== "__main__":