Merge branch 'for-4.18/alps' into for-linus
[linux-2.6-block.git] / tools / kvm / kvm_stat / kvm_stat
1 #!/usr/bin/python
2 #
3 # top-like utility for displaying kvm statistics
4 #
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
7 #
8 # Authors:
9 #  Avi Kivity <avi@redhat.com>
10 #
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
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 """
22 from __future__ import print_function
23
24 import curses
25 import sys
26 import locale
27 import os
28 import time
29 import optparse
30 import ctypes
31 import fcntl
32 import resource
33 import struct
34 import re
35 import subprocess
36 from collections import defaultdict, namedtuple
37
38 VMX_EXIT_REASONS = {
39     'EXCEPTION_NMI':        0,
40     'EXTERNAL_INTERRUPT':   1,
41     'TRIPLE_FAULT':         2,
42     'PENDING_INTERRUPT':    7,
43     'NMI_WINDOW':           8,
44     'TASK_SWITCH':          9,
45     'CPUID':                10,
46     'HLT':                  12,
47     'INVLPG':               14,
48     'RDPMC':                15,
49     'RDTSC':                16,
50     'VMCALL':               18,
51     'VMCLEAR':              19,
52     'VMLAUNCH':             20,
53     'VMPTRLD':              21,
54     'VMPTRST':              22,
55     'VMREAD':               23,
56     'VMRESUME':             24,
57     'VMWRITE':              25,
58     'VMOFF':                26,
59     'VMON':                 27,
60     'CR_ACCESS':            28,
61     'DR_ACCESS':            29,
62     'IO_INSTRUCTION':       30,
63     'MSR_READ':             31,
64     'MSR_WRITE':            32,
65     'INVALID_STATE':        33,
66     'MWAIT_INSTRUCTION':    36,
67     'MONITOR_INSTRUCTION':  39,
68     'PAUSE_INSTRUCTION':    40,
69     'MCE_DURING_VMENTRY':   41,
70     'TPR_BELOW_THRESHOLD':  43,
71     'APIC_ACCESS':          44,
72     'EPT_VIOLATION':        48,
73     'EPT_MISCONFIG':        49,
74     'WBINVD':               54,
75     'XSETBV':               55,
76     'APIC_WRITE':           56,
77     'INVPCID':              58,
78 }
79
80 SVM_EXIT_REASONS = {
81     'READ_CR0':       0x000,
82     'READ_CR3':       0x003,
83     'READ_CR4':       0x004,
84     'READ_CR8':       0x008,
85     'WRITE_CR0':      0x010,
86     'WRITE_CR3':      0x013,
87     'WRITE_CR4':      0x014,
88     'WRITE_CR8':      0x018,
89     'READ_DR0':       0x020,
90     'READ_DR1':       0x021,
91     'READ_DR2':       0x022,
92     'READ_DR3':       0x023,
93     'READ_DR4':       0x024,
94     'READ_DR5':       0x025,
95     'READ_DR6':       0x026,
96     'READ_DR7':       0x027,
97     'WRITE_DR0':      0x030,
98     'WRITE_DR1':      0x031,
99     'WRITE_DR2':      0x032,
100     'WRITE_DR3':      0x033,
101     'WRITE_DR4':      0x034,
102     'WRITE_DR5':      0x035,
103     'WRITE_DR6':      0x036,
104     'WRITE_DR7':      0x037,
105     'EXCP_BASE':      0x040,
106     'INTR':           0x060,
107     'NMI':            0x061,
108     'SMI':            0x062,
109     'INIT':           0x063,
110     'VINTR':          0x064,
111     'CR0_SEL_WRITE':  0x065,
112     'IDTR_READ':      0x066,
113     'GDTR_READ':      0x067,
114     'LDTR_READ':      0x068,
115     'TR_READ':        0x069,
116     'IDTR_WRITE':     0x06a,
117     'GDTR_WRITE':     0x06b,
118     'LDTR_WRITE':     0x06c,
119     'TR_WRITE':       0x06d,
120     'RDTSC':          0x06e,
121     'RDPMC':          0x06f,
122     'PUSHF':          0x070,
123     'POPF':           0x071,
124     'CPUID':          0x072,
125     'RSM':            0x073,
126     'IRET':           0x074,
127     'SWINT':          0x075,
128     'INVD':           0x076,
129     'PAUSE':          0x077,
130     'HLT':            0x078,
131     'INVLPG':         0x079,
132     'INVLPGA':        0x07a,
133     'IOIO':           0x07b,
134     'MSR':            0x07c,
135     'TASK_SWITCH':    0x07d,
136     'FERR_FREEZE':    0x07e,
137     'SHUTDOWN':       0x07f,
138     'VMRUN':          0x080,
139     'VMMCALL':        0x081,
140     'VMLOAD':         0x082,
141     'VMSAVE':         0x083,
142     'STGI':           0x084,
143     'CLGI':           0x085,
144     'SKINIT':         0x086,
145     'RDTSCP':         0x087,
146     'ICEBP':          0x088,
147     'WBINVD':         0x089,
148     'MONITOR':        0x08a,
149     'MWAIT':          0x08b,
150     'MWAIT_COND':     0x08c,
151     'XSETBV':         0x08d,
152     'NPF':            0x400,
153 }
154
155 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
156 AARCH64_EXIT_REASONS = {
157     'UNKNOWN':      0x00,
158     'WFI':          0x01,
159     'CP15_32':      0x03,
160     'CP15_64':      0x04,
161     'CP14_MR':      0x05,
162     'CP14_LS':      0x06,
163     'FP_ASIMD':     0x07,
164     'CP10_ID':      0x08,
165     'CP14_64':      0x0C,
166     'ILL_ISS':      0x0E,
167     'SVC32':        0x11,
168     'HVC32':        0x12,
169     'SMC32':        0x13,
170     'SVC64':        0x15,
171     'HVC64':        0x16,
172     'SMC64':        0x17,
173     'SYS64':        0x18,
174     'IABT':         0x20,
175     'IABT_HYP':     0x21,
176     'PC_ALIGN':     0x22,
177     'DABT':         0x24,
178     'DABT_HYP':     0x25,
179     'SP_ALIGN':     0x26,
180     'FP_EXC32':     0x28,
181     'FP_EXC64':     0x2C,
182     'SERROR':       0x2F,
183     'BREAKPT':      0x30,
184     'BREAKPT_HYP':  0x31,
185     'SOFTSTP':      0x32,
186     'SOFTSTP_HYP':  0x33,
187     'WATCHPT':      0x34,
188     'WATCHPT_HYP':  0x35,
189     'BKPT32':       0x38,
190     'VECTOR32':     0x3A,
191     'BRK64':        0x3C,
192 }
193
194 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
195 USERSPACE_EXIT_REASONS = {
196     'UNKNOWN':          0,
197     'EXCEPTION':        1,
198     'IO':               2,
199     'HYPERCALL':        3,
200     'DEBUG':            4,
201     'HLT':              5,
202     'MMIO':             6,
203     'IRQ_WINDOW_OPEN':  7,
204     'SHUTDOWN':         8,
205     'FAIL_ENTRY':       9,
206     'INTR':             10,
207     'SET_TPR':          11,
208     'TPR_ACCESS':       12,
209     'S390_SIEIC':       13,
210     'S390_RESET':       14,
211     'DCR':              15,
212     'NMI':              16,
213     'INTERNAL_ERROR':   17,
214     'OSI':              18,
215     'PAPR_HCALL':       19,
216     'S390_UCONTROL':    20,
217     'WATCHDOG':         21,
218     'S390_TSCH':        22,
219     'EPR':              23,
220     'SYSTEM_EVENT':     24,
221 }
222
223 IOCTL_NUMBERS = {
224     'SET_FILTER':  0x40082406,
225     'ENABLE':      0x00002400,
226     'DISABLE':     0x00002401,
227     'RESET':       0x00002403,
228 }
229
230 ENCODING = locale.getpreferredencoding(False)
231 TRACE_FILTER = re.compile(r'^[^\(]*$')
232
233
234 class Arch(object):
235     """Encapsulates global architecture specific data.
236
237     Contains the performance event open syscall and ioctl numbers, as
238     well as the VM exit reasons for the architecture it runs on.
239
240     """
241     @staticmethod
242     def get_arch():
243         machine = os.uname()[4]
244
245         if machine.startswith('ppc'):
246             return ArchPPC()
247         elif machine.startswith('aarch64'):
248             return ArchA64()
249         elif machine.startswith('s390'):
250             return ArchS390()
251         else:
252             # X86_64
253             for line in open('/proc/cpuinfo'):
254                 if not line.startswith('flags'):
255                     continue
256
257                 flags = line.split()
258                 if 'vmx' in flags:
259                     return ArchX86(VMX_EXIT_REASONS)
260                 if 'svm' in flags:
261                     return ArchX86(SVM_EXIT_REASONS)
262                 return
263
264     def tracepoint_is_child(self, field):
265         if (TRACE_FILTER.match(field)):
266             return None
267         return field.split('(', 1)[0]
268
269
270 class ArchX86(Arch):
271     def __init__(self, exit_reasons):
272         self.sc_perf_evt_open = 298
273         self.ioctl_numbers = IOCTL_NUMBERS
274         self.exit_reasons = exit_reasons
275
276     def debugfs_is_child(self, field):
277         """ Returns name of parent if 'field' is a child, None otherwise """
278         return None
279
280
281 class ArchPPC(Arch):
282     def __init__(self):
283         self.sc_perf_evt_open = 319
284         self.ioctl_numbers = IOCTL_NUMBERS
285         self.ioctl_numbers['ENABLE'] = 0x20002400
286         self.ioctl_numbers['DISABLE'] = 0x20002401
287         self.ioctl_numbers['RESET'] = 0x20002403
288
289         # PPC comes in 32 and 64 bit and some generated ioctl
290         # numbers depend on the wordsize.
291         char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
292         self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
293         self.exit_reasons = {}
294
295     def debugfs_is_child(self, field):
296         """ Returns name of parent if 'field' is a child, None otherwise """
297         return None
298
299
300 class ArchA64(Arch):
301     def __init__(self):
302         self.sc_perf_evt_open = 241
303         self.ioctl_numbers = IOCTL_NUMBERS
304         self.exit_reasons = AARCH64_EXIT_REASONS
305
306     def debugfs_is_child(self, field):
307         """ Returns name of parent if 'field' is a child, None otherwise """
308         return None
309
310
311 class ArchS390(Arch):
312     def __init__(self):
313         self.sc_perf_evt_open = 331
314         self.ioctl_numbers = IOCTL_NUMBERS
315         self.exit_reasons = None
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
323 ARCH = Arch.get_arch()
324
325
326 class perf_event_attr(ctypes.Structure):
327     """Struct that holds the necessary data to set up a trace event.
328
329     For an extensive explanation see perf_event_open(2) and
330     include/uapi/linux/perf_event.h, struct perf_event_attr
331
332     All fields that are not initialized in the constructor are 0.
333
334     """
335     _fields_ = [('type', ctypes.c_uint32),
336                 ('size', ctypes.c_uint32),
337                 ('config', ctypes.c_uint64),
338                 ('sample_freq', ctypes.c_uint64),
339                 ('sample_type', ctypes.c_uint64),
340                 ('read_format', ctypes.c_uint64),
341                 ('flags', ctypes.c_uint64),
342                 ('wakeup_events', ctypes.c_uint32),
343                 ('bp_type', ctypes.c_uint32),
344                 ('bp_addr', ctypes.c_uint64),
345                 ('bp_len', ctypes.c_uint64),
346                 ]
347
348     def __init__(self):
349         super(self.__class__, self).__init__()
350         self.type = PERF_TYPE_TRACEPOINT
351         self.size = ctypes.sizeof(self)
352         self.read_format = PERF_FORMAT_GROUP
353
354
355 PERF_TYPE_TRACEPOINT = 2
356 PERF_FORMAT_GROUP = 1 << 3
357
358
359 class Group(object):
360     """Represents a perf event group."""
361
362     def __init__(self):
363         self.events = []
364
365     def add_event(self, event):
366         self.events.append(event)
367
368     def read(self):
369         """Returns a dict with 'event name: value' for all events in the
370         group.
371
372         Values are read by reading from the file descriptor of the
373         event that is the group leader. See perf_event_open(2) for
374         details.
375
376         Read format for the used event configuration is:
377         struct read_format {
378             u64 nr; /* The number of events */
379             struct {
380                 u64 value; /* The value of the event */
381             } values[nr];
382         };
383
384         """
385         length = 8 * (1 + len(self.events))
386         read_format = 'xxxxxxxx' + 'Q' * len(self.events)
387         return dict(zip([event.name for event in self.events],
388                         struct.unpack(read_format,
389                                       os.read(self.events[0].fd, length))))
390
391
392 class Event(object):
393     """Represents a performance event and manages its life cycle."""
394     def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
395                  trace_filter, trace_set='kvm'):
396         self.libc = ctypes.CDLL('libc.so.6', use_errno=True)
397         self.syscall = self.libc.syscall
398         self.name = name
399         self.fd = None
400         self._setup_event(group, trace_cpu, trace_pid, trace_point,
401                           trace_filter, trace_set)
402
403     def __del__(self):
404         """Closes the event's file descriptor.
405
406         As no python file object was created for the file descriptor,
407         python will not reference count the descriptor and will not
408         close it itself automatically, so we do it.
409
410         """
411         if self.fd:
412             os.close(self.fd)
413
414     def _perf_event_open(self, attr, pid, cpu, group_fd, flags):
415         """Wrapper for the sys_perf_evt_open() syscall.
416
417         Used to set up performance events, returns a file descriptor or -1
418         on error.
419
420         Attributes are:
421         - syscall number
422         - struct perf_event_attr *
423         - pid or -1 to monitor all pids
424         - cpu number or -1 to monitor all cpus
425         - The file descriptor of the group leader or -1 to create a group.
426         - flags
427
428         """
429         return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
430                             ctypes.c_int(pid), ctypes.c_int(cpu),
431                             ctypes.c_int(group_fd), ctypes.c_long(flags))
432
433     def _setup_event_attribute(self, trace_set, trace_point):
434         """Returns an initialized ctype perf_event_attr struct."""
435
436         id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
437                                trace_point, 'id')
438
439         event_attr = perf_event_attr()
440         event_attr.config = int(open(id_path).read())
441         return event_attr
442
443     def _setup_event(self, group, trace_cpu, trace_pid, trace_point,
444                      trace_filter, trace_set):
445         """Sets up the perf event in Linux.
446
447         Issues the syscall to register the event in the kernel and
448         then sets the optional filter.
449
450         """
451
452         event_attr = self._setup_event_attribute(trace_set, trace_point)
453
454         # First event will be group leader.
455         group_leader = -1
456
457         # All others have to pass the leader's descriptor instead.
458         if group.events:
459             group_leader = group.events[0].fd
460
461         fd = self._perf_event_open(event_attr, trace_pid,
462                                    trace_cpu, group_leader, 0)
463         if fd == -1:
464             err = ctypes.get_errno()
465             raise OSError(err, os.strerror(err),
466                           'while calling sys_perf_event_open().')
467
468         if trace_filter:
469             fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
470                         trace_filter)
471
472         self.fd = fd
473
474     def enable(self):
475         """Enables the trace event in the kernel.
476
477         Enabling the group leader makes reading counters from it and the
478         events under it possible.
479
480         """
481         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
482
483     def disable(self):
484         """Disables the trace event in the kernel.
485
486         Disabling the group leader makes reading all counters under it
487         impossible.
488
489         """
490         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
491
492     def reset(self):
493         """Resets the count of the trace event in the kernel."""
494         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
495
496
497 class Provider(object):
498     """Encapsulates functionalities used by all providers."""
499     def __init__(self, pid):
500         self.child_events = False
501         self.pid = pid
502
503     @staticmethod
504     def is_field_wanted(fields_filter, field):
505         """Indicate whether field is valid according to fields_filter."""
506         if not fields_filter:
507             return True
508         return re.match(fields_filter, field) is not None
509
510     @staticmethod
511     def walkdir(path):
512         """Returns os.walk() data for specified directory.
513
514         As it is only a wrapper it returns the same 3-tuple of (dirpath,
515         dirnames, filenames).
516         """
517         return next(os.walk(path))
518
519
520 class TracepointProvider(Provider):
521     """Data provider for the stats class.
522
523     Manages the events/groups from which it acquires its data.
524
525     """
526     def __init__(self, pid, fields_filter):
527         self.group_leaders = []
528         self.filters = self._get_filters()
529         self.update_fields(fields_filter)
530         super(TracepointProvider, self).__init__(pid)
531
532     @staticmethod
533     def _get_filters():
534         """Returns a dict of trace events, their filter ids and
535         the values that can be filtered.
536
537         Trace events can be filtered for special values by setting a
538         filter string via an ioctl. The string normally has the format
539         identifier==value. For each filter a new event will be created, to
540         be able to distinguish the events.
541
542         """
543         filters = {}
544         filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
545         if ARCH.exit_reasons:
546             filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
547         return filters
548
549     def _get_available_fields(self):
550         """Returns a list of available events of format 'event name(filter
551         name)'.
552
553         All available events have directories under
554         /sys/kernel/debug/tracing/events/ which export information
555         about the specific event. Therefore, listing the dirs gives us
556         a list of all available events.
557
558         Some events like the vm exit reasons can be filtered for
559         specific values. To take account for that, the routine below
560         creates special fields with the following format:
561         event name(filter name)
562
563         """
564         path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
565         fields = self.walkdir(path)[1]
566         extra = []
567         for field in fields:
568             if field in self.filters:
569                 filter_name_, filter_dicts = self.filters[field]
570                 for name in filter_dicts:
571                     extra.append(field + '(' + name + ')')
572         fields += extra
573         return fields
574
575     def update_fields(self, fields_filter):
576         """Refresh fields, applying fields_filter"""
577         self.fields = [field for field in self._get_available_fields()
578                        if self.is_field_wanted(fields_filter, field) or
579                        ARCH.tracepoint_is_child(field)]
580
581     @staticmethod
582     def _get_online_cpus():
583         """Returns a list of cpu id integers."""
584         def parse_int_list(list_string):
585             """Returns an int list from a string of comma separated integers and
586             integer ranges."""
587             integers = []
588             members = list_string.split(',')
589
590             for member in members:
591                 if '-' not in member:
592                     integers.append(int(member))
593                 else:
594                     int_range = member.split('-')
595                     integers.extend(range(int(int_range[0]),
596                                           int(int_range[1]) + 1))
597
598             return integers
599
600         with open('/sys/devices/system/cpu/online') as cpu_list:
601             cpu_string = cpu_list.readline()
602             return parse_int_list(cpu_string)
603
604     def _setup_traces(self):
605         """Creates all event and group objects needed to be able to retrieve
606         data."""
607         fields = self._get_available_fields()
608         if self._pid > 0:
609             # Fetch list of all threads of the monitored pid, as qemu
610             # starts a thread for each vcpu.
611             path = os.path.join('/proc', str(self._pid), 'task')
612             groupids = self.walkdir(path)[1]
613         else:
614             groupids = self._get_online_cpus()
615
616         # The constant is needed as a buffer for python libs, std
617         # streams and other files that the script opens.
618         newlim = len(groupids) * len(fields) + 50
619         try:
620             softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
621
622             if hardlim < newlim:
623                 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
624                 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
625             else:
626                 # Raising the soft limit is sufficient.
627                 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
628
629         except ValueError:
630             sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
631
632         for groupid in groupids:
633             group = Group()
634             for name in fields:
635                 tracepoint = name
636                 tracefilter = None
637                 match = re.match(r'(.*)\((.*)\)', name)
638                 if match:
639                     tracepoint, sub = match.groups()
640                     tracefilter = ('%s==%d\0' %
641                                    (self.filters[tracepoint][0],
642                                     self.filters[tracepoint][1][sub]))
643
644                 # From perf_event_open(2):
645                 # pid > 0 and cpu == -1
646                 # This measures the specified process/thread on any CPU.
647                 #
648                 # pid == -1 and cpu >= 0
649                 # This measures all processes/threads on the specified CPU.
650                 trace_cpu = groupid if self._pid == 0 else -1
651                 trace_pid = int(groupid) if self._pid != 0 else -1
652
653                 group.add_event(Event(name=name,
654                                       group=group,
655                                       trace_cpu=trace_cpu,
656                                       trace_pid=trace_pid,
657                                       trace_point=tracepoint,
658                                       trace_filter=tracefilter))
659
660             self.group_leaders.append(group)
661
662     @property
663     def fields(self):
664         return self._fields
665
666     @fields.setter
667     def fields(self, fields):
668         """Enables/disables the (un)wanted events"""
669         self._fields = fields
670         for group in self.group_leaders:
671             for index, event in enumerate(group.events):
672                 if event.name in fields:
673                     event.reset()
674                     event.enable()
675                 else:
676                     # Do not disable the group leader.
677                     # It would disable all of its events.
678                     if index != 0:
679                         event.disable()
680
681     @property
682     def pid(self):
683         return self._pid
684
685     @pid.setter
686     def pid(self, pid):
687         """Changes the monitored pid by setting new traces."""
688         self._pid = pid
689         # The garbage collector will get rid of all Event/Group
690         # objects and open files after removing the references.
691         self.group_leaders = []
692         self._setup_traces()
693         self.fields = self._fields
694
695     def read(self, by_guest=0):
696         """Returns 'event name: current value' for all enabled events."""
697         ret = defaultdict(int)
698         for group in self.group_leaders:
699             for name, val in group.read().items():
700                 if name not in self._fields:
701                     continue
702                 parent = ARCH.tracepoint_is_child(name)
703                 if parent:
704                     name += ' ' + parent
705                 ret[name] += val
706         return ret
707
708     def reset(self):
709         """Reset all field counters"""
710         for group in self.group_leaders:
711             for event in group.events:
712                 event.reset()
713
714
715 class DebugfsProvider(Provider):
716     """Provides data from the files that KVM creates in the kvm debugfs
717     folder."""
718     def __init__(self, pid, fields_filter, include_past):
719         self.update_fields(fields_filter)
720         self._baseline = {}
721         self.do_read = True
722         self.paths = []
723         super(DebugfsProvider, self).__init__(pid)
724         if include_past:
725             self._restore()
726
727     def _get_available_fields(self):
728         """"Returns a list of available fields.
729
730         The fields are all available KVM debugfs files
731
732         """
733         return self.walkdir(PATH_DEBUGFS_KVM)[2]
734
735     def update_fields(self, fields_filter):
736         """Refresh fields, applying fields_filter"""
737         self._fields = [field for field in self._get_available_fields()
738                         if self.is_field_wanted(fields_filter, field) or
739                         ARCH.debugfs_is_child(field)]
740
741     @property
742     def fields(self):
743         return self._fields
744
745     @fields.setter
746     def fields(self, fields):
747         self._fields = fields
748         self.reset()
749
750     @property
751     def pid(self):
752         return self._pid
753
754     @pid.setter
755     def pid(self, pid):
756         self._pid = pid
757         if pid != 0:
758             vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
759             if len(vms) == 0:
760                 self.do_read = False
761
762             self.paths = filter(lambda x: "{}-".format(pid) in x, vms)
763
764         else:
765             self.paths = []
766             self.do_read = True
767         self.reset()
768
769     def read(self, reset=0, by_guest=0):
770         """Returns a dict with format:'file name / field -> current value'.
771
772         Parameter 'reset':
773           0   plain read
774           1   reset field counts to 0
775           2   restore the original field counts
776
777         """
778         results = {}
779
780         # If no debugfs filtering support is available, then don't read.
781         if not self.do_read:
782             return results
783
784         paths = self.paths
785         if self._pid == 0:
786             paths = []
787             for entry in os.walk(PATH_DEBUGFS_KVM):
788                 for dir in entry[1]:
789                     paths.append(dir)
790         for path in paths:
791             for field in self._fields:
792                 value = self._read_field(field, path)
793                 key = path + field
794                 if reset == 1:
795                     self._baseline[key] = value
796                 if reset == 2:
797                     self._baseline[key] = 0
798                 if self._baseline.get(key, -1) == -1:
799                     self._baseline[key] = value
800                 parent = ARCH.debugfs_is_child(field)
801                 if parent:
802                     field = field + ' ' + parent
803                 else:
804                     if by_guest:
805                         field = key.split('-')[0]    # set 'field' to 'pid'
806                 increment = value - self._baseline.get(key, 0)
807                 if field in results:
808                     results[field] += increment
809                 else:
810                     results[field] = increment
811
812         return results
813
814     def _read_field(self, field, path):
815         """Returns the value of a single field from a specific VM."""
816         try:
817             return int(open(os.path.join(PATH_DEBUGFS_KVM,
818                                          path,
819                                          field))
820                        .read())
821         except IOError:
822             return 0
823
824     def reset(self):
825         """Reset field counters"""
826         self._baseline = {}
827         self.read(1)
828
829     def _restore(self):
830         """Reset field counters"""
831         self._baseline = {}
832         self.read(2)
833
834
835 EventStat = namedtuple('EventStat', ['value', 'delta'])
836
837
838 class Stats(object):
839     """Manages the data providers and the data they provide.
840
841     It is used to set filters on the provider's data and collect all
842     provider data.
843
844     """
845     def __init__(self, options):
846         self.providers = self._get_providers(options)
847         self._pid_filter = options.pid
848         self._fields_filter = options.fields
849         self.values = {}
850         self._child_events = False
851
852     def _get_providers(self, options):
853         """Returns a list of data providers depending on the passed options."""
854         providers = []
855
856         if options.debugfs:
857             providers.append(DebugfsProvider(options.pid, options.fields,
858                                              options.dbgfs_include_past))
859         if options.tracepoints or not providers:
860             providers.append(TracepointProvider(options.pid, options.fields))
861
862         return providers
863
864     def _update_provider_filters(self):
865         """Propagates fields filters to providers."""
866         # As we reset the counters when updating the fields we can
867         # also clear the cache of old values.
868         self.values = {}
869         for provider in self.providers:
870             provider.update_fields(self._fields_filter)
871
872     def reset(self):
873         self.values = {}
874         for provider in self.providers:
875             provider.reset()
876
877     @property
878     def fields_filter(self):
879         return self._fields_filter
880
881     @fields_filter.setter
882     def fields_filter(self, fields_filter):
883         if fields_filter != self._fields_filter:
884             self._fields_filter = fields_filter
885             self._update_provider_filters()
886
887     @property
888     def pid_filter(self):
889         return self._pid_filter
890
891     @pid_filter.setter
892     def pid_filter(self, pid):
893         if pid != self._pid_filter:
894             self._pid_filter = pid
895             self.values = {}
896             for provider in self.providers:
897                 provider.pid = self._pid_filter
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
909     def get(self, by_guest=0):
910         """Returns a dict with field -> (value, delta to last value) of all
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         """
919         for provider in self.providers:
920             new = provider.read(by_guest=by_guest)
921             for key in new:
922                 oldval = self.values.get(key, EventStat(0, 0)).value
923                 newval = new.get(key, 0)
924                 newdelta = newval - oldval
925                 self.values[key] = EventStat(newval, newdelta)
926         return self.values
927
928     def toggle_display_guests(self, to_pid):
929         """Toggle between collection of stats by individual event and by
930         guest pid
931
932         Events reported by DebugfsProvider change when switching to/from
933         reading by guest values. Hence we have to remove the excess event
934         names from self.values.
935
936         """
937         if any(isinstance(ins, TracepointProvider) for ins in self.providers):
938             return 1
939         if to_pid:
940             for provider in self.providers:
941                 if isinstance(provider, DebugfsProvider):
942                     for key in provider.fields:
943                         if key in self.values.keys():
944                             del self.values[key]
945         else:
946             oldvals = self.values.copy()
947             for key in oldvals:
948                 if key.isdigit():
949                     del self.values[key]
950         # Update oldval (see get())
951         self.get(to_pid)
952         return 0
953
954
955 DELAY_DEFAULT = 3.0
956 MAX_GUEST_NAME_LEN = 48
957 MAX_REGEX_LEN = 44
958 SORT_DEFAULT = 0
959
960
961 class Tui(object):
962     """Instruments curses to draw a nice text ui."""
963     def __init__(self, stats):
964         self.stats = stats
965         self.screen = None
966         self._delay_initial = 0.25
967         self._delay_regular = DELAY_DEFAULT
968         self._sorting = SORT_DEFAULT
969         self._display_guests = 0
970
971     def __enter__(self):
972         """Initialises curses for later use.  Based on curses.wrapper
973            implementation from the Python standard library."""
974         self.screen = curses.initscr()
975         curses.noecho()
976         curses.cbreak()
977
978         # The try/catch works around a minor bit of
979         # over-conscientiousness in the curses module, the error
980         # return from C start_color() is ignorable.
981         try:
982             curses.start_color()
983         except curses.error:
984             pass
985
986         # Hide cursor in extra statement as some monochrome terminals
987         # might support hiding but not colors.
988         try:
989             curses.curs_set(0)
990         except curses.error:
991             pass
992
993         curses.use_default_colors()
994         return self
995
996     def __exit__(self, *exception):
997         """Resets the terminal to its normal state.  Based on curses.wrapper
998            implementation from the Python standard library."""
999         if self.screen:
1000             self.screen.keypad(0)
1001             curses.echo()
1002             curses.nocbreak()
1003             curses.endwin()
1004
1005     @staticmethod
1006     def get_all_gnames():
1007         """Returns a list of (pid, gname) tuples of all running guests"""
1008         res = []
1009         try:
1010             child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
1011                                      stdout=subprocess.PIPE)
1012         except:
1013             raise Exception
1014         for line in child.stdout:
1015             line = line.decode(ENCODING).lstrip().split(' ', 1)
1016             # perform a sanity check before calling the more expensive
1017             # function to possibly extract the guest name
1018             if ' -name ' in line[1]:
1019                 res.append((line[0], Tui.get_gname_from_pid(line[0])))
1020         child.stdout.close()
1021
1022         return res
1023
1024     def _print_all_gnames(self, row):
1025         """Print a list of all running guests along with their pids."""
1026         self.screen.addstr(row, 2, '%8s  %-60s' %
1027                            ('Pid', 'Guest Name (fuzzy list, might be '
1028                             'inaccurate!)'),
1029                            curses.A_UNDERLINE)
1030         row += 1
1031         try:
1032             for line in self.get_all_gnames():
1033                 self.screen.addstr(row, 2, '%8s  %-60s' % (line[0], line[1]))
1034                 row += 1
1035                 if row >= self.screen.getmaxyx()[0]:
1036                     break
1037         except Exception:
1038             self.screen.addstr(row + 1, 2, 'Not available')
1039
1040     @staticmethod
1041     def get_pid_from_gname(gname):
1042         """Fuzzy function to convert guest name to QEMU process pid.
1043
1044         Returns a list of potential pids, can be empty if no match found.
1045         Throws an exception on processing errors.
1046
1047         """
1048         pids = []
1049         for line in Tui.get_all_gnames():
1050             if gname == line[1]:
1051                 pids.append(int(line[0]))
1052
1053         return pids
1054
1055     @staticmethod
1056     def get_gname_from_pid(pid):
1057         """Returns the guest name for a QEMU process pid.
1058
1059         Extracts the guest name from the QEMU comma line by processing the
1060         '-name' option. Will also handle names specified out of sequence.
1061
1062         """
1063         name = ''
1064         try:
1065             line = open('/proc/{}/cmdline'
1066                         .format(pid), 'r').read().split('\0')
1067             parms = line[line.index('-name') + 1].split(',')
1068             while '' in parms:
1069                 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1070                 # in # ['foo', '', 'bar'], which we revert here
1071                 idx = parms.index('')
1072                 parms[idx - 1] += ',' + parms[idx + 1]
1073                 del parms[idx:idx+2]
1074             # the '-name' switch allows for two ways to specify the guest name,
1075             # where the plain name overrides the name specified via 'guest='
1076             for arg in parms:
1077                 if '=' not in arg:
1078                     name = arg
1079                     break
1080                 if arg[:6] == 'guest=':
1081                     name = arg[6:]
1082         except (ValueError, IOError, IndexError):
1083             pass
1084
1085         return name
1086
1087     def _update_pid(self, pid):
1088         """Propagates pid selection to stats object."""
1089         self.screen.addstr(4, 1, 'Updating pid filter...')
1090         self.screen.refresh()
1091         self.stats.pid_filter = pid
1092
1093     def _refresh_header(self, pid=None):
1094         """Refreshes the header."""
1095         if pid is None:
1096             pid = self.stats.pid_filter
1097         self.screen.erase()
1098         gname = self.get_gname_from_pid(pid)
1099         if gname:
1100             gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1101                                    if len(gname) > MAX_GUEST_NAME_LEN
1102                                    else gname))
1103         if pid > 0:
1104             self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1105                                .format(pid, gname), curses.A_BOLD)
1106         else:
1107             self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1108         if self.stats.fields_filter:
1109             regex = self.stats.fields_filter
1110             if len(regex) > MAX_REGEX_LEN:
1111                 regex = regex[:MAX_REGEX_LEN] + '...'
1112             self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1113         if self._display_guests:
1114             col_name = 'Guest Name'
1115         else:
1116             col_name = 'Event'
1117         self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1118                            (col_name, 'Total', '%Total', 'CurAvg/s'),
1119                            curses.A_STANDOUT)
1120         self.screen.addstr(4, 1, 'Collecting data...')
1121         self.screen.refresh()
1122
1123     def _refresh_body(self, sleeptime):
1124         def insert_child(sorted_items, child, values, parent):
1125             num = len(sorted_items)
1126             for i in range(0, num):
1127                 # only add child if parent is present
1128                 if parent.startswith(sorted_items[i][0]):
1129                     sorted_items.insert(i + 1, ('  ' + child, values))
1130
1131         def get_sorted_events(self, stats):
1132             """ separate parent and child events """
1133             if self._sorting == SORT_DEFAULT:
1134                 def sortkey(pair):
1135                     # sort by (delta value, overall value)
1136                     v = pair[1]
1137                     return (v.delta, v.value)
1138             else:
1139                 def sortkey(pair):
1140                     # sort by overall value
1141                     v = pair[1]
1142                     return v.value
1143
1144             childs = []
1145             sorted_items = []
1146             # we can't rule out child events to appear prior to parents even
1147             # when sorted - separate out all children first, and add in later
1148             for key, values in sorted(stats.items(), key=sortkey,
1149                                       reverse=True):
1150                 if values == (0, 0):
1151                     continue
1152                 if key.find(' ') != -1:
1153                     if not self.stats.child_events:
1154                         continue
1155                     childs.insert(0, (key, values))
1156                 else:
1157                     sorted_items.append((key, values))
1158             if self.stats.child_events:
1159                 for key, values in childs:
1160                     (child, parent) = key.split(' ')
1161                     insert_child(sorted_items, child, values, parent)
1162
1163             return sorted_items
1164
1165         row = 3
1166         self.screen.move(row, 0)
1167         self.screen.clrtobot()
1168         stats = self.stats.get(self._display_guests)
1169         total = 0.
1170         ctotal = 0.
1171         for key, values in stats.items():
1172             if self._display_guests:
1173                 if self.get_gname_from_pid(key):
1174                     total += values.value
1175                 continue
1176             if not key.find(' ') != -1:
1177                 total += values.value
1178             else:
1179                 ctotal += values.value
1180         if total == 0.:
1181             # we don't have any fields, or all non-child events are filtered
1182             total = ctotal
1183
1184         # print events
1185         tavg = 0
1186         tcur = 0
1187         for key, values in get_sorted_events(self, stats):
1188             if row >= self.screen.getmaxyx()[0] - 1 or values == (0, 0):
1189                 break
1190             if self._display_guests:
1191                 key = self.get_gname_from_pid(key)
1192                 if not key:
1193                     continue
1194             cur = int(round(values.delta / sleeptime)) if values.delta else ''
1195             if key[0] != ' ':
1196                 if values.delta:
1197                     tcur += values.delta
1198                 ptotal = values.value
1199                 ltotal = total
1200             else:
1201                 ltotal = ptotal
1202             self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % (key,
1203                                values.value,
1204                                values.value * 100 / float(ltotal), cur))
1205             row += 1
1206         if row == 3:
1207             self.screen.addstr(4, 1, 'No matching events reported yet')
1208         if row > 4:
1209             tavg = int(round(tcur / sleeptime)) if tcur > 0 else ''
1210             self.screen.addstr(row, 1, '%-40s %10d        %8s' %
1211                                ('Total', total, tavg), curses.A_BOLD)
1212         self.screen.refresh()
1213
1214     def _show_msg(self, text):
1215         """Display message centered text and exit on key press"""
1216         hint = 'Press any key to continue'
1217         curses.cbreak()
1218         self.screen.erase()
1219         (x, term_width) = self.screen.getmaxyx()
1220         row = 2
1221         for line in text:
1222             start = (term_width - len(line)) / 2
1223             self.screen.addstr(row, start, line)
1224             row += 1
1225         self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint,
1226                            curses.A_STANDOUT)
1227         self.screen.getkey()
1228
1229     def _show_help_interactive(self):
1230         """Display help with list of interactive commands"""
1231         msg = ('   b     toggle events by guests (debugfs only, honors'
1232                ' filters)',
1233                '   c     clear filter',
1234                '   f     filter by regular expression',
1235                '   g     filter by guest name/PID',
1236                '   h     display interactive commands reference',
1237                '   o     toggle sorting order (Total vs CurAvg/s)',
1238                '   p     filter by guest name/PID',
1239                '   q     quit',
1240                '   r     reset stats',
1241                '   s     set update interval',
1242                '   x     toggle reporting of stats for individual child trace'
1243                ' events',
1244                'Any other key refreshes statistics immediately')
1245         curses.cbreak()
1246         self.screen.erase()
1247         self.screen.addstr(0, 0, "Interactive commands reference",
1248                            curses.A_BOLD)
1249         self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1250         row = 4
1251         for line in msg:
1252             self.screen.addstr(row, 0, line)
1253             row += 1
1254         self.screen.getkey()
1255         self._refresh_header()
1256
1257     def _show_filter_selection(self):
1258         """Draws filter selection mask.
1259
1260         Asks for a valid regex and sets the fields filter accordingly.
1261
1262         """
1263         msg = ''
1264         while True:
1265             self.screen.erase()
1266             self.screen.addstr(0, 0,
1267                                "Show statistics for events matching a regex.",
1268                                curses.A_BOLD)
1269             self.screen.addstr(2, 0,
1270                                "Current regex: {0}"
1271                                .format(self.stats.fields_filter))
1272             self.screen.addstr(5, 0, msg)
1273             self.screen.addstr(3, 0, "New regex: ")
1274             curses.echo()
1275             regex = self.screen.getstr().decode(ENCODING)
1276             curses.noecho()
1277             if len(regex) == 0:
1278                 self.stats.fields_filter = ''
1279                 self._refresh_header()
1280                 return
1281             try:
1282                 re.compile(regex)
1283                 self.stats.fields_filter = regex
1284                 self._refresh_header()
1285                 return
1286             except re.error:
1287                 msg = '"' + regex + '": Not a valid regular expression'
1288                 continue
1289
1290     def _show_set_update_interval(self):
1291         """Draws update interval selection mask."""
1292         msg = ''
1293         while True:
1294             self.screen.erase()
1295             self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1296                                DELAY_DEFAULT, curses.A_BOLD)
1297             self.screen.addstr(4, 0, msg)
1298             self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1299                                self._delay_regular)
1300             curses.echo()
1301             val = self.screen.getstr().decode(ENCODING)
1302             curses.noecho()
1303
1304             try:
1305                 if len(val) > 0:
1306                     delay = float(val)
1307                     if delay < 0.1:
1308                         msg = '"' + str(val) + '": Value must be >=0.1'
1309                         continue
1310                     if delay > 25.5:
1311                         msg = '"' + str(val) + '": Value must be <=25.5'
1312                         continue
1313                 else:
1314                     delay = DELAY_DEFAULT
1315                 self._delay_regular = delay
1316                 break
1317
1318             except ValueError:
1319                 msg = '"' + str(val) + '": Invalid value'
1320         self._refresh_header()
1321
1322     def _show_vm_selection_by_guest(self):
1323         """Draws guest selection mask.
1324
1325         Asks for a guest name or pid until a valid guest name or '' is entered.
1326
1327         """
1328         msg = ''
1329         while True:
1330             self.screen.erase()
1331             self.screen.addstr(0, 0,
1332                                'Show statistics for specific guest or pid.',
1333                                curses.A_BOLD)
1334             self.screen.addstr(1, 0,
1335                                'This might limit the shown data to the trace '
1336                                'statistics.')
1337             self.screen.addstr(5, 0, msg)
1338             self._print_all_gnames(7)
1339             curses.echo()
1340             curses.curs_set(1)
1341             self.screen.addstr(3, 0, "Guest or pid [ENTER exits]: ")
1342             guest = self.screen.getstr().decode(ENCODING)
1343             curses.noecho()
1344
1345             pid = 0
1346             if not guest or guest == '0':
1347                 break
1348             if guest.isdigit():
1349                 if not os.path.isdir(os.path.join('/proc/', guest)):
1350                     msg = '"' + guest + '": Not a running process'
1351                     continue
1352                 pid = int(guest)
1353                 break
1354             pids = []
1355             try:
1356                 pids = self.get_pid_from_gname(guest)
1357             except:
1358                 msg = '"' + guest + '": Internal error while searching, ' \
1359                       'use pid filter instead'
1360                 continue
1361             if len(pids) == 0:
1362                 msg = '"' + guest + '": Not an active guest'
1363                 continue
1364             if len(pids) > 1:
1365                 msg = '"' + guest + '": Multiple matches found, use pid ' \
1366                       'filter instead'
1367                 continue
1368             pid = pids[0]
1369             break
1370         curses.curs_set(0)
1371         self._refresh_header(pid)
1372         self._update_pid(pid)
1373
1374     def show_stats(self):
1375         """Refreshes the screen and processes user input."""
1376         sleeptime = self._delay_initial
1377         self._refresh_header()
1378         start = 0.0  # result based on init value never appears on screen
1379         while True:
1380             self._refresh_body(time.time() - start)
1381             curses.halfdelay(int(sleeptime * 10))
1382             start = time.time()
1383             sleeptime = self._delay_regular
1384             try:
1385                 char = self.screen.getkey()
1386                 if char == 'b':
1387                     self._display_guests = not self._display_guests
1388                     if self.stats.toggle_display_guests(self._display_guests):
1389                         self._show_msg(['Command not available with '
1390                                         'tracepoints enabled', 'Restart with '
1391                                         'debugfs only (see option \'-d\') and '
1392                                         'try again!'])
1393                         self._display_guests = not self._display_guests
1394                     self._refresh_header()
1395                 if char == 'c':
1396                     self.stats.fields_filter = ''
1397                     self._refresh_header(0)
1398                     self._update_pid(0)
1399                 if char == 'f':
1400                     curses.curs_set(1)
1401                     self._show_filter_selection()
1402                     curses.curs_set(0)
1403                     sleeptime = self._delay_initial
1404                 if char == 'g' or char == 'p':
1405                     self._show_vm_selection_by_guest()
1406                     sleeptime = self._delay_initial
1407                 if char == 'h':
1408                     self._show_help_interactive()
1409                 if char == 'o':
1410                     self._sorting = not self._sorting
1411                 if char == 'q':
1412                     break
1413                 if char == 'r':
1414                     self.stats.reset()
1415                 if char == 's':
1416                     curses.curs_set(1)
1417                     self._show_set_update_interval()
1418                     curses.curs_set(0)
1419                     sleeptime = self._delay_initial
1420                 if char == 'x':
1421                     self.stats.child_events = not self.stats.child_events
1422             except KeyboardInterrupt:
1423                 break
1424             except curses.error:
1425                 continue
1426
1427
1428 def batch(stats):
1429     """Prints statistics in a key, value format."""
1430     try:
1431         s = stats.get()
1432         time.sleep(1)
1433         s = stats.get()
1434         for key, values in sorted(s.items()):
1435             print('%-42s%10d%10d' % (key.split(' ')[0], values.value,
1436                   values.delta))
1437     except KeyboardInterrupt:
1438         pass
1439
1440
1441 def log(stats):
1442     """Prints statistics as reiterating key block, multiple value blocks."""
1443     keys = sorted(stats.get().keys())
1444
1445     def banner():
1446         for key in keys:
1447             print(key.split(' ')[0], end=' ')
1448         print()
1449
1450     def statline():
1451         s = stats.get()
1452         for key in keys:
1453             print(' %9d' % s[key].delta, end=' ')
1454         print()
1455     line = 0
1456     banner_repeat = 20
1457     while True:
1458         try:
1459             time.sleep(1)
1460             if line % banner_repeat == 0:
1461                 banner()
1462             statline()
1463             line += 1
1464         except KeyboardInterrupt:
1465             break
1466
1467
1468 def get_options():
1469     """Returns processed program arguments."""
1470     description_text = """
1471 This script displays various statistics about VMs running under KVM.
1472 The statistics are gathered from the KVM debugfs entries and / or the
1473 currently available perf traces.
1474
1475 The monitoring takes additional cpu cycles and might affect the VM's
1476 performance.
1477
1478 Requirements:
1479 - Access to:
1480     %s
1481     %s/events/*
1482     /proc/pid/task
1483 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1484   CAP_SYS_ADMIN and perf events are used.
1485 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1486   the large number of files that are possibly opened.
1487
1488 Interactive Commands:
1489    b     toggle events by guests (debugfs only, honors filters)
1490    c     clear filter
1491    f     filter by regular expression
1492    g     filter by guest name
1493    h     display interactive commands reference
1494    o     toggle sorting order (Total vs CurAvg/s)
1495    p     filter by PID
1496    q     quit
1497    r     reset stats
1498    s     set update interval
1499    x     toggle reporting of stats for individual child trace events
1500 Press any other key to refresh statistics immediately.
1501 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1502
1503     class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1504         def format_description(self, description):
1505             if description:
1506                 return description + "\n"
1507             else:
1508                 return ""
1509
1510     def cb_guest_to_pid(option, opt, val, parser):
1511         try:
1512             pids = Tui.get_pid_from_gname(val)
1513         except:
1514             sys.exit('Error while searching for guest "{}". Use "-p" to '
1515                      'specify a pid instead?'.format(val))
1516         if len(pids) == 0:
1517             sys.exit('Error: No guest by the name "{}" found'.format(val))
1518         if len(pids) > 1:
1519             sys.exit('Error: Multiple processes found (pids: {}). Use "-p" '
1520                      'to specify the desired pid'.format(" ".join(pids)))
1521         parser.values.pid = pids[0]
1522
1523     optparser = optparse.OptionParser(description=description_text,
1524                                       formatter=PlainHelpFormatter())
1525     optparser.add_option('-1', '--once', '--batch',
1526                          action='store_true',
1527                          default=False,
1528                          dest='once',
1529                          help='run in batch mode for one second',
1530                          )
1531     optparser.add_option('-i', '--debugfs-include-past',
1532                          action='store_true',
1533                          default=False,
1534                          dest='dbgfs_include_past',
1535                          help='include all available data on past events for '
1536                               'debugfs',
1537                          )
1538     optparser.add_option('-l', '--log',
1539                          action='store_true',
1540                          default=False,
1541                          dest='log',
1542                          help='run in logging mode (like vmstat)',
1543                          )
1544     optparser.add_option('-t', '--tracepoints',
1545                          action='store_true',
1546                          default=False,
1547                          dest='tracepoints',
1548                          help='retrieve statistics from tracepoints',
1549                          )
1550     optparser.add_option('-d', '--debugfs',
1551                          action='store_true',
1552                          default=False,
1553                          dest='debugfs',
1554                          help='retrieve statistics from debugfs',
1555                          )
1556     optparser.add_option('-f', '--fields',
1557                          action='store',
1558                          default='',
1559                          dest='fields',
1560                          help='''fields to display (regex)
1561                                  "-f help" for a list of available events''',
1562                          )
1563     optparser.add_option('-p', '--pid',
1564                          action='store',
1565                          default=0,
1566                          type='int',
1567                          dest='pid',
1568                          help='restrict statistics to pid',
1569                          )
1570     optparser.add_option('-g', '--guest',
1571                          action='callback',
1572                          type='string',
1573                          dest='pid',
1574                          metavar='GUEST',
1575                          help='restrict statistics to guest by name',
1576                          callback=cb_guest_to_pid,
1577                          )
1578     options, unkn = optparser.parse_args(sys.argv)
1579     if len(unkn) != 1:
1580         sys.exit('Error: Extra argument(s): ' + ' '.join(unkn[1:]))
1581     try:
1582         # verify that we were passed a valid regex up front
1583         re.compile(options.fields)
1584     except re.error:
1585         sys.exit('Error: "' + options.fields + '" is not a valid regular '
1586                  'expression')
1587
1588     return options
1589
1590
1591 def check_access(options):
1592     """Exits if the current user can't access all needed directories."""
1593     if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1594                                                      not options.debugfs):
1595         sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1596                          "when using the option -t (default).\n"
1597                          "If it is enabled, make {0} readable by the "
1598                          "current user.\n"
1599                          .format(PATH_DEBUGFS_TRACING))
1600         if options.tracepoints:
1601             sys.exit(1)
1602
1603         sys.stderr.write("Falling back to debugfs statistics!\n")
1604         options.debugfs = True
1605         time.sleep(5)
1606
1607     return options
1608
1609
1610 def assign_globals():
1611     global PATH_DEBUGFS_KVM
1612     global PATH_DEBUGFS_TRACING
1613
1614     debugfs = ''
1615     for line in open('/proc/mounts'):
1616         if line.split(' ')[0] == 'debugfs':
1617             debugfs = line.split(' ')[1]
1618             break
1619     if debugfs == '':
1620         sys.stderr.write("Please make sure that CONFIG_DEBUG_FS is enabled in "
1621                          "your kernel, mounted and\nreadable by the current "
1622                          "user:\n"
1623                          "('mount -t debugfs debugfs /sys/kernel/debug')\n")
1624         sys.exit(1)
1625
1626     PATH_DEBUGFS_KVM = os.path.join(debugfs, 'kvm')
1627     PATH_DEBUGFS_TRACING = os.path.join(debugfs, 'tracing')
1628
1629     if not os.path.exists(PATH_DEBUGFS_KVM):
1630         sys.stderr.write("Please make sure that CONFIG_KVM is enabled in "
1631                          "your kernel and that the modules are loaded.\n")
1632         sys.exit(1)
1633
1634
1635 def main():
1636     assign_globals()
1637     options = get_options()
1638     options = check_access(options)
1639
1640     if (options.pid > 0 and
1641         not os.path.isdir(os.path.join('/proc/',
1642                                        str(options.pid)))):
1643         sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1644         sys.exit('Specified pid does not exist.')
1645
1646     stats = Stats(options)
1647
1648     if options.fields == 'help':
1649         stats.fields_filter = None
1650         event_list = []
1651         for key in stats.get().keys():
1652             event_list.append(key.split('(', 1)[0])
1653         sys.stdout.write('  ' + '\n  '.join(sorted(set(event_list))) + '\n')
1654         sys.exit(0)
1655
1656     if options.log:
1657         log(stats)
1658     elif not options.once:
1659         with Tui(stats) as tui:
1660             tui.show_stats()
1661     else:
1662         batch(stats)
1663
1664 if __name__ == "__main__":
1665     main()