]>
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
):
685 self
.update_fields(fields_filter
)
691 def get_available_fields(self
):
692 """"Returns a list of available fields.
694 The fields are all available KVM debugfs files
697 return self
.walkdir(PATH_DEBUGFS_KVM
)[2]
699 def update_fields(self
, fields_filter
):
700 """Refresh fields, applying fields_filter"""
701 self
._fields
= [field
for field
in self
.get_available_fields()
702 if self
.is_field_wanted(fields_filter
, field
)]
709 def fields(self
, fields
):
710 self
._fields
= fields
721 vms
= self
.walkdir(PATH_DEBUGFS_KVM
)[1]
725 self
.paths
= filter(lambda x
: "{}-".format(pid
) in x
, vms
)
732 def read(self
, reset
=0):
733 """Returns a dict with format:'file name / field -> current value'."""
736 # If no debugfs filtering support is available, then don't read.
743 for entry
in os
.walk(PATH_DEBUGFS_KVM
):
747 for field
in self
._fields
:
748 value
= self
.read_field(field
, path
)
751 self
._baseline
[key
] = value
752 if self
._baseline
.get(key
, -1) == -1:
753 self
._baseline
[key
] = value
754 results
[field
] = (results
.get(field
, 0) + value
-
755 self
._baseline
.get(key
, 0))
759 def read_field(self
, field
, path
):
760 """Returns the value of a single field from a specific VM."""
762 return int(open(os
.path
.join(PATH_DEBUGFS_KVM
,
770 """Reset field counters"""
776 """Manages the data providers and the data they provide.
778 It is used to set filters on the provider's data and collect all
782 def __init__(self
, options
):
783 self
.providers
= self
.get_providers(options
)
784 self
._pid
_filter
= options
.pid
785 self
._fields
_filter
= options
.fields
789 def get_providers(options
):
790 """Returns a list of data providers depending on the passed options."""
794 providers
.append(DebugfsProvider(options
.pid
, options
.fields
))
795 if options
.tracepoints
or not providers
:
796 providers
.append(TracepointProvider(options
.pid
, options
.fields
))
800 def update_provider_filters(self
):
801 """Propagates fields filters to providers."""
802 # As we reset the counters when updating the fields we can
803 # also clear the cache of old values.
805 for provider
in self
.providers
:
806 provider
.update_fields(self
._fields
_filter
)
810 for provider
in self
.providers
:
814 def fields_filter(self
):
815 return self
._fields
_filter
817 @fields_filter.setter
818 def fields_filter(self
, fields_filter
):
819 if fields_filter
!= self
._fields
_filter
:
820 self
._fields
_filter
= fields_filter
821 self
.update_provider_filters()
824 def pid_filter(self
):
825 return self
._pid
_filter
828 def pid_filter(self
, pid
):
829 if pid
!= self
._pid
_filter
:
830 self
._pid
_filter
= pid
832 for provider
in self
.providers
:
833 provider
.pid
= self
._pid
_filter
836 """Returns a dict with field -> (value, delta to last value) of all
838 for provider
in self
.providers
:
839 new
= provider
.read()
840 for key
in provider
.fields
:
841 oldval
= self
.values
.get(key
, (0, 0))[0]
842 newval
= new
.get(key
, 0)
843 newdelta
= newval
- oldval
844 self
.values
[key
] = (newval
, newdelta
)
849 MAX_GUEST_NAME_LEN
= 48
851 DEFAULT_REGEX
= r
'^[^\(]*$'
855 """Instruments curses to draw a nice text ui."""
856 def __init__(self
, stats
):
861 """Initialises curses for later use. Based on curses.wrapper
862 implementation from the Python standard library."""
863 self
.screen
= curses
.initscr()
867 # The try/catch works around a minor bit of
868 # over-conscientiousness in the curses module, the error
869 # return from C start_color() is ignorable.
875 # Hide cursor in extra statement as some monochrome terminals
876 # might support hiding but not colors.
882 curses
.use_default_colors()
885 def __exit__(self
, *exception
):
886 """Resets the terminal to its normal state. Based on curses.wrapper
887 implementation from the Python standard library."""
889 self
.screen
.keypad(0)
895 def get_pid_from_gname(gname
):
896 """Fuzzy function to convert guest name to QEMU process pid.
898 Returns a list of potential pids, can be empty if no match found.
899 Throws an exception on processing errors.
904 child
= subprocess
.Popen(['ps', '-A', '--format', 'pid,args'],
905 stdout
=subprocess
.PIPE
)
908 for line
in child
.stdout
:
909 line
= line
.lstrip().split(' ', 1)
910 # perform a sanity check before calling the more expensive
911 # function to possibly extract the guest name
912 if (' -name ' in line
[1] and
913 gname
== self
.get_gname_from_pid(line
[0])):
914 pids
.append(int(line
[0]))
920 def get_gname_from_pid(pid
):
921 """Returns the guest name for a QEMU process pid.
923 Extracts the guest name from the QEMU comma line by processing the
924 '-name' option. Will also handle names specified out of sequence.
929 line
= open('/proc/{}/cmdline'
930 .format(pid
), 'rb').read().split('\0')
931 parms
= line
[line
.index('-name') + 1].split(',')
933 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
934 # in # ['foo', '', 'bar'], which we revert here
935 idx
= parms
.index('')
936 parms
[idx
- 1] += ',' + parms
[idx
+ 1]
938 # the '-name' switch allows for two ways to specify the guest name,
939 # where the plain name overrides the name specified via 'guest='
944 if arg
[:6] == 'guest=':
946 except (ValueError, IOError, IndexError):
951 def update_drilldown(self
):
952 """Sets or removes a filter that only allows fields without braces."""
953 if not self
.stats
.fields_filter
:
954 self
.stats
.fields_filter
= DEFAULT_REGEX
956 elif self
.stats
.fields_filter
== DEFAULT_REGEX
:
957 self
.stats
.fields_filter
= None
959 def update_pid(self
, pid
):
960 """Propagates pid selection to stats object."""
961 self
.stats
.pid_filter
= pid
963 def refresh_header(self
, pid
=None):
964 """Refreshes the header."""
966 pid
= self
.stats
.pid_filter
968 gname
= self
.get_gname_from_pid(pid
)
970 gname
= ('({})'.format(gname
[:MAX_GUEST_NAME_LEN
] + '...'
971 if len(gname
) > MAX_GUEST_NAME_LEN
974 self
.screen
.addstr(0, 0, 'kvm statistics - pid {0} {1}'
975 .format(pid
, gname
), curses
.A_BOLD
)
977 self
.screen
.addstr(0, 0, 'kvm statistics - summary', curses
.A_BOLD
)
978 if self
.stats
.fields_filter
and self
.stats
.fields_filter \
980 regex
= self
.stats
.fields_filter
981 if len(regex
) > MAX_REGEX_LEN
:
982 regex
= regex
[:MAX_REGEX_LEN
] + '...'
983 self
.screen
.addstr(1, 17, 'regex filter: {0}'.format(regex
))
984 self
.screen
.addstr(2, 1, '%-40s %10s%7s %8s' %
985 ('Event', 'Total', '%Total', 'CurAvg/s'),
987 self
.screen
.addstr(4, 1, 'Collecting data...')
988 self
.screen
.refresh()
990 def refresh_body(self
, sleeptime
):
992 self
.screen
.move(row
, 0)
993 self
.screen
.clrtobot()
994 stats
= self
.stats
.get()
998 return (-stats
[x
][1], -stats
[x
][0])
1000 return (0, -stats
[x
][0])
1002 for val
in stats
.values():
1004 for key
in sorted(stats
.keys(), key
=sortkey
):
1006 if row
>= self
.screen
.getmaxyx()[0]:
1009 if not values
[0] and not values
[1]:
1011 if values
[0] is not None:
1012 cur
= int(round(values
[1] / sleeptime
)) if values
[1] else ''
1013 self
.screen
.addstr(row
, 1, '%-40s %10d%7.1f %8s' %
1014 (key
, values
[0], values
[0] * 100 / total
,
1018 self
.screen
.addstr(4, 1, 'No matching events reported yet')
1019 self
.screen
.refresh()
1021 def show_filter_selection(self
):
1022 """Draws filter selection mask.
1024 Asks for a valid regex and sets the fields filter accordingly.
1029 self
.screen
.addstr(0, 0,
1030 "Show statistics for events matching a regex.",
1032 self
.screen
.addstr(2, 0,
1033 "Current regex: {0}"
1034 .format(self
.stats
.fields_filter
))
1035 self
.screen
.addstr(3, 0, "New regex: ")
1037 regex
= self
.screen
.getstr()
1040 self
.stats
.fields_filter
= DEFAULT_REGEX
1041 self
.refresh_header()
1045 self
.stats
.fields_filter
= regex
1046 self
.refresh_header()
1051 def show_vm_selection_by_pid(self
):
1052 """Draws PID selection mask.
1054 Asks for a pid until a valid pid or 0 has been entered.
1060 self
.screen
.addstr(0, 0,
1061 'Show statistics for specific pid.',
1063 self
.screen
.addstr(1, 0,
1064 'This might limit the shown data to the trace '
1066 self
.screen
.addstr(5, 0, msg
)
1069 self
.screen
.addstr(3, 0, "Pid [0 or pid]: ")
1070 pid
= self
.screen
.getstr()
1076 if pid
!= 0 and not os
.path
.isdir(os
.path
.join('/proc/',
1078 msg
= '"' + str(pid
) + '": Not a running process'
1082 self
.refresh_header(pid
)
1083 self
.update_pid(pid
)
1087 msg
= '"' + str(pid
) + '": Not a valid pid'
1089 def show_vm_selection_by_guest_name(self
):
1090 """Draws guest selection mask.
1092 Asks for a guest name until a valid guest name or '' is entered.
1098 self
.screen
.addstr(0, 0,
1099 'Show statistics for specific guest.',
1101 self
.screen
.addstr(1, 0,
1102 'This might limit the shown data to the trace '
1104 self
.screen
.addstr(5, 0, msg
)
1106 self
.screen
.addstr(3, 0, "Guest [ENTER or guest]: ")
1107 gname
= self
.screen
.getstr()
1111 self
.refresh_header(0)
1117 pids
= self
.get_pid_from_gname(gname
)
1119 msg
= '"' + gname
+ '": Internal error while searching, ' \
1120 'use pid filter instead'
1123 msg
= '"' + gname
+ '": Not an active guest'
1126 msg
= '"' + gname
+ '": Multiple matches found, use pid ' \
1129 self
.refresh_header(pids
[0])
1130 self
.update_pid(pids
[0])
1133 def show_stats(self
):
1134 """Refreshes the screen and processes user input."""
1135 sleeptime
= DELAY_INITIAL
1136 self
.refresh_header()
1137 start
= 0.0 # result based on init value never appears on screen
1139 self
.refresh_body(time
.time() - start
)
1140 curses
.halfdelay(int(sleeptime
* 10))
1142 sleeptime
= DELAY_REGULAR
1144 char
= self
.screen
.getkey()
1146 self
.update_drilldown()
1150 self
.stats
.fields_filter
= DEFAULT_REGEX
1151 self
.refresh_header(0)
1155 self
.show_filter_selection()
1157 sleeptime
= DELAY_INITIAL
1160 self
.show_vm_selection_by_guest_name()
1162 sleeptime
= DELAY_INITIAL
1165 self
.show_vm_selection_by_pid()
1167 sleeptime
= DELAY_INITIAL
1170 except KeyboardInterrupt:
1172 except curses
.error
:
1177 """Prints statistics in a key, value format."""
1182 for key
in sorted(s
.keys()):
1184 print '%-42s%10d%10d' % (key
, values
[0], values
[1])
1185 except KeyboardInterrupt:
1190 """Prints statistics as reiterating key block, multiple value blocks."""
1191 keys
= sorted(stats
.get().iterkeys())
1201 print ' %9d' % s
[k
][1],
1208 if line
% banner_repeat
== 0:
1212 except KeyboardInterrupt:
1217 """Returns processed program arguments."""
1218 description_text
= """
1219 This script displays various statistics about VMs running under KVM.
1220 The statistics are gathered from the KVM debugfs entries and / or the
1221 currently available perf traces.
1223 The monitoring takes additional cpu cycles and might affect the VM's
1228 /sys/kernel/debug/kvm
1229 /sys/kernel/debug/trace/events/*
1231 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1232 CAP_SYS_ADMIN and perf events are used.
1233 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1234 the large number of files that are possibly opened.
1236 Interactive Commands:
1238 f filter by regular expression
1239 g filter by guest name
1242 x toggle reporting of stats for individual child trace events
1244 Press any other key to refresh statistics immediately.
1247 class PlainHelpFormatter(optparse
.IndentedHelpFormatter
):
1248 def format_description(self
, description
):
1250 return description
+ "\n"
1254 def cb_guest_to_pid(option
, opt
, val
, parser
):
1256 pids
= Tui
.get_pid_from_gname(val
)
1258 raise optparse
.OptionValueError('Error while searching for guest '
1259 '"{}", use "-p" to specify a pid '
1260 'instead'.format(val
))
1262 raise optparse
.OptionValueError('No guest by the name "{}" '
1263 'found'.format(val
))
1265 raise optparse
.OptionValueError('Multiple processes found (pids: '
1266 '{}) - use "-p" to specify a pid '
1267 'instead'.format(" ".join(pids
)))
1268 parser
.values
.pid
= pids
[0]
1270 optparser
= optparse
.OptionParser(description
=description_text
,
1271 formatter
=PlainHelpFormatter())
1272 optparser
.add_option('-1', '--once', '--batch',
1273 action
='store_true',
1276 help='run in batch mode for one second',
1278 optparser
.add_option('-l', '--log',
1279 action
='store_true',
1282 help='run in logging mode (like vmstat)',
1284 optparser
.add_option('-t', '--tracepoints',
1285 action
='store_true',
1288 help='retrieve statistics from tracepoints',
1290 optparser
.add_option('-d', '--debugfs',
1291 action
='store_true',
1294 help='retrieve statistics from debugfs',
1296 optparser
.add_option('-f', '--fields',
1298 default
=DEFAULT_REGEX
,
1300 help='fields to display (regex)',
1302 optparser
.add_option('-p', '--pid',
1307 help='restrict statistics to pid',
1309 optparser
.add_option('-g', '--guest',
1314 help='restrict statistics to guest by name',
1315 callback
=cb_guest_to_pid
,
1317 (options
, _
) = optparser
.parse_args(sys
.argv
)
1321 def check_access(options
):
1322 """Exits if the current user can't access all needed directories."""
1323 if not os
.path
.exists('/sys/kernel/debug'):
1324 sys
.stderr
.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1327 if not os
.path
.exists(PATH_DEBUGFS_KVM
):
1328 sys
.stderr
.write("Please make sure, that debugfs is mounted and "
1329 "readable by the current user:\n"
1330 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1331 "Also ensure, that the kvm modules are loaded.\n")
1334 if not os
.path
.exists(PATH_DEBUGFS_TRACING
) and (options
.tracepoints
or
1335 not options
.debugfs
):
1336 sys
.stderr
.write("Please enable CONFIG_TRACING in your kernel "
1337 "when using the option -t (default).\n"
1338 "If it is enabled, make {0} readable by the "
1340 .format(PATH_DEBUGFS_TRACING
))
1341 if options
.tracepoints
:
1344 sys
.stderr
.write("Falling back to debugfs statistics!\n")
1345 options
.debugfs
= True
1352 options
= get_options()
1353 options
= check_access(options
)
1355 if (options
.pid
> 0 and
1356 not os
.path
.isdir(os
.path
.join('/proc/',
1357 str(options
.pid
)))):
1358 sys
.stderr
.write('Did you use a (unsupported) tid instead of a pid?\n')
1359 sys
.exit('Specified pid does not exist.')
1361 stats
= Stats(options
)
1365 elif not options
.once
:
1366 with
Tui(stats
) as tui
:
1371 if __name__
== "__main__":