]>
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 class DebugfsProvider(object):
721 """Provides data from the files that KVM creates in the kvm debugfs
724 self
._fields
= self
.get_available_fields()
729 def get_available_fields(self
):
730 """"Returns a list of available fields.
732 The fields are all available KVM debugfs files
735 return walkdir(PATH_DEBUGFS_KVM
)[2]
742 def fields(self
, fields
):
743 self
._fields
= fields
754 vms
= walkdir(PATH_DEBUGFS_KVM
)[1]
758 self
.paths
= filter(lambda x
: "{}-".format(pid
) in x
, vms
)
765 """Returns a dict with format:'file name / field -> current value'."""
768 # If no debugfs filtering support is available, then don't read.
772 for path
in self
.paths
:
773 for field
in self
._fields
:
774 results
[field
] = results
.get(field
, 0) \
775 + self
.read_field(field
, path
)
779 def read_field(self
, field
, path
):
780 """Returns the value of a single field from a specific VM."""
782 return int(open(os
.path
.join(PATH_DEBUGFS_KVM
,
791 """Manages the data providers and the data they provide.
793 It is used to set filters on the provider's data and collect all
797 def __init__(self
, providers
, pid
, fields
=None):
798 self
.providers
= providers
799 self
._pid
_filter
= pid
800 self
._fields
_filter
= fields
802 self
.update_provider_pid()
803 self
.update_provider_filters()
805 def update_provider_filters(self
):
806 """Propagates fields filters to providers."""
808 if not self
._fields
_filter
:
810 return re
.match(self
._fields
_filter
, key
) is not None
812 # As we reset the counters when updating the fields we can
813 # also clear the cache of old values.
815 for provider
in self
.providers
:
816 provider_fields
= [key
for key
in provider
.get_available_fields()
818 provider
.fields
= provider_fields
820 def update_provider_pid(self
):
821 """Propagates pid filters to providers."""
822 for provider
in self
.providers
:
823 provider
.pid
= self
._pid
_filter
826 def fields_filter(self
):
827 return self
._fields
_filter
829 @fields_filter.setter
830 def fields_filter(self
, fields_filter
):
831 self
._fields
_filter
= fields_filter
832 self
.update_provider_filters()
835 def pid_filter(self
):
836 return self
._pid
_filter
839 def pid_filter(self
, pid
):
840 self
._pid
_filter
= pid
842 self
.update_provider_pid()
845 """Returns a dict with field -> (value, delta to last value) of all
847 for provider
in self
.providers
:
848 new
= provider
.read()
849 for key
in provider
.fields
:
850 oldval
= self
.values
.get(key
, (0, 0))
851 newval
= new
.get(key
, 0)
853 if oldval
is not None:
854 newdelta
= newval
- oldval
[0]
855 self
.values
[key
] = (newval
, newdelta
)
862 MAX_GUEST_NAME_LEN
= 48
867 """Instruments curses to draw a nice text ui."""
868 def __init__(self
, stats
):
871 self
.update_drilldown()
874 """Initialises curses for later use. Based on curses.wrapper
875 implementation from the Python standard library."""
876 self
.screen
= curses
.initscr()
880 # The try/catch works around a minor bit of
881 # over-conscientiousness in the curses module, the error
882 # return from C start_color() is ignorable.
888 # Hide cursor in extra statement as some monochrome terminals
889 # might support hiding but not colors.
895 curses
.use_default_colors()
898 def __exit__(self
, *exception
):
899 """Resets the terminal to its normal state. Based on curses.wrappre
900 implementation from the Python standard library."""
902 self
.screen
.keypad(0)
907 def update_drilldown(self
):
908 """Sets or removes a filter that only allows fields without braces."""
909 if not self
.stats
.fields_filter
:
910 self
.stats
.fields_filter
= r
'^[^\(]*$'
912 elif self
.stats
.fields_filter
== r
'^[^\(]*$':
913 self
.stats
.fields_filter
= None
915 def update_pid(self
, pid
):
916 """Propagates pid selection to stats object."""
917 self
.stats
.pid_filter
= pid
919 def refresh_header(self
, pid
=None):
920 """Refreshes the header."""
922 pid
= self
.stats
.pid_filter
924 gname
= get_gname_from_pid(pid
)
926 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
927 if len(gname
) > MAX_GUEST_NAME_LEN
930 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
931 .format(pid
, gname
), curses
.A_BOLD
)
933 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
934 if self
.stats
.fields_filter
and self
.stats
.fields_filter
!= '^[^\(]*$':
935 regex
= self
.stats
.fields_filter
936 if len(regex
) > MAX_REGEX_LEN
:
937 regex
= regex
[:MAX_REGEX_LEN
] + '...'
938 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
939 self
.screen
.addstr(2, 1, 'Event')
940 self
.screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
-
941 len('Total'), 'Total')
942 self
.screen
.addstr(2, 1 + LABEL_WIDTH
+ NUMBER_WIDTH
+ 8 -
943 len('Current'), 'Current')
944 self
.screen
.addstr(4, 1, 'Collecting data...')
945 self
.screen
.refresh()
947 def refresh_body(self
, sleeptime
):
949 self
.screen
.move(row
, 0)
950 self
.screen
.clrtobot()
951 stats
= self
.stats
.get()
955 return (-stats
[x
][1], -stats
[x
][0])
957 return (0, -stats
[x
][0])
958 for key
in sorted(stats
.keys(), key
=sortkey
):
960 if row
>= self
.screen
.getmaxyx()[0]:
963 if not values
[0] and not values
[1]:
966 self
.screen
.addstr(row
, col
, key
)
968 self
.screen
.addstr(row
, col
, '%10d' % (values
[0],))
970 if values
[1] is not None:
971 self
.screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
973 self
.screen
.refresh()
975 def show_filter_selection(self
):
976 """Draws filter selection mask.
978 Asks for a valid regex and sets the fields filter accordingly.
983 self
.screen
.addstr(0, 0,
984 "Show statistics for events matching a regex.",
986 self
.screen
.addstr(2, 0,
988 .format(self
.stats
.fields_filter
))
989 self
.screen
.addstr(3, 0, "New regex: ")
991 regex
= self
.screen
.getstr()
994 self
.stats
.fields_filter
= r
'^[^\(]*$'
995 self
.refresh_header()
999 self
.stats
.fields_filter
= regex
1000 self
.refresh_header()
1005 def show_vm_selection_by_pid(self
):
1006 """Draws PID selection mask.
1008 Asks for a pid until a valid pid or 0 has been entered.
1014 self
.screen
.addstr(0, 0,
1015 'Show statistics for specific pid.',
1017 self
.screen
.addstr(1, 0,
1018 'This might limit the shown data to the trace '
1020 self
.screen
.addstr(5, 0, msg
)
1023 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1024 pid
= self
.screen
.getstr()
1030 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1032 msg
= '"' + str(pid
) + '": Not a running process'
1036 self
.refresh_header(pid
)
1037 self
.update_pid(pid
)
1041 msg
= '"' + str(pid
) + '": Not a valid pid'
1044 def show_vm_selection_by_guest_name(self
):
1045 """Draws guest selection mask.
1047 Asks for a guest name until a valid guest name or '' is entered.
1053 self
.screen
.addstr(0, 0,
1054 'Show statistics for specific guest.',
1056 self
.screen
.addstr(1, 0,
1057 'This might limit the shown data to the trace '
1059 self
.screen
.addstr(5, 0, msg
)
1061 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1062 gname
= self
.screen
.getstr()
1066 self
.refresh_header(0)
1072 pids
= get_pid_from_gname(gname
)
1074 msg
= '"' + gname
+ '": Internal error while searching, ' \
1075 'use pid filter instead'
1078 msg
= '"' + gname
+ '": Not an active guest'
1081 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1084 self
.refresh_header(pids
[0])
1085 self
.update_pid(pids
[0])
1088 def show_stats(self
):
1089 """Refreshes the screen and processes user input."""
1090 sleeptime
= DELAY_INITIAL
1091 self
.refresh_header()
1093 self
.refresh_body(sleeptime
)
1094 curses
.halfdelay(int(sleeptime
* 10))
1095 sleeptime
= DELAY_REGULAR
1097 char
= self
.screen
.getkey()
1099 self
.refresh_header()
1100 self
.update_drilldown()
1101 sleeptime
= DELAY_INITIAL
1105 self
.show_filter_selection()
1106 sleeptime
= DELAY_INITIAL
1108 self
.show_vm_selection_by_guest_name()
1109 sleeptime
= DELAY_INITIAL
1111 self
.show_vm_selection_by_pid()
1112 sleeptime
= DELAY_INITIAL
1113 except KeyboardInterrupt:
1115 except curses
.error
:
1120 """Prints statistics in a key, value format."""
1125 for key
in sorted(s
.keys()):
1127 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1128 except KeyboardInterrupt:
1133 """Prints statistics as reiterating key block, multiple value blocks."""
1134 keys
= sorted(stats
.get().iterkeys())
1144 print ' %9d' % s
[k
][1],
1151 if line
% banner_repeat
== 0:
1155 except KeyboardInterrupt:
1160 """Returns processed program arguments."""
1161 description_text
= """
1162 This script displays various statistics about VMs running under KVM.
1163 The statistics are gathered from the KVM debugfs entries and / or the
1164 currently available perf traces.
1166 The monitoring takes additional cpu cycles and might affect the VM's
1171 /sys/kernel/debug/kvm
1172 /sys/kernel/debug/trace/events/*
1174 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1175 CAP_SYS_ADMIN and perf events are used.
1176 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1177 the large number of files that are possibly opened.
1179 Interactive Commands:
1180 f filter by regular expression
1181 g filter by guest name
1184 x toggle reporting of stats for individual child trace events
1185 Press any other key to refresh statistics immediately.
1188 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1189 def format_description(self
, description
):
1191 return description
+ "\n"
1195 def cb_guest_to_pid(option
, opt
, val
, parser
):
1197 pids
= get_pid_from_gname(val
)
1199 raise optparse
.OptionValueError('Error while searching for guest '
1200 '"{}", use "-p" to specify a pid '
1201 'instead'.format(val
))
1203 raise optparse
.OptionValueError('No guest by the name "{}" '
1204 'found'.format(val
))
1206 raise optparse
.OptionValueError('Multiple processes found (pids: '
1207 '{}) - use "-p" to specify a pid '
1208 'instead'.format(" ".join(pids
)))
1209 parser
.values
.pid
= pids
[0]
1211 optparser
= optparse
.OptionParser(description
=description_text
,
1212 formatter
=PlainHelpFormatter())
1213 optparser
.add_option('-1', '--once', '--batch',
1214 action
='store_true',
1217 help='run in batch mode for one second',
1219 optparser
.add_option('-l', '--log',
1220 action
='store_true',
1223 help='run in logging mode (like vmstat)',
1225 optparser
.add_option('-t', '--tracepoints',
1226 action
='store_true',
1229 help='retrieve statistics from tracepoints',
1231 optparser
.add_option('-d', '--debugfs',
1232 action
='store_true',
1235 help='retrieve statistics from debugfs',
1237 optparser
.add_option('-f', '--fields',
1241 help='fields to display (regex)',
1243 optparser
.add_option('-p', '--pid',
1248 help='restrict statistics to pid',
1250 optparser
.add_option('-g', '--guest',
1255 help='restrict statistics to guest by name',
1256 callback
=cb_guest_to_pid
,
1258 (options
, _
) = optparser
.parse_args(sys
.argv
)
1262 def get_providers(options
):
1263 """Returns a list of data providers depending on the passed options."""
1266 if options
.tracepoints
:
1267 providers
.append(TracepointProvider())
1269 providers
.append(DebugfsProvider())
1270 if len(providers
) == 0:
1271 providers
.append(TracepointProvider())
1276 def check_access(options
):
1277 """Exits if the current user can't access all needed directories."""
1278 if not os
.path
.exists('/sys/kernel/debug'):
1279 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1282 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1283 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1284 "readable by the current user:\n"
1285 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1286 "Also ensure, that the kvm modules are loaded.\n")
1289 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1290 not options
.debugfs
):
1291 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1292 "when using the option -t (default).\n"
1293 "If it is enabled, make {0} readable by the "
1295 .format(PATH_DEBUGFS_TRACING
))
1296 if options
.tracepoints
:
1299 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1300 options
.debugfs
= True
1307 options
= get_options()
1308 options
= check_access(options
)
1310 if (options
.pid
> 0 and
1311 not os
.path
.isdir(os
.path
.join('/proc/',
1312 str(options
.pid
)))):
1313 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1314 sys
.exit('Specified pid does not exist.')
1316 providers
= get_providers(options
)
1317 stats
= Stats(providers
, options
.pid
, fields
=options
.fields
)
1321 elif not options
.once
:
1322 with
Tui(stats
) as tui
:
1327 if __name__
== "__main__":