aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJanosch Frank <frankja@linux.vnet.ibm.com>2016-05-18 07:26:25 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2016-05-25 10:12:06 -0400
commitfabc712866435660f7fa1070e1fabe29eba5bc4c (patch)
treeeeed15b77c036c628dc8620db627d22a88bbaeb3
parentf0cf040f842242d55744c2606e8b7177507fbbb0 (diff)
tools: kvm_stat: Add comments
A lot of the code works with the perf events about which only sparse documentation was available until 2012. Having that information now, we can clarify what is done in the code. Signed-off-by: Janosch Frank <frankja@linux.vnet.ibm.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rwxr-xr-xtools/kvm/kvm_stat/kvm_stat161
1 files changed, 159 insertions, 2 deletions
diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat
index b4d50e8eb75a..581278c58488 100755
--- a/tools/kvm/kvm_stat/kvm_stat
+++ b/tools/kvm/kvm_stat/kvm_stat
@@ -10,6 +10,15 @@
10# 10#
11# This work is licensed under the terms of the GNU GPL, version 2. See 11# This work is licensed under the terms of the GNU GPL, version 2. See
12# the COPYING file in the top-level directory. 12# the COPYING file in the top-level directory.
13"""The kvm_stat module outputs statistics about running KVM VMs
14
15Three 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
19
20The data is sampled from the KVM's debugfs entries and its perf events.
21"""
13 22
14import curses 23import curses
15import sys 24import sys
@@ -217,8 +226,10 @@ IOCTL_NUMBERS = {
217} 226}
218 227
219class Arch(object): 228class Arch(object):
220 """Class that encapsulates global architecture specific data like 229 """Encapsulates global architecture specific data.
221 syscall and ioctl numbers. 230
231 Contains the performance event open syscall and ioctl numbers, as
232 well as the VM exit reasons for the architecture it runs on.
222 233
223 """ 234 """
224 @staticmethod 235 @staticmethod
@@ -306,12 +317,22 @@ def parse_int_list(list_string):
306 317
307 318
308def get_online_cpus(): 319def get_online_cpus():
320 """Returns a list of cpu id integers."""
309 with open('/sys/devices/system/cpu/online') as cpu_list: 321 with open('/sys/devices/system/cpu/online') as cpu_list:
310 cpu_string = cpu_list.readline() 322 cpu_string = cpu_list.readline()
311 return parse_int_list(cpu_string) 323 return parse_int_list(cpu_string)
312 324
313 325
314def get_filters(): 326def get_filters():
327 """Returns a dict of trace events, their filter ids and
328 the values that can be filtered.
329
330 Trace events can be filtered for special values by setting a
331 filter string via an ioctl. The string normally has the format
332 identifier==value. For each filter a new event will be created, to
333 be able to distinguish the events.
334
335 """
315 filters = {} 336 filters = {}
316 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) 337 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
317 if ARCH.exit_reasons: 338 if ARCH.exit_reasons:
@@ -322,6 +343,14 @@ libc = ctypes.CDLL('libc.so.6', use_errno=True)
322syscall = libc.syscall 343syscall = libc.syscall
323 344
324class perf_event_attr(ctypes.Structure): 345class perf_event_attr(ctypes.Structure):
346 """Struct that holds the necessary data to set up a trace event.
347
348 For an extensive explanation see perf_event_open(2) and
349 include/uapi/linux/perf_event.h, struct perf_event_attr
350
351 All fields that are not initialized in the constructor are 0.
352
353 """
325 _fields_ = [('type', ctypes.c_uint32), 354 _fields_ = [('type', ctypes.c_uint32),
326 ('size', ctypes.c_uint32), 355 ('size', ctypes.c_uint32),
327 ('config', ctypes.c_uint64), 356 ('config', ctypes.c_uint64),
@@ -342,6 +371,20 @@ class perf_event_attr(ctypes.Structure):
342 self.read_format = PERF_FORMAT_GROUP 371 self.read_format = PERF_FORMAT_GROUP
343 372
344def perf_event_open(attr, pid, cpu, group_fd, flags): 373def perf_event_open(attr, pid, cpu, group_fd, flags):
374 """Wrapper for the sys_perf_evt_open() syscall.
375
376 Used to set up performance events, returns a file descriptor or -1
377 on error.
378
379 Attributes are:
380 - syscall number
381 - struct perf_event_attr *
382 - pid or -1 to monitor all pids
383 - cpu number or -1 to monitor all cpus
384 - The file descriptor of the group leader or -1 to create a group.
385 - flags
386
387 """
345 return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), 388 return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
346 ctypes.c_int(pid), ctypes.c_int(cpu), 389 ctypes.c_int(pid), ctypes.c_int(cpu),
347 ctypes.c_int(group_fd), ctypes.c_long(flags)) 390 ctypes.c_int(group_fd), ctypes.c_long(flags))
@@ -353,6 +396,8 @@ PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
353PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm' 396PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
354 397
355class Group(object): 398class Group(object):
399 """Represents a perf event group."""
400
356 def __init__(self): 401 def __init__(self):
357 self.events = [] 402 self.events = []
358 403
@@ -360,6 +405,22 @@ class Group(object):
360 self.events.append(event) 405 self.events.append(event)
361 406
362 def read(self): 407 def read(self):
408 """Returns a dict with 'event name: value' for all events in the
409 group.
410
411 Values are read by reading from the file descriptor of the
412 event that is the group leader. See perf_event_open(2) for
413 details.
414
415 Read format for the used event configuration is:
416 struct read_format {
417 u64 nr; /* The number of events */
418 struct {
419 u64 value; /* The value of the event */
420 } values[nr];
421 };
422
423 """
363 length = 8 * (1 + len(self.events)) 424 length = 8 * (1 + len(self.events))
364 read_format = 'xxxxxxxx' + 'Q' * len(self.events) 425 read_format = 'xxxxxxxx' + 'Q' * len(self.events)
365 return dict(zip([event.name for event in self.events], 426 return dict(zip([event.name for event in self.events],
@@ -367,6 +428,7 @@ class Group(object):
367 os.read(self.events[0].fd, length)))) 428 os.read(self.events[0].fd, length))))
368 429
369class Event(object): 430class Event(object):
431 """Represents a performance event and manages its life cycle."""
370 def __init__(self, name, group, trace_cpu, trace_pid, trace_point, 432 def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
371 trace_filter, trace_set='kvm'): 433 trace_filter, trace_set='kvm'):
372 self.name = name 434 self.name = name
@@ -375,10 +437,19 @@ class Event(object):
375 trace_filter, trace_set) 437 trace_filter, trace_set)
376 438
377 def __del__(self): 439 def __del__(self):
440 """Closes the event's file descriptor.
441
442 As no python file object was created for the file descriptor,
443 python will not reference count the descriptor and will not
444 close it itself automatically, so we do it.
445
446 """
378 if self.fd: 447 if self.fd:
379 os.close(self.fd) 448 os.close(self.fd)
380 449
381 def setup_event_attribute(self, trace_set, trace_point): 450 def setup_event_attribute(self, trace_set, trace_point):
451 """Returns an initialized ctype perf_event_attr struct."""
452
382 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, 453 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
383 trace_point, 'id') 454 trace_point, 'id')
384 455
@@ -388,9 +459,19 @@ class Event(object):
388 459
389 def setup_event(self, group, trace_cpu, trace_pid, trace_point, 460 def setup_event(self, group, trace_cpu, trace_pid, trace_point,
390 trace_filter, trace_set): 461 trace_filter, trace_set):
462 """Sets up the perf event in Linux.
463
464 Issues the syscall to register the event in the kernel and
465 then sets the optional filter.
466
467 """
468
391 event_attr = self.setup_event_attribute(trace_set, trace_point) 469 event_attr = self.setup_event_attribute(trace_set, trace_point)
392 470
471 # First event will be group leader.
393 group_leader = -1 472 group_leader = -1
473
474 # All others have to pass the leader's descriptor instead.
394 if group.events: 475 if group.events:
395 group_leader = group.events[0].fd 476 group_leader = group.events[0].fd
396 477
@@ -408,15 +489,33 @@ class Event(object):
408 self.fd = fd 489 self.fd = fd
409 490
410 def enable(self): 491 def enable(self):
492 """Enables the trace event in the kernel.
493
494 Enabling the group leader makes reading counters from it and the
495 events under it possible.
496
497 """
411 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0) 498 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
412 499
413 def disable(self): 500 def disable(self):
501 """Disables the trace event in the kernel.
502
503 Disabling the group leader makes reading all counters under it
504 impossible.
505
506 """
414 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0) 507 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
415 508
416 def reset(self): 509 def reset(self):
510 """Resets the count of the trace event in the kernel."""
417 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0) 511 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
418 512
419class TracepointProvider(object): 513class TracepointProvider(object):
514 """Data provider for the stats class.
515
516 Manages the events/groups from which it acquires its data.
517
518 """
420 def __init__(self): 519 def __init__(self):
421 self.group_leaders = [] 520 self.group_leaders = []
422 self.filters = get_filters() 521 self.filters = get_filters()
@@ -424,6 +523,20 @@ class TracepointProvider(object):
424 self._pid = 0 523 self._pid = 0
425 524
426 def get_available_fields(self): 525 def get_available_fields(self):
526 """Returns a list of available event's of format 'event name(filter
527 name)'.
528
529 All available events have directories under
530 /sys/kernel/debug/tracing/events/ which export information
531 about the specific event. Therefore, listing the dirs gives us
532 a list of all available events.
533
534 Some events like the vm exit reasons can be filtered for
535 specific values. To take account for that, the routine below
536 creates special fields with the following format:
537 event name(filter name)
538
539 """
427 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm') 540 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
428 fields = walkdir(path)[1] 541 fields = walkdir(path)[1]
429 extra = [] 542 extra = []
@@ -436,6 +549,8 @@ class TracepointProvider(object):
436 return fields 549 return fields
437 550
438 def setup_traces(self): 551 def setup_traces(self):
552 """Creates all event and group objects needed to be able to retrieve
553 data."""
439 if self._pid > 0: 554 if self._pid > 0:
440 # Fetch list of all threads of the monitored pid, as qemu 555 # Fetch list of all threads of the monitored pid, as qemu
441 # starts a thread for each vcpu. 556 # starts a thread for each vcpu.
@@ -499,6 +614,7 @@ class TracepointProvider(object):
499 614
500 @fields.setter 615 @fields.setter
501 def fields(self, fields): 616 def fields(self, fields):
617 """Enables/disables the (un)wanted events"""
502 self._fields = fields 618 self._fields = fields
503 for group in self.group_leaders: 619 for group in self.group_leaders:
504 for index, event in enumerate(group.events): 620 for index, event in enumerate(group.events):
@@ -517,12 +633,16 @@ class TracepointProvider(object):
517 633
518 @pid.setter 634 @pid.setter
519 def pid(self, pid): 635 def pid(self, pid):
636 """Changes the monitored pid by setting new traces."""
520 self._pid = pid 637 self._pid = pid
638 # The garbage collector will get rid of all Event/Group
639 # objects and open files after removing the references.
521 self.group_leaders = [] 640 self.group_leaders = []
522 self.setup_traces() 641 self.setup_traces()
523 self.fields = self._fields 642 self.fields = self._fields
524 643
525 def read(self): 644 def read(self):
645 """Returns 'event name: current value' for all enabled events."""
526 ret = defaultdict(int) 646 ret = defaultdict(int)
527 for group in self.group_leaders: 647 for group in self.group_leaders:
528 for name, val in group.read().iteritems(): 648 for name, val in group.read().iteritems():
@@ -531,12 +651,19 @@ class TracepointProvider(object):
531 return ret 651 return ret
532 652
533class DebugfsProvider(object): 653class DebugfsProvider(object):
654 """Provides data from the files that KVM creates in the kvm debugfs
655 folder."""
534 def __init__(self): 656 def __init__(self):
535 self._fields = self.get_available_fields() 657 self._fields = self.get_available_fields()
536 self._pid = 0 658 self._pid = 0
537 self.do_read = True 659 self.do_read = True
538 660
539 def get_available_fields(self): 661 def get_available_fields(self):
662 """"Returns a list of available fields.
663
664 The fields are all available KVM debugfs files
665
666 """
540 return walkdir(PATH_DEBUGFS_KVM)[2] 667 return walkdir(PATH_DEBUGFS_KVM)[2]
541 668
542 @property 669 @property
@@ -592,6 +719,12 @@ class DebugfsProvider(object):
592 return 0 719 return 0
593 720
594class Stats(object): 721class Stats(object):
722 """Manages the data providers and the data they provide.
723
724 It is used to set filters on the provider's data and collect all
725 provider data.
726
727 """
595 def __init__(self, providers, pid, fields=None): 728 def __init__(self, providers, pid, fields=None):
596 self.providers = providers 729 self.providers = providers
597 self._pid_filter = pid 730 self._pid_filter = pid
@@ -601,6 +734,7 @@ class Stats(object):
601 self.update_provider_filters() 734 self.update_provider_filters()
602 735
603 def update_provider_filters(self): 736 def update_provider_filters(self):
737 """Propagates fields filters to providers."""
604 def wanted(key): 738 def wanted(key):
605 if not self._fields_filter: 739 if not self._fields_filter:
606 return True 740 return True
@@ -615,6 +749,7 @@ class Stats(object):
615 provider.fields = provider_fields 749 provider.fields = provider_fields
616 750
617 def update_provider_pid(self): 751 def update_provider_pid(self):
752 """Propagates pid filters to providers."""
618 for provider in self.providers: 753 for provider in self.providers:
619 provider.pid = self._pid_filter 754 provider.pid = self._pid_filter
620 755
@@ -638,6 +773,8 @@ class Stats(object):
638 self.update_provider_pid() 773 self.update_provider_pid()
639 774
640 def get(self): 775 def get(self):
776 """Returns a dict with field -> (value, delta to last value) of all
777 provider data."""
641 for provider in self.providers: 778 for provider in self.providers:
642 new = provider.read() 779 new = provider.read()
643 for key in provider.fields: 780 for key in provider.fields:
@@ -653,6 +790,7 @@ LABEL_WIDTH = 40
653NUMBER_WIDTH = 10 790NUMBER_WIDTH = 10
654 791
655class Tui(object): 792class Tui(object):
793 """Instruments curses to draw a nice text ui."""
656 def __init__(self, stats): 794 def __init__(self, stats):
657 self.stats = stats 795 self.stats = stats
658 self.screen = None 796 self.screen = None
@@ -687,6 +825,7 @@ class Tui(object):
687 curses.endwin() 825 curses.endwin()
688 826
689 def update_drilldown(self): 827 def update_drilldown(self):
828 """Sets or removes a filter that only allows fields without braces."""
690 if not self.stats.fields_filter: 829 if not self.stats.fields_filter:
691 self.stats.fields_filter = r'^[^\(]*$' 830 self.stats.fields_filter = r'^[^\(]*$'
692 831
@@ -694,9 +833,11 @@ class Tui(object):
694 self.stats.fields_filter = None 833 self.stats.fields_filter = None
695 834
696 def update_pid(self, pid): 835 def update_pid(self, pid):
836 """Propagates pid selection to stats object."""
697 self.stats.pid_filter = pid 837 self.stats.pid_filter = pid
698 838
699 def refresh(self, sleeptime): 839 def refresh(self, sleeptime):
840 """Refreshes on-screen data."""
700 self.screen.erase() 841 self.screen.erase()
701 if self.stats.pid_filter > 0: 842 if self.stats.pid_filter > 0:
702 self.screen.addstr(0, 0, 'kvm statistics - pid {0}' 843 self.screen.addstr(0, 0, 'kvm statistics - pid {0}'
@@ -734,6 +875,11 @@ class Tui(object):
734 self.screen.refresh() 875 self.screen.refresh()
735 876
736 def show_filter_selection(self): 877 def show_filter_selection(self):
878 """Draws filter selection mask.
879
880 Asks for a valid regex and sets the fields filter accordingly.
881
882 """
737 while True: 883 while True:
738 self.screen.erase() 884 self.screen.erase()
739 self.screen.addstr(0, 0, 885 self.screen.addstr(0, 0,
@@ -756,6 +902,11 @@ class Tui(object):
756 continue 902 continue
757 903
758 def show_vm_selection(self): 904 def show_vm_selection(self):
905 """Draws PID selection mask.
906
907 Asks for a pid until a valid pid or 0 has been entered.
908
909 """
759 while True: 910 while True:
760 self.screen.erase() 911 self.screen.erase()
761 self.screen.addstr(0, 0, 912 self.screen.addstr(0, 0,
@@ -787,6 +938,7 @@ class Tui(object):
787 continue 938 continue
788 939
789 def show_stats(self): 940 def show_stats(self):
941 """Refreshes the screen and processes user input."""
790 sleeptime = 0.25 942 sleeptime = 0.25
791 while True: 943 while True:
792 self.refresh(sleeptime) 944 self.refresh(sleeptime)
@@ -809,6 +961,7 @@ class Tui(object):
809 continue 961 continue
810 962
811def batch(stats): 963def batch(stats):
964 """Prints statistics in a key, value format."""
812 s = stats.get() 965 s = stats.get()
813 time.sleep(1) 966 time.sleep(1)
814 s = stats.get() 967 s = stats.get()
@@ -817,6 +970,7 @@ def batch(stats):
817 print '%-42s%10d%10d' % (key, values[0], values[1]) 970 print '%-42s%10d%10d' % (key, values[0], values[1])
818 971
819def log(stats): 972def log(stats):
973 """Prints statistics as reiterating key block, multiple value blocks."""
820 keys = sorted(stats.get().iterkeys()) 974 keys = sorted(stats.get().iterkeys())
821 def banner(): 975 def banner():
822 for k in keys: 976 for k in keys:
@@ -837,6 +991,7 @@ def log(stats):
837 line += 1 991 line += 1
838 992
839def get_options(): 993def get_options():
994 """Returns processed program arguments."""
840 description_text = """ 995 description_text = """
841This script displays various statistics about VMs running under KVM. 996This script displays various statistics about VMs running under KVM.
842The statistics are gathered from the KVM debugfs entries and / or the 997The statistics are gathered from the KVM debugfs entries and / or the
@@ -906,6 +1061,7 @@ Requirements:
906 return options 1061 return options
907 1062
908def get_providers(options): 1063def get_providers(options):
1064 """Returns a list of data providers depending on the passed options."""
909 providers = [] 1065 providers = []
910 1066
911 if options.tracepoints: 1067 if options.tracepoints:
@@ -918,6 +1074,7 @@ def get_providers(options):
918 return providers 1074 return providers
919 1075
920def check_access(options): 1076def check_access(options):
1077 """Exits if the current user can't access all needed directories."""
921 if not os.path.exists('/sys/kernel/debug'): 1078 if not os.path.exists('/sys/kernel/debug'):
922 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.') 1079 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
923 sys.exit(1) 1080 sys.exit(1)