]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - tools/kvm/kvm_stat/kvm_stat
a527b2fc66850fc108f8fc9401a833f025625c20
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
)
892 MAX_GUEST_NAME_LEN
= 48
894 DEFAULT_REGEX
= r
'^[^\(]*$'
898 """Instruments curses to draw a nice text ui."""
899 def __init__(self
, stats
):
902 self
.update_drilldown()
905 """Initialises curses for later use. Based on curses.wrapper
906 implementation from the Python standard library."""
907 self
.screen
= curses
.initscr()
911 # The try/catch works around a minor bit of
912 # over-conscientiousness in the curses module, the error
913 # return from C start_color() is ignorable.
919 # Hide cursor in extra statement as some monochrome terminals
920 # might support hiding but not colors.
926 curses
.use_default_colors()
929 def __exit__(self
, *exception
):
930 """Resets the terminal to its normal state. Based on curses.wrapper
931 implementation from the Python standard library."""
933 self
.screen
.keypad(0)
938 def update_drilldown(self
):
939 """Sets or removes a filter that only allows fields without braces."""
940 if not self
.stats
.fields_filter
:
941 self
.stats
.fields_filter
= DEFAULT_REGEX
943 elif self
.stats
.fields_filter
== DEFAULT_REGEX
:
944 self
.stats
.fields_filter
= None
946 def update_pid(self
, pid
):
947 """Propagates pid selection to stats object."""
948 self
.stats
.pid_filter
= pid
950 def refresh_header(self
, pid
=None):
951 """Refreshes the header."""
953 pid
= self
.stats
.pid_filter
955 gname
= get_gname_from_pid(pid
)
957 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
958 if len(gname
) > MAX_GUEST_NAME_LEN
961 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
962 .format(pid
, gname
), curses
.A_BOLD
)
964 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
965 if self
.stats
.fields_filter
and self
.stats
.fields_filter \
967 regex
= self
.stats
.fields_filter
968 if len(regex
) > MAX_REGEX_LEN
:
969 regex
= regex
[:MAX_REGEX_LEN
] + '...'
970 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
971 self
.screen
.addstr(2, 1, '%-40s %10s%7s %7s' %
972 ('Event', 'Total', '%Total', 'Current'))
973 self
.screen
.addstr(4, 1, 'Collecting data...')
974 self
.screen
.refresh()
976 def refresh_body(self
, sleeptime
):
978 self
.screen
.move(row
, 0)
979 self
.screen
.clrtobot()
980 stats
= self
.stats
.get()
984 return (-stats
[x
][1], -stats
[x
][0])
986 return (0, -stats
[x
][0])
988 for val
in stats
.values():
990 for key
in sorted(stats
.keys(), key
=sortkey
):
992 if row
>= self
.screen
.getmaxyx()[0]:
995 if not values
[0] and not values
[1]:
997 if values
[0] is not None:
998 cur
= int(round(values
[1] / sleeptime
)) if values
[1] else ''
999 self
.screen
.addstr(row
, 1, '%-40s %10d%7.1f %7s' %
1000 (key
, values
[0], values
[0] * 100 / total
,
1003 self
.screen
.refresh()
1005 def show_filter_selection(self
):
1006 """Draws filter selection mask.
1008 Asks for a valid regex and sets the fields filter accordingly.
1013 self
.screen
.addstr(0, 0,
1014 "Show statistics for events matching a regex.",
1016 self
.screen
.addstr(2, 0,
1017 "Current regex: {0}"
1018 .format(self
.stats
.fields_filter
))
1019 self
.screen
.addstr(3, 0, "New regex: ")
1021 regex
= self
.screen
.getstr()
1024 self
.stats
.fields_filter
= DEFAULT_REGEX
1025 self
.refresh_header()
1029 self
.stats
.fields_filter
= regex
1030 self
.refresh_header()
1035 def show_vm_selection_by_pid(self
):
1036 """Draws PID selection mask.
1038 Asks for a pid until a valid pid or 0 has been entered.
1044 self
.screen
.addstr(0, 0,
1045 'Show statistics for specific pid.',
1047 self
.screen
.addstr(1, 0,
1048 'This might limit the shown data to the trace '
1050 self
.screen
.addstr(5, 0, msg
)
1053 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1054 pid
= self
.screen
.getstr()
1060 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1062 msg
= '"' + str(pid
) + '": Not a running process'
1066 self
.refresh_header(pid
)
1067 self
.update_pid(pid
)
1071 msg
= '"' + str(pid
) + '": Not a valid pid'
1074 def show_vm_selection_by_guest_name(self
):
1075 """Draws guest selection mask.
1077 Asks for a guest name until a valid guest name or '' is entered.
1083 self
.screen
.addstr(0, 0,
1084 'Show statistics for specific guest.',
1086 self
.screen
.addstr(1, 0,
1087 'This might limit the shown data to the trace '
1089 self
.screen
.addstr(5, 0, msg
)
1091 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1092 gname
= self
.screen
.getstr()
1096 self
.refresh_header(0)
1102 pids
= get_pid_from_gname(gname
)
1104 msg
= '"' + gname
+ '": Internal error while searching, ' \
1105 'use pid filter instead'
1108 msg
= '"' + gname
+ '": Not an active guest'
1111 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1114 self
.refresh_header(pids
[0])
1115 self
.update_pid(pids
[0])
1118 def show_stats(self
):
1119 """Refreshes the screen and processes user input."""
1120 sleeptime
= DELAY_INITIAL
1121 self
.refresh_header()
1122 start
= 0.0 # result based on init value never appears on screen
1124 self
.refresh_body(time
.time() - start
)
1125 curses
.halfdelay(int(sleeptime
* 10))
1127 sleeptime
= DELAY_REGULAR
1129 char
= self
.screen
.getkey()
1131 self
.update_drilldown()
1135 self
.stats
.fields_filter
= DEFAULT_REGEX
1136 self
.refresh_header(0)
1139 self
.show_filter_selection()
1140 sleeptime
= DELAY_INITIAL
1142 self
.show_vm_selection_by_guest_name()
1143 sleeptime
= DELAY_INITIAL
1145 self
.show_vm_selection_by_pid()
1146 sleeptime
= DELAY_INITIAL
1149 except KeyboardInterrupt:
1151 except curses
.error
:
1156 """Prints statistics in a key, value format."""
1161 for key
in sorted(s
.keys()):
1163 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1164 except KeyboardInterrupt:
1169 """Prints statistics as reiterating key block, multiple value blocks."""
1170 keys
= sorted(stats
.get().iterkeys())
1180 print ' %9d' % s
[k
][1],
1187 if line
% banner_repeat
== 0:
1191 except KeyboardInterrupt:
1196 """Returns processed program arguments."""
1197 description_text
= """
1198 This script displays various statistics about VMs running under KVM.
1199 The statistics are gathered from the KVM debugfs entries and / or the
1200 currently available perf traces.
1202 The monitoring takes additional cpu cycles and might affect the VM's
1207 /sys/kernel/debug/kvm
1208 /sys/kernel/debug/trace/events/*
1210 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1211 CAP_SYS_ADMIN and perf events are used.
1212 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1213 the large number of files that are possibly opened.
1215 Interactive Commands:
1217 f filter by regular expression
1218 g filter by guest name
1221 x toggle reporting of stats for individual child trace events
1223 Press any other key to refresh statistics immediately.
1226 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1227 def format_description(self
, description
):
1229 return description
+ "\n"
1233 def cb_guest_to_pid(option
, opt
, val
, parser
):
1235 pids
= get_pid_from_gname(val
)
1237 raise optparse
.OptionValueError('Error while searching for guest '
1238 '"{}", use "-p" to specify a pid '
1239 'instead'.format(val
))
1241 raise optparse
.OptionValueError('No guest by the name "{}" '
1242 'found'.format(val
))
1244 raise optparse
.OptionValueError('Multiple processes found (pids: '
1245 '{}) - use "-p" to specify a pid '
1246 'instead'.format(" ".join(pids
)))
1247 parser
.values
.pid
= pids
[0]
1249 optparser
= optparse
.OptionParser(description
=description_text
,
1250 formatter
=PlainHelpFormatter())
1251 optparser
.add_option('-1', '--once', '--batch',
1252 action
='store_true',
1255 help='run in batch mode for one second',
1257 optparser
.add_option('-l', '--log',
1258 action
='store_true',
1261 help='run in logging mode (like vmstat)',
1263 optparser
.add_option('-t', '--tracepoints',
1264 action
='store_true',
1267 help='retrieve statistics from tracepoints',
1269 optparser
.add_option('-d', '--debugfs',
1270 action
='store_true',
1273 help='retrieve statistics from debugfs',
1275 optparser
.add_option('-f', '--fields',
1279 help='fields to display (regex)',
1281 optparser
.add_option('-p', '--pid',
1286 help='restrict statistics to pid',
1288 optparser
.add_option('-g', '--guest',
1293 help='restrict statistics to guest by name',
1294 callback
=cb_guest_to_pid
,
1296 (options
, _
) = optparser
.parse_args(sys
.argv
)
1300 def get_providers(options
):
1301 """Returns a list of data providers depending on the passed options."""
1304 if options
.tracepoints
:
1305 providers
.append(TracepointProvider())
1307 providers
.append(DebugfsProvider())
1308 if len(providers
) == 0:
1309 providers
.append(TracepointProvider())
1314 def check_access(options
):
1315 """Exits if the current user can't access all needed directories."""
1316 if not os
.path
.exists('/sys/kernel/debug'):
1317 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1320 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1321 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1322 "readable by the current user:\n"
1323 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1324 "Also ensure, that the kvm modules are loaded.\n")
1327 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1328 not options
.debugfs
):
1329 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1330 "when using the option -t (default).\n"
1331 "If it is enabled, make {0} readable by the "
1333 .format(PATH_DEBUGFS_TRACING
))
1334 if options
.tracepoints
:
1337 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1338 options
.debugfs
= True
1345 options
= get_options()
1346 options
= check_access(options
)
1348 if (options
.pid
> 0 and
1349 not os
.path
.isdir(os
.path
.join('/proc/',
1350 str(options
.pid
)))):
1351 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1352 sys
.exit('Specified pid does not exist.')
1354 providers
= get_providers(options
)
1355 stats
= Stats(providers
, options
.pid
, fields
=options
.fields
)
1359 elif not options
.once
:
1360 with
Tui(stats
) as tui
:
1365 if __name__
== "__main__":