aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-02-26 12:28:35 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-26 12:28:35 -0500
commitd4858aaf6bd8a90e2dacc0dfec2077e334dcedbf (patch)
treeccb1934d3943fd7ed443f533409d02f4e2a5ad05 /tools
parent4a3928c6f8a53fa1aed28ccba227742486e8ddcb (diff)
parent9c5e0afaf15788bcbd1c3469da701ac3da826886 (diff)
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM fixes from Paolo Bonzini: "s390: - optimization for the exitless interrupt support that was merged in 4.16-rc1 - improve the branch prediction blocking for nested KVM - replace some jump tables with switch statements to improve expoline performance - fixes for multiple epoch facility ARM: - fix the interaction of userspace irqchip VMs with in-kernel irqchip VMs - make sure we can build 32-bit KVM/ARM with gcc-8. x86: - fixes for AMD SEV - fixes for Intel nested VMX, emulated UMIP and a dump_stack() on VM startup - fixes for async page fault migration - small optimization to PV TLB flush (new in 4.16-rc1) - syzkaller fixes Generic: - compiler warning fixes - syzkaller fixes - more improvements to the kvm_stat tool Two more small Spectre fixes are going to reach you via Ingo" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (40 commits) KVM: SVM: Fix SEV LAUNCH_SECRET command KVM: SVM: install RSM intercept KVM: SVM: no need to call access_ok() in LAUNCH_MEASURE command include: psp-sev: Capitalize invalid length enum crypto: ccp: Fix sparse, use plain integer as NULL pointer KVM: X86: Avoid traversing all the cpus for pv tlb flush when steal time is disabled x86/kvm: Make parse_no_xxx __init for kvm KVM: x86: fix backward migration with async_PF kvm: fix warning for non-x86 builds kvm: fix warning for CONFIG_HAVE_KVM_EVENTFD builds tools/kvm_stat: print 'Total' line for multiple events only tools/kvm_stat: group child events indented after parent tools/kvm_stat: separate drilldown and fields filtering tools/kvm_stat: eliminate extra guest/pid selection dialog tools/kvm_stat: mark private methods as such tools/kvm_stat: fix debugfs handling tools/kvm_stat: print error on invalid regex tools/kvm_stat: fix crash when filtering out all non-child trace events tools/kvm_stat: avoid 'is' for equality checks tools/kvm_stat: use a more pythonic way to iterate over dictionaries ...
Diffstat (limited to 'tools')
-rwxr-xr-xtools/kvm/kvm_stat/kvm_stat503
-rw-r--r--tools/kvm/kvm_stat/kvm_stat.txt4
2 files changed, 287 insertions, 220 deletions
diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat
index a5684d0968b4..5898c22ba310 100755
--- a/tools/kvm/kvm_stat/kvm_stat
+++ b/tools/kvm/kvm_stat/kvm_stat
@@ -33,7 +33,7 @@ import resource
33import struct 33import struct
34import re 34import re
35import subprocess 35import subprocess
36from collections import defaultdict 36from collections import defaultdict, namedtuple
37 37
38VMX_EXIT_REASONS = { 38VMX_EXIT_REASONS = {
39 'EXCEPTION_NMI': 0, 39 'EXCEPTION_NMI': 0,
@@ -228,6 +228,7 @@ IOCTL_NUMBERS = {
228} 228}
229 229
230ENCODING = locale.getpreferredencoding(False) 230ENCODING = locale.getpreferredencoding(False)
231TRACE_FILTER = re.compile(r'^[^\(]*$')
231 232
232 233
233class Arch(object): 234class Arch(object):
@@ -260,6 +261,11 @@ class Arch(object):
260 return ArchX86(SVM_EXIT_REASONS) 261 return ArchX86(SVM_EXIT_REASONS)
261 return 262 return
262 263
264 def tracepoint_is_child(self, field):
265 if (TRACE_FILTER.match(field)):
266 return None
267 return field.split('(', 1)[0]
268
263 269
264class ArchX86(Arch): 270class ArchX86(Arch):
265 def __init__(self, exit_reasons): 271 def __init__(self, exit_reasons):
@@ -267,6 +273,10 @@ class ArchX86(Arch):
267 self.ioctl_numbers = IOCTL_NUMBERS 273 self.ioctl_numbers = IOCTL_NUMBERS
268 self.exit_reasons = exit_reasons 274 self.exit_reasons = exit_reasons
269 275
276 def debugfs_is_child(self, field):
277 """ Returns name of parent if 'field' is a child, None otherwise """
278 return None
279
270 280
271class ArchPPC(Arch): 281class ArchPPC(Arch):
272 def __init__(self): 282 def __init__(self):
@@ -282,6 +292,10 @@ class ArchPPC(Arch):
282 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 292 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
283 self.exit_reasons = {} 293 self.exit_reasons = {}
284 294
295 def debugfs_is_child(self, field):
296 """ Returns name of parent if 'field' is a child, None otherwise """
297 return None
298
285 299
286class ArchA64(Arch): 300class ArchA64(Arch):
287 def __init__(self): 301 def __init__(self):
@@ -289,6 +303,10 @@ class ArchA64(Arch):
289 self.ioctl_numbers = IOCTL_NUMBERS 303 self.ioctl_numbers = IOCTL_NUMBERS
290 self.exit_reasons = AARCH64_EXIT_REASONS 304 self.exit_reasons = AARCH64_EXIT_REASONS
291 305
306 def debugfs_is_child(self, field):
307 """ Returns name of parent if 'field' is a child, None otherwise """
308 return None
309
292 310
293class ArchS390(Arch): 311class ArchS390(Arch):
294 def __init__(self): 312 def __init__(self):
@@ -296,6 +314,12 @@ class ArchS390(Arch):
296 self.ioctl_numbers = IOCTL_NUMBERS 314 self.ioctl_numbers = IOCTL_NUMBERS
297 self.exit_reasons = None 315 self.exit_reasons = None
298 316
317 def debugfs_is_child(self, field):
318 """ Returns name of parent if 'field' is a child, None otherwise """
319 if field.startswith('instruction_'):
320 return 'exit_instruction'
321
322
299ARCH = Arch.get_arch() 323ARCH = Arch.get_arch()
300 324
301 325
@@ -331,9 +355,6 @@ class perf_event_attr(ctypes.Structure):
331PERF_TYPE_TRACEPOINT = 2 355PERF_TYPE_TRACEPOINT = 2
332PERF_FORMAT_GROUP = 1 << 3 356PERF_FORMAT_GROUP = 1 << 3
333 357
334PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
335PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
336
337 358
338class Group(object): 359class Group(object):
339 """Represents a perf event group.""" 360 """Represents a perf event group."""
@@ -376,8 +397,8 @@ class Event(object):
376 self.syscall = self.libc.syscall 397 self.syscall = self.libc.syscall
377 self.name = name 398 self.name = name
378 self.fd = None 399 self.fd = None
379 self.setup_event(group, trace_cpu, trace_pid, trace_point, 400 self._setup_event(group, trace_cpu, trace_pid, trace_point,
380 trace_filter, trace_set) 401 trace_filter, trace_set)
381 402
382 def __del__(self): 403 def __del__(self):
383 """Closes the event's file descriptor. 404 """Closes the event's file descriptor.
@@ -390,7 +411,7 @@ class Event(object):
390 if self.fd: 411 if self.fd:
391 os.close(self.fd) 412 os.close(self.fd)
392 413
393 def perf_event_open(self, attr, pid, cpu, group_fd, flags): 414 def _perf_event_open(self, attr, pid, cpu, group_fd, flags):
394 """Wrapper for the sys_perf_evt_open() syscall. 415 """Wrapper for the sys_perf_evt_open() syscall.
395 416
396 Used to set up performance events, returns a file descriptor or -1 417 Used to set up performance events, returns a file descriptor or -1
@@ -409,7 +430,7 @@ class Event(object):
409 ctypes.c_int(pid), ctypes.c_int(cpu), 430 ctypes.c_int(pid), ctypes.c_int(cpu),
410 ctypes.c_int(group_fd), ctypes.c_long(flags)) 431 ctypes.c_int(group_fd), ctypes.c_long(flags))
411 432
412 def setup_event_attribute(self, trace_set, trace_point): 433 def _setup_event_attribute(self, trace_set, trace_point):
413 """Returns an initialized ctype perf_event_attr struct.""" 434 """Returns an initialized ctype perf_event_attr struct."""
414 435
415 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, 436 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
@@ -419,8 +440,8 @@ class Event(object):
419 event_attr.config = int(open(id_path).read()) 440 event_attr.config = int(open(id_path).read())
420 return event_attr 441 return event_attr
421 442
422 def setup_event(self, group, trace_cpu, trace_pid, trace_point, 443 def _setup_event(self, group, trace_cpu, trace_pid, trace_point,
423 trace_filter, trace_set): 444 trace_filter, trace_set):
424 """Sets up the perf event in Linux. 445 """Sets up the perf event in Linux.
425 446
426 Issues the syscall to register the event in the kernel and 447 Issues the syscall to register the event in the kernel and
@@ -428,7 +449,7 @@ class Event(object):
428 449
429 """ 450 """
430 451
431 event_attr = self.setup_event_attribute(trace_set, trace_point) 452 event_attr = self._setup_event_attribute(trace_set, trace_point)
432 453
433 # First event will be group leader. 454 # First event will be group leader.
434 group_leader = -1 455 group_leader = -1
@@ -437,8 +458,8 @@ class Event(object):
437 if group.events: 458 if group.events:
438 group_leader = group.events[0].fd 459 group_leader = group.events[0].fd
439 460
440 fd = self.perf_event_open(event_attr, trace_pid, 461 fd = self._perf_event_open(event_attr, trace_pid,
441 trace_cpu, group_leader, 0) 462 trace_cpu, group_leader, 0)
442 if fd == -1: 463 if fd == -1:
443 err = ctypes.get_errno() 464 err = ctypes.get_errno()
444 raise OSError(err, os.strerror(err), 465 raise OSError(err, os.strerror(err),
@@ -475,6 +496,10 @@ class Event(object):
475 496
476class Provider(object): 497class Provider(object):
477 """Encapsulates functionalities used by all providers.""" 498 """Encapsulates functionalities used by all providers."""
499 def __init__(self, pid):
500 self.child_events = False
501 self.pid = pid
502
478 @staticmethod 503 @staticmethod
479 def is_field_wanted(fields_filter, field): 504 def is_field_wanted(fields_filter, field):
480 """Indicate whether field is valid according to fields_filter.""" 505 """Indicate whether field is valid according to fields_filter."""
@@ -500,12 +525,12 @@ class TracepointProvider(Provider):
500 """ 525 """
501 def __init__(self, pid, fields_filter): 526 def __init__(self, pid, fields_filter):
502 self.group_leaders = [] 527 self.group_leaders = []
503 self.filters = self.get_filters() 528 self.filters = self._get_filters()
504 self.update_fields(fields_filter) 529 self.update_fields(fields_filter)
505 self.pid = pid 530 super(TracepointProvider, self).__init__(pid)
506 531
507 @staticmethod 532 @staticmethod
508 def get_filters(): 533 def _get_filters():
509 """Returns a dict of trace events, their filter ids and 534 """Returns a dict of trace events, their filter ids and
510 the values that can be filtered. 535 the values that can be filtered.
511 536
@@ -521,8 +546,8 @@ class TracepointProvider(Provider):
521 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) 546 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
522 return filters 547 return filters
523 548
524 def get_available_fields(self): 549 def _get_available_fields(self):
525 """Returns a list of available event's of format 'event name(filter 550 """Returns a list of available events of format 'event name(filter
526 name)'. 551 name)'.
527 552
528 All available events have directories under 553 All available events have directories under
@@ -549,11 +574,12 @@ class TracepointProvider(Provider):
549 574
550 def update_fields(self, fields_filter): 575 def update_fields(self, fields_filter):
551 """Refresh fields, applying fields_filter""" 576 """Refresh fields, applying fields_filter"""
552 self.fields = [field for field in self.get_available_fields() 577 self.fields = [field for field in self._get_available_fields()
553 if self.is_field_wanted(fields_filter, field)] 578 if self.is_field_wanted(fields_filter, field) or
579 ARCH.tracepoint_is_child(field)]
554 580
555 @staticmethod 581 @staticmethod
556 def get_online_cpus(): 582 def _get_online_cpus():
557 """Returns a list of cpu id integers.""" 583 """Returns a list of cpu id integers."""
558 def parse_int_list(list_string): 584 def parse_int_list(list_string):
559 """Returns an int list from a string of comma separated integers and 585 """Returns an int list from a string of comma separated integers and
@@ -575,17 +601,17 @@ class TracepointProvider(Provider):
575 cpu_string = cpu_list.readline() 601 cpu_string = cpu_list.readline()
576 return parse_int_list(cpu_string) 602 return parse_int_list(cpu_string)
577 603
578 def setup_traces(self): 604 def _setup_traces(self):
579 """Creates all event and group objects needed to be able to retrieve 605 """Creates all event and group objects needed to be able to retrieve
580 data.""" 606 data."""
581 fields = self.get_available_fields() 607 fields = self._get_available_fields()
582 if self._pid > 0: 608 if self._pid > 0:
583 # Fetch list of all threads of the monitored pid, as qemu 609 # Fetch list of all threads of the monitored pid, as qemu
584 # starts a thread for each vcpu. 610 # starts a thread for each vcpu.
585 path = os.path.join('/proc', str(self._pid), 'task') 611 path = os.path.join('/proc', str(self._pid), 'task')
586 groupids = self.walkdir(path)[1] 612 groupids = self.walkdir(path)[1]
587 else: 613 else:
588 groupids = self.get_online_cpus() 614 groupids = self._get_online_cpus()
589 615
590 # The constant is needed as a buffer for python libs, std 616 # The constant is needed as a buffer for python libs, std
591 # streams and other files that the script opens. 617 # streams and other files that the script opens.
@@ -663,7 +689,7 @@ class TracepointProvider(Provider):
663 # The garbage collector will get rid of all Event/Group 689 # The garbage collector will get rid of all Event/Group
664 # objects and open files after removing the references. 690 # objects and open files after removing the references.
665 self.group_leaders = [] 691 self.group_leaders = []
666 self.setup_traces() 692 self._setup_traces()
667 self.fields = self._fields 693 self.fields = self._fields
668 694
669 def read(self, by_guest=0): 695 def read(self, by_guest=0):
@@ -671,8 +697,12 @@ class TracepointProvider(Provider):
671 ret = defaultdict(int) 697 ret = defaultdict(int)
672 for group in self.group_leaders: 698 for group in self.group_leaders:
673 for name, val in group.read().items(): 699 for name, val in group.read().items():
674 if name in self._fields: 700 if name not in self._fields:
675 ret[name] += val 701 continue
702 parent = ARCH.tracepoint_is_child(name)
703 if parent:
704 name += ' ' + parent
705 ret[name] += val
676 return ret 706 return ret
677 707
678 def reset(self): 708 def reset(self):
@@ -690,11 +720,11 @@ class DebugfsProvider(Provider):
690 self._baseline = {} 720 self._baseline = {}
691 self.do_read = True 721 self.do_read = True
692 self.paths = [] 722 self.paths = []
693 self.pid = pid 723 super(DebugfsProvider, self).__init__(pid)
694 if include_past: 724 if include_past:
695 self.restore() 725 self._restore()
696 726
697 def get_available_fields(self): 727 def _get_available_fields(self):
698 """"Returns a list of available fields. 728 """"Returns a list of available fields.
699 729
700 The fields are all available KVM debugfs files 730 The fields are all available KVM debugfs files
@@ -704,8 +734,9 @@ class DebugfsProvider(Provider):
704 734
705 def update_fields(self, fields_filter): 735 def update_fields(self, fields_filter):
706 """Refresh fields, applying fields_filter""" 736 """Refresh fields, applying fields_filter"""
707 self._fields = [field for field in self.get_available_fields() 737 self._fields = [field for field in self._get_available_fields()
708 if self.is_field_wanted(fields_filter, field)] 738 if self.is_field_wanted(fields_filter, field) or
739 ARCH.debugfs_is_child(field)]
709 740
710 @property 741 @property
711 def fields(self): 742 def fields(self):
@@ -758,7 +789,7 @@ class DebugfsProvider(Provider):
758 paths.append(dir) 789 paths.append(dir)
759 for path in paths: 790 for path in paths:
760 for field in self._fields: 791 for field in self._fields:
761 value = self.read_field(field, path) 792 value = self._read_field(field, path)
762 key = path + field 793 key = path + field
763 if reset == 1: 794 if reset == 1:
764 self._baseline[key] = value 795 self._baseline[key] = value
@@ -766,20 +797,21 @@ class DebugfsProvider(Provider):
766 self._baseline[key] = 0 797 self._baseline[key] = 0
767 if self._baseline.get(key, -1) == -1: 798 if self._baseline.get(key, -1) == -1:
768 self._baseline[key] = value 799 self._baseline[key] = value
769 increment = (results.get(field, 0) + value - 800 parent = ARCH.debugfs_is_child(field)
770 self._baseline.get(key, 0)) 801 if parent:
771 if by_guest: 802 field = field + ' ' + parent
772 pid = key.split('-')[0] 803 else:
773 if pid in results: 804 if by_guest:
774 results[pid] += increment 805 field = key.split('-')[0] # set 'field' to 'pid'
775 else: 806 increment = value - self._baseline.get(key, 0)
776 results[pid] = increment 807 if field in results:
808 results[field] += increment
777 else: 809 else:
778 results[field] = increment 810 results[field] = increment
779 811
780 return results 812 return results
781 813
782 def read_field(self, field, path): 814 def _read_field(self, field, path):
783 """Returns the value of a single field from a specific VM.""" 815 """Returns the value of a single field from a specific VM."""
784 try: 816 try:
785 return int(open(os.path.join(PATH_DEBUGFS_KVM, 817 return int(open(os.path.join(PATH_DEBUGFS_KVM,
@@ -794,12 +826,15 @@ class DebugfsProvider(Provider):
794 self._baseline = {} 826 self._baseline = {}
795 self.read(1) 827 self.read(1)
796 828
797 def restore(self): 829 def _restore(self):
798 """Reset field counters""" 830 """Reset field counters"""
799 self._baseline = {} 831 self._baseline = {}
800 self.read(2) 832 self.read(2)
801 833
802 834
835EventStat = namedtuple('EventStat', ['value', 'delta'])
836
837
803class Stats(object): 838class Stats(object):
804 """Manages the data providers and the data they provide. 839 """Manages the data providers and the data they provide.
805 840
@@ -808,13 +843,13 @@ class Stats(object):
808 843
809 """ 844 """
810 def __init__(self, options): 845 def __init__(self, options):
811 self.providers = self.get_providers(options) 846 self.providers = self._get_providers(options)
812 self._pid_filter = options.pid 847 self._pid_filter = options.pid
813 self._fields_filter = options.fields 848 self._fields_filter = options.fields
814 self.values = {} 849 self.values = {}
850 self._child_events = False
815 851
816 @staticmethod 852 def _get_providers(self, options):
817 def get_providers(options):
818 """Returns a list of data providers depending on the passed options.""" 853 """Returns a list of data providers depending on the passed options."""
819 providers = [] 854 providers = []
820 855
@@ -826,7 +861,7 @@ class Stats(object):
826 861
827 return providers 862 return providers
828 863
829 def update_provider_filters(self): 864 def _update_provider_filters(self):
830 """Propagates fields filters to providers.""" 865 """Propagates fields filters to providers."""
831 # As we reset the counters when updating the fields we can 866 # As we reset the counters when updating the fields we can
832 # also clear the cache of old values. 867 # also clear the cache of old values.
@@ -847,7 +882,7 @@ class Stats(object):
847 def fields_filter(self, fields_filter): 882 def fields_filter(self, fields_filter):
848 if fields_filter != self._fields_filter: 883 if fields_filter != self._fields_filter:
849 self._fields_filter = fields_filter 884 self._fields_filter = fields_filter
850 self.update_provider_filters() 885 self._update_provider_filters()
851 886
852 @property 887 @property
853 def pid_filter(self): 888 def pid_filter(self):
@@ -861,16 +896,33 @@ class Stats(object):
861 for provider in self.providers: 896 for provider in self.providers:
862 provider.pid = self._pid_filter 897 provider.pid = self._pid_filter
863 898
899 @property
900 def child_events(self):
901 return self._child_events
902
903 @child_events.setter
904 def child_events(self, val):
905 self._child_events = val
906 for provider in self.providers:
907 provider.child_events = val
908
864 def get(self, by_guest=0): 909 def get(self, by_guest=0):
865 """Returns a dict with field -> (value, delta to last value) of all 910 """Returns a dict with field -> (value, delta to last value) of all
866 provider data.""" 911 provider data.
912 Key formats:
913 * plain: 'key' is event name
914 * child-parent: 'key' is in format '<child> <parent>'
915 * pid: 'key' is the pid of the guest, and the record contains the
916 aggregated event data
917 These formats are generated by the providers, and handled in class TUI.
918 """
867 for provider in self.providers: 919 for provider in self.providers:
868 new = provider.read(by_guest=by_guest) 920 new = provider.read(by_guest=by_guest)
869 for key in new if by_guest else provider.fields: 921 for key in new:
870 oldval = self.values.get(key, (0, 0))[0] 922 oldval = self.values.get(key, EventStat(0, 0)).value
871 newval = new.get(key, 0) 923 newval = new.get(key, 0)
872 newdelta = newval - oldval 924 newdelta = newval - oldval
873 self.values[key] = (newval, newdelta) 925 self.values[key] = EventStat(newval, newdelta)
874 return self.values 926 return self.values
875 927
876 def toggle_display_guests(self, to_pid): 928 def toggle_display_guests(self, to_pid):
@@ -899,10 +951,10 @@ class Stats(object):
899 self.get(to_pid) 951 self.get(to_pid)
900 return 0 952 return 0
901 953
954
902DELAY_DEFAULT = 3.0 955DELAY_DEFAULT = 3.0
903MAX_GUEST_NAME_LEN = 48 956MAX_GUEST_NAME_LEN = 48
904MAX_REGEX_LEN = 44 957MAX_REGEX_LEN = 44
905DEFAULT_REGEX = r'^[^\(]*$'
906SORT_DEFAULT = 0 958SORT_DEFAULT = 0
907 959
908 960
@@ -969,7 +1021,7 @@ class Tui(object):
969 1021
970 return res 1022 return res
971 1023
972 def print_all_gnames(self, row): 1024 def _print_all_gnames(self, row):
973 """Print a list of all running guests along with their pids.""" 1025 """Print a list of all running guests along with their pids."""
974 self.screen.addstr(row, 2, '%8s %-60s' % 1026 self.screen.addstr(row, 2, '%8s %-60s' %
975 ('Pid', 'Guest Name (fuzzy list, might be ' 1027 ('Pid', 'Guest Name (fuzzy list, might be '
@@ -1032,19 +1084,13 @@ class Tui(object):
1032 1084
1033 return name 1085 return name
1034 1086
1035 def update_drilldown(self): 1087 def _update_pid(self, pid):
1036 """Sets or removes a filter that only allows fields without braces."""
1037 if not self.stats.fields_filter:
1038 self.stats.fields_filter = DEFAULT_REGEX
1039
1040 elif self.stats.fields_filter == DEFAULT_REGEX:
1041 self.stats.fields_filter = None
1042
1043 def update_pid(self, pid):
1044 """Propagates pid selection to stats object.""" 1088 """Propagates pid selection to stats object."""
1089 self.screen.addstr(4, 1, 'Updating pid filter...')
1090 self.screen.refresh()
1045 self.stats.pid_filter = pid 1091 self.stats.pid_filter = pid
1046 1092
1047 def refresh_header(self, pid=None): 1093 def _refresh_header(self, pid=None):
1048 """Refreshes the header.""" 1094 """Refreshes the header."""
1049 if pid is None: 1095 if pid is None:
1050 pid = self.stats.pid_filter 1096 pid = self.stats.pid_filter
@@ -1059,8 +1105,7 @@ class Tui(object):
1059 .format(pid, gname), curses.A_BOLD) 1105 .format(pid, gname), curses.A_BOLD)
1060 else: 1106 else:
1061 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD) 1107 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1062 if self.stats.fields_filter and self.stats.fields_filter \ 1108 if self.stats.fields_filter:
1063 != DEFAULT_REGEX:
1064 regex = self.stats.fields_filter 1109 regex = self.stats.fields_filter
1065 if len(regex) > MAX_REGEX_LEN: 1110 if len(regex) > MAX_REGEX_LEN:
1066 regex = regex[:MAX_REGEX_LEN] + '...' 1111 regex = regex[:MAX_REGEX_LEN] + '...'
@@ -1075,56 +1120,99 @@ class Tui(object):
1075 self.screen.addstr(4, 1, 'Collecting data...') 1120 self.screen.addstr(4, 1, 'Collecting data...')
1076 self.screen.refresh() 1121 self.screen.refresh()
1077 1122
1078 def refresh_body(self, sleeptime): 1123 def _refresh_body(self, sleeptime):
1124 def is_child_field(field):
1125 return field.find('(') != -1
1126
1127 def insert_child(sorted_items, child, values, parent):
1128 num = len(sorted_items)
1129 for i in range(0, num):
1130 # only add child if parent is present
1131 if parent.startswith(sorted_items[i][0]):
1132 sorted_items.insert(i + 1, (' ' + child, values))
1133
1134 def get_sorted_events(self, stats):
1135 """ separate parent and child events """
1136 if self._sorting == SORT_DEFAULT:
1137 def sortkey((_k, v)):
1138 # sort by (delta value, overall value)
1139 return (v.delta, v.value)
1140 else:
1141 def sortkey((_k, v)):
1142 # sort by overall value
1143 return v.value
1144
1145 childs = []
1146 sorted_items = []
1147 # we can't rule out child events to appear prior to parents even
1148 # when sorted - separate out all children first, and add in later
1149 for key, values in sorted(stats.items(), key=sortkey,
1150 reverse=True):
1151 if values == (0, 0):
1152 continue
1153 if key.find(' ') != -1:
1154 if not self.stats.child_events:
1155 continue
1156 childs.insert(0, (key, values))
1157 else:
1158 sorted_items.append((key, values))
1159 if self.stats.child_events:
1160 for key, values in childs:
1161 (child, parent) = key.split(' ')
1162 insert_child(sorted_items, child, values, parent)
1163
1164 return sorted_items
1165
1079 row = 3 1166 row = 3
1080 self.screen.move(row, 0) 1167 self.screen.move(row, 0)
1081 self.screen.clrtobot() 1168 self.screen.clrtobot()
1082 stats = self.stats.get(self._display_guests) 1169 stats = self.stats.get(self._display_guests)
1083 1170 total = 0.
1084 def sortCurAvg(x): 1171 ctotal = 0.
1085 # sort by current events if available 1172 for key, values in stats.items():
1086 if stats[x][1]: 1173 if self._display_guests:
1087 return (-stats[x][1], -stats[x][0]) 1174 if self.get_gname_from_pid(key):
1175 total += values.value
1176 continue
1177 if not key.find(' ') != -1:
1178 total += values.value
1088 else: 1179 else:
1089 return (0, -stats[x][0]) 1180 ctotal += values.value
1181 if total == 0.:
1182 # we don't have any fields, or all non-child events are filtered
1183 total = ctotal
1090 1184
1091 def sortTotal(x): 1185 # print events
1092 # sort by totals
1093 return (0, -stats[x][0])
1094 total = 0.
1095 for key in stats.keys():
1096 if key.find('(') is -1:
1097 total += stats[key][0]
1098 if self._sorting == SORT_DEFAULT:
1099 sortkey = sortCurAvg
1100 else:
1101 sortkey = sortTotal
1102 tavg = 0 1186 tavg = 0
1103 for key in sorted(stats.keys(), key=sortkey): 1187 tcur = 0
1104 if row >= self.screen.getmaxyx()[0] - 1: 1188 for key, values in get_sorted_events(self, stats):
1105 break 1189 if row >= self.screen.getmaxyx()[0] - 1 or values == (0, 0):
1106 values = stats[key]
1107 if not values[0] and not values[1]:
1108 break 1190 break
1109 if values[0] is not None: 1191 if self._display_guests:
1110 cur = int(round(values[1] / sleeptime)) if values[1] else '' 1192 key = self.get_gname_from_pid(key)
1111 if self._display_guests: 1193 if not key:
1112 key = self.get_gname_from_pid(key) 1194 continue
1113 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % 1195 cur = int(round(values.delta / sleeptime)) if values.delta else ''
1114 (key, values[0], values[0] * 100 / total, 1196 if key[0] != ' ':
1115 cur)) 1197 if values.delta:
1116 if cur is not '' and key.find('(') is -1: 1198 tcur += values.delta
1117 tavg += cur 1199 ptotal = values.value
1200 ltotal = total
1201 else:
1202 ltotal = ptotal
1203 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % (key,
1204 values.value,
1205 values.value * 100 / float(ltotal), cur))
1118 row += 1 1206 row += 1
1119 if row == 3: 1207 if row == 3:
1120 self.screen.addstr(4, 1, 'No matching events reported yet') 1208 self.screen.addstr(4, 1, 'No matching events reported yet')
1121 else: 1209 if row > 4:
1210 tavg = int(round(tcur / sleeptime)) if tcur > 0 else ''
1122 self.screen.addstr(row, 1, '%-40s %10d %8s' % 1211 self.screen.addstr(row, 1, '%-40s %10d %8s' %
1123 ('Total', total, tavg if tavg else ''), 1212 ('Total', total, tavg), curses.A_BOLD)
1124 curses.A_BOLD)
1125 self.screen.refresh() 1213 self.screen.refresh()
1126 1214
1127 def show_msg(self, text): 1215 def _show_msg(self, text):
1128 """Display message centered text and exit on key press""" 1216 """Display message centered text and exit on key press"""
1129 hint = 'Press any key to continue' 1217 hint = 'Press any key to continue'
1130 curses.cbreak() 1218 curses.cbreak()
@@ -1139,16 +1227,16 @@ class Tui(object):
1139 curses.A_STANDOUT) 1227 curses.A_STANDOUT)
1140 self.screen.getkey() 1228 self.screen.getkey()
1141 1229
1142 def show_help_interactive(self): 1230 def _show_help_interactive(self):
1143 """Display help with list of interactive commands""" 1231 """Display help with list of interactive commands"""
1144 msg = (' b toggle events by guests (debugfs only, honors' 1232 msg = (' b toggle events by guests (debugfs only, honors'
1145 ' filters)', 1233 ' filters)',
1146 ' c clear filter', 1234 ' c clear filter',
1147 ' f filter by regular expression', 1235 ' f filter by regular expression',
1148 ' g filter by guest name', 1236 ' g filter by guest name/PID',
1149 ' h display interactive commands reference', 1237 ' h display interactive commands reference',
1150 ' o toggle sorting order (Total vs CurAvg/s)', 1238 ' o toggle sorting order (Total vs CurAvg/s)',
1151 ' p filter by PID', 1239 ' p filter by guest name/PID',
1152 ' q quit', 1240 ' q quit',
1153 ' r reset stats', 1241 ' r reset stats',
1154 ' s set update interval', 1242 ' s set update interval',
@@ -1165,14 +1253,15 @@ class Tui(object):
1165 self.screen.addstr(row, 0, line) 1253 self.screen.addstr(row, 0, line)
1166 row += 1 1254 row += 1
1167 self.screen.getkey() 1255 self.screen.getkey()
1168 self.refresh_header() 1256 self._refresh_header()
1169 1257
1170 def show_filter_selection(self): 1258 def _show_filter_selection(self):
1171 """Draws filter selection mask. 1259 """Draws filter selection mask.
1172 1260
1173 Asks for a valid regex and sets the fields filter accordingly. 1261 Asks for a valid regex and sets the fields filter accordingly.
1174 1262
1175 """ 1263 """
1264 msg = ''
1176 while True: 1265 while True:
1177 self.screen.erase() 1266 self.screen.erase()
1178 self.screen.addstr(0, 0, 1267 self.screen.addstr(0, 0,
@@ -1181,61 +1270,25 @@ class Tui(object):
1181 self.screen.addstr(2, 0, 1270 self.screen.addstr(2, 0,
1182 "Current regex: {0}" 1271 "Current regex: {0}"
1183 .format(self.stats.fields_filter)) 1272 .format(self.stats.fields_filter))
1273 self.screen.addstr(5, 0, msg)
1184 self.screen.addstr(3, 0, "New regex: ") 1274 self.screen.addstr(3, 0, "New regex: ")
1185 curses.echo() 1275 curses.echo()
1186 regex = self.screen.getstr().decode(ENCODING) 1276 regex = self.screen.getstr().decode(ENCODING)
1187 curses.noecho() 1277 curses.noecho()
1188 if len(regex) == 0: 1278 if len(regex) == 0:
1189 self.stats.fields_filter = DEFAULT_REGEX 1279 self.stats.fields_filter = ''
1190 self.refresh_header() 1280 self._refresh_header()
1191 return 1281 return
1192 try: 1282 try:
1193 re.compile(regex) 1283 re.compile(regex)
1194 self.stats.fields_filter = regex 1284 self.stats.fields_filter = regex
1195 self.refresh_header() 1285 self._refresh_header()
1196 return 1286 return
1197 except re.error: 1287 except re.error:
1288 msg = '"' + regex + '": Not a valid regular expression'
1198 continue 1289 continue
1199 1290
1200 def show_vm_selection_by_pid(self): 1291 def _show_set_update_interval(self):
1201 """Draws PID selection mask.
1202
1203 Asks for a pid until a valid pid or 0 has been entered.
1204
1205 """
1206 msg = ''
1207 while True:
1208 self.screen.erase()
1209 self.screen.addstr(0, 0,
1210 'Show statistics for specific pid.',
1211 curses.A_BOLD)
1212 self.screen.addstr(1, 0,
1213 'This might limit the shown data to the trace '
1214 'statistics.')
1215 self.screen.addstr(5, 0, msg)
1216 self.print_all_gnames(7)
1217
1218 curses.echo()
1219 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1220 pid = self.screen.getstr().decode(ENCODING)
1221 curses.noecho()
1222
1223 try:
1224 if len(pid) > 0:
1225 pid = int(pid)
1226 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1227 str(pid))):
1228 msg = '"' + str(pid) + '": Not a running process'
1229 continue
1230 else:
1231 pid = 0
1232 self.refresh_header(pid)
1233 self.update_pid(pid)
1234 break
1235 except ValueError:
1236 msg = '"' + str(pid) + '": Not a valid pid'
1237
1238 def show_set_update_interval(self):
1239 """Draws update interval selection mask.""" 1292 """Draws update interval selection mask."""
1240 msg = '' 1293 msg = ''
1241 while True: 1294 while True:
@@ -1265,60 +1318,67 @@ class Tui(object):
1265 1318
1266 except ValueError: 1319 except ValueError:
1267 msg = '"' + str(val) + '": Invalid value' 1320 msg = '"' + str(val) + '": Invalid value'
1268 self.refresh_header() 1321 self._refresh_header()
1269 1322
1270 def show_vm_selection_by_guest_name(self): 1323 def _show_vm_selection_by_guest(self):
1271 """Draws guest selection mask. 1324 """Draws guest selection mask.
1272 1325
1273 Asks for a guest name until a valid guest name or '' is entered. 1326 Asks for a guest name or pid until a valid guest name or '' is entered.
1274 1327
1275 """ 1328 """
1276 msg = '' 1329 msg = ''
1277 while True: 1330 while True:
1278 self.screen.erase() 1331 self.screen.erase()
1279 self.screen.addstr(0, 0, 1332 self.screen.addstr(0, 0,
1280 'Show statistics for specific guest.', 1333 'Show statistics for specific guest or pid.',
1281 curses.A_BOLD) 1334 curses.A_BOLD)
1282 self.screen.addstr(1, 0, 1335 self.screen.addstr(1, 0,
1283 'This might limit the shown data to the trace ' 1336 'This might limit the shown data to the trace '
1284 'statistics.') 1337 'statistics.')
1285 self.screen.addstr(5, 0, msg) 1338 self.screen.addstr(5, 0, msg)
1286 self.print_all_gnames(7) 1339 self._print_all_gnames(7)
1287 curses.echo() 1340 curses.echo()
1288 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ") 1341 curses.curs_set(1)
1289 gname = self.screen.getstr().decode(ENCODING) 1342 self.screen.addstr(3, 0, "Guest or pid [ENTER exits]: ")
1343 guest = self.screen.getstr().decode(ENCODING)
1290 curses.noecho() 1344 curses.noecho()
1291 1345
1292 if not gname: 1346 pid = 0
1293 self.refresh_header(0) 1347 if not guest or guest == '0':
1294 self.update_pid(0)
1295 break 1348 break
1296 else: 1349 if guest.isdigit():
1297 pids = [] 1350 if not os.path.isdir(os.path.join('/proc/', guest)):
1298 try: 1351 msg = '"' + guest + '": Not a running process'
1299 pids = self.get_pid_from_gname(gname)
1300 except:
1301 msg = '"' + gname + '": Internal error while searching, ' \
1302 'use pid filter instead'
1303 continue
1304 if len(pids) == 0:
1305 msg = '"' + gname + '": Not an active guest'
1306 continue 1352 continue
1307 if len(pids) > 1: 1353 pid = int(guest)
1308 msg = '"' + gname + '": Multiple matches found, use pid ' \
1309 'filter instead'
1310 continue
1311 self.refresh_header(pids[0])
1312 self.update_pid(pids[0])
1313 break 1354 break
1355 pids = []
1356 try:
1357 pids = self.get_pid_from_gname(guest)
1358 except:
1359 msg = '"' + guest + '": Internal error while searching, ' \
1360 'use pid filter instead'
1361 continue
1362 if len(pids) == 0:
1363 msg = '"' + guest + '": Not an active guest'
1364 continue
1365 if len(pids) > 1:
1366 msg = '"' + guest + '": Multiple matches found, use pid ' \
1367 'filter instead'
1368 continue
1369 pid = pids[0]
1370 break
1371 curses.curs_set(0)
1372 self._refresh_header(pid)
1373 self._update_pid(pid)
1314 1374
1315 def show_stats(self): 1375 def show_stats(self):
1316 """Refreshes the screen and processes user input.""" 1376 """Refreshes the screen and processes user input."""
1317 sleeptime = self._delay_initial 1377 sleeptime = self._delay_initial
1318 self.refresh_header() 1378 self._refresh_header()
1319 start = 0.0 # result based on init value never appears on screen 1379 start = 0.0 # result based on init value never appears on screen
1320 while True: 1380 while True:
1321 self.refresh_body(time.time() - start) 1381 self._refresh_body(time.time() - start)
1322 curses.halfdelay(int(sleeptime * 10)) 1382 curses.halfdelay(int(sleeptime * 10))
1323 start = time.time() 1383 start = time.time()
1324 sleeptime = self._delay_regular 1384 sleeptime = self._delay_regular
@@ -1327,47 +1387,39 @@ class Tui(object):
1327 if char == 'b': 1387 if char == 'b':
1328 self._display_guests = not self._display_guests 1388 self._display_guests = not self._display_guests
1329 if self.stats.toggle_display_guests(self._display_guests): 1389 if self.stats.toggle_display_guests(self._display_guests):
1330 self.show_msg(['Command not available with tracepoints' 1390 self._show_msg(['Command not available with '
1331 ' enabled', 'Restart with debugfs only ' 1391 'tracepoints enabled', 'Restart with '
1332 '(see option \'-d\') and try again!']) 1392 'debugfs only (see option \'-d\') and '
1393 'try again!'])
1333 self._display_guests = not self._display_guests 1394 self._display_guests = not self._display_guests
1334 self.refresh_header() 1395 self._refresh_header()
1335 if char == 'c': 1396 if char == 'c':
1336 self.stats.fields_filter = DEFAULT_REGEX 1397 self.stats.fields_filter = ''
1337 self.refresh_header(0) 1398 self._refresh_header(0)
1338 self.update_pid(0) 1399 self._update_pid(0)
1339 if char == 'f': 1400 if char == 'f':
1340 curses.curs_set(1) 1401 curses.curs_set(1)
1341 self.show_filter_selection() 1402 self._show_filter_selection()
1342 curses.curs_set(0) 1403 curses.curs_set(0)
1343 sleeptime = self._delay_initial 1404 sleeptime = self._delay_initial
1344 if char == 'g': 1405 if char == 'g' or char == 'p':
1345 curses.curs_set(1) 1406 self._show_vm_selection_by_guest()
1346 self.show_vm_selection_by_guest_name()
1347 curses.curs_set(0)
1348 sleeptime = self._delay_initial 1407 sleeptime = self._delay_initial
1349 if char == 'h': 1408 if char == 'h':
1350 self.show_help_interactive() 1409 self._show_help_interactive()
1351 if char == 'o': 1410 if char == 'o':
1352 self._sorting = not self._sorting 1411 self._sorting = not self._sorting
1353 if char == 'p':
1354 curses.curs_set(1)
1355 self.show_vm_selection_by_pid()
1356 curses.curs_set(0)
1357 sleeptime = self._delay_initial
1358 if char == 'q': 1412 if char == 'q':
1359 break 1413 break
1360 if char == 'r': 1414 if char == 'r':
1361 self.stats.reset() 1415 self.stats.reset()
1362 if char == 's': 1416 if char == 's':
1363 curses.curs_set(1) 1417 curses.curs_set(1)
1364 self.show_set_update_interval() 1418 self._show_set_update_interval()
1365 curses.curs_set(0) 1419 curses.curs_set(0)
1366 sleeptime = self._delay_initial 1420 sleeptime = self._delay_initial
1367 if char == 'x': 1421 if char == 'x':
1368 self.update_drilldown() 1422 self.stats.child_events = not self.stats.child_events
1369 # prevents display of current values on next refresh
1370 self.stats.get(self._display_guests)
1371 except KeyboardInterrupt: 1423 except KeyboardInterrupt:
1372 break 1424 break
1373 except curses.error: 1425 except curses.error:
@@ -1380,9 +1432,9 @@ def batch(stats):
1380 s = stats.get() 1432 s = stats.get()
1381 time.sleep(1) 1433 time.sleep(1)
1382 s = stats.get() 1434 s = stats.get()
1383 for key in sorted(s.keys()): 1435 for key, values in sorted(s.items()):
1384 values = s[key] 1436 print('%-42s%10d%10d' % (key.split(' ')[0], values.value,
1385 print('%-42s%10d%10d' % (key, values[0], values[1])) 1437 values.delta))
1386 except KeyboardInterrupt: 1438 except KeyboardInterrupt:
1387 pass 1439 pass
1388 1440
@@ -1392,14 +1444,14 @@ def log(stats):
1392 keys = sorted(stats.get().keys()) 1444 keys = sorted(stats.get().keys())
1393 1445
1394 def banner(): 1446 def banner():
1395 for k in keys: 1447 for key in keys:
1396 print(k, end=' ') 1448 print(key.split(' ')[0], end=' ')
1397 print() 1449 print()
1398 1450
1399 def statline(): 1451 def statline():
1400 s = stats.get() 1452 s = stats.get()
1401 for k in keys: 1453 for key in keys:
1402 print(' %9d' % s[k][1], end=' ') 1454 print(' %9d' % s[key].delta, end=' ')
1403 print() 1455 print()
1404 line = 0 1456 line = 0
1405 banner_repeat = 20 1457 banner_repeat = 20
@@ -1504,7 +1556,7 @@ Press any other key to refresh statistics immediately.
1504 ) 1556 )
1505 optparser.add_option('-f', '--fields', 1557 optparser.add_option('-f', '--fields',
1506 action='store', 1558 action='store',
1507 default=DEFAULT_REGEX, 1559 default='',
1508 dest='fields', 1560 dest='fields',
1509 help='''fields to display (regex) 1561 help='''fields to display (regex)
1510 "-f help" for a list of available events''', 1562 "-f help" for a list of available events''',
@@ -1539,17 +1591,6 @@ Press any other key to refresh statistics immediately.
1539 1591
1540def check_access(options): 1592def check_access(options):
1541 """Exits if the current user can't access all needed directories.""" 1593 """Exits if the current user can't access all needed directories."""
1542 if not os.path.exists('/sys/kernel/debug'):
1543 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1544 sys.exit(1)
1545
1546 if not os.path.exists(PATH_DEBUGFS_KVM):
1547 sys.stderr.write("Please make sure, that debugfs is mounted and "
1548 "readable by the current user:\n"
1549 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1550 "Also ensure, that the kvm modules are loaded.\n")
1551 sys.exit(1)
1552
1553 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or 1594 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1554 not options.debugfs): 1595 not options.debugfs):
1555 sys.stderr.write("Please enable CONFIG_TRACING in your kernel " 1596 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
@@ -1567,7 +1608,33 @@ def check_access(options):
1567 return options 1608 return options
1568 1609
1569 1610
1611def assign_globals():
1612 global PATH_DEBUGFS_KVM
1613 global PATH_DEBUGFS_TRACING
1614
1615 debugfs = ''
1616 for line in file('/proc/mounts'):
1617 if line.split(' ')[0] == 'debugfs':
1618 debugfs = line.split(' ')[1]
1619 break
1620 if debugfs == '':
1621 sys.stderr.write("Please make sure that CONFIG_DEBUG_FS is enabled in "
1622 "your kernel, mounted and\nreadable by the current "
1623 "user:\n"
1624 "('mount -t debugfs debugfs /sys/kernel/debug')\n")
1625 sys.exit(1)
1626
1627 PATH_DEBUGFS_KVM = os.path.join(debugfs, 'kvm')
1628 PATH_DEBUGFS_TRACING = os.path.join(debugfs, 'tracing')
1629
1630 if not os.path.exists(PATH_DEBUGFS_KVM):
1631 sys.stderr.write("Please make sure that CONFIG_KVM is enabled in "
1632 "your kernel and that the modules are loaded.\n")
1633 sys.exit(1)
1634
1635
1570def main(): 1636def main():
1637 assign_globals()
1571 options = get_options() 1638 options = get_options()
1572 options = check_access(options) 1639 options = check_access(options)
1573 1640
diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt
index b5b3810c9e94..0811d860fe75 100644
--- a/tools/kvm/kvm_stat/kvm_stat.txt
+++ b/tools/kvm/kvm_stat/kvm_stat.txt
@@ -35,13 +35,13 @@ INTERACTIVE COMMANDS
35 35
36*f*:: filter by regular expression 36*f*:: filter by regular expression
37 37
38*g*:: filter by guest name 38*g*:: filter by guest name/PID
39 39
40*h*:: display interactive commands reference 40*h*:: display interactive commands reference
41 41
42*o*:: toggle sorting order (Total vs CurAvg/s) 42*o*:: toggle sorting order (Total vs CurAvg/s)
43 43
44*p*:: filter by PID 44*p*:: filter by guest name/PID
45 45
46*q*:: quit 46*q*:: quit
47 47