Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Janosch Frank and committed by
Paolo Bonzini
fabc7128 f0cf040f

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