Merge tag 'tty-4.15-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[sfrench/cifs-2.6.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
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
232
233 class Arch(object):
234     """Encapsulates global architecture specific data.
235
236     Contains the performance event open syscall and ioctl numbers, as
237     well as the VM exit reasons for the architecture it runs on.
238
239     """
240     @staticmethod
241     def get_arch():
242         machine = os.uname()[4]
243
244         if machine.startswith('ppc'):
245             return ArchPPC()
246         elif machine.startswith('aarch64'):
247             return ArchA64()
248         elif machine.startswith('s390'):
249             return ArchS390()
250         else:
251             # X86_64
252             for line in open('/proc/cpuinfo'):
253                 if not line.startswith('flags'):
254                     continue
255
256                 flags = line.split()
257                 if 'vmx' in flags:
258                     return ArchX86(VMX_EXIT_REASONS)
259                 if 'svm' in flags:
260                     return ArchX86(SVM_EXIT_REASONS)
261                 return
262
263
264 class ArchX86(Arch):
265     def __init__(self, exit_reasons):
266         self.sc_perf_evt_open = 298
267         self.ioctl_numbers = IOCTL_NUMBERS
268         self.exit_reasons = exit_reasons
269
270
271 class ArchPPC(Arch):
272     def __init__(self):
273         self.sc_perf_evt_open = 319
274         self.ioctl_numbers = IOCTL_NUMBERS
275         self.ioctl_numbers['ENABLE'] = 0x20002400
276         self.ioctl_numbers['DISABLE'] = 0x20002401
277         self.ioctl_numbers['RESET'] = 0x20002403
278
279         # PPC comes in 32 and 64 bit and some generated ioctl
280         # numbers depend on the wordsize.
281         char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
282         self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
283         self.exit_reasons = {}
284
285
286 class ArchA64(Arch):
287     def __init__(self):
288         self.sc_perf_evt_open = 241
289         self.ioctl_numbers = IOCTL_NUMBERS
290         self.exit_reasons = AARCH64_EXIT_REASONS
291
292
293 class ArchS390(Arch):
294     def __init__(self):
295         self.sc_perf_evt_open = 331
296         self.ioctl_numbers = IOCTL_NUMBERS
297         self.exit_reasons = None
298
299 ARCH = Arch.get_arch()
300
301
302 class perf_event_attr(ctypes.Structure):
303     """Struct that holds the necessary data to set up a trace event.
304
305     For an extensive explanation see perf_event_open(2) and
306     include/uapi/linux/perf_event.h, struct perf_event_attr
307
308     All fields that are not initialized in the constructor are 0.
309
310     """
311     _fields_ = [('type', ctypes.c_uint32),
312                 ('size', ctypes.c_uint32),
313                 ('config', ctypes.c_uint64),
314                 ('sample_freq', ctypes.c_uint64),
315                 ('sample_type', ctypes.c_uint64),
316                 ('read_format', ctypes.c_uint64),
317                 ('flags', ctypes.c_uint64),
318                 ('wakeup_events', ctypes.c_uint32),
319                 ('bp_type', ctypes.c_uint32),
320                 ('bp_addr', ctypes.c_uint64),
321                 ('bp_len', ctypes.c_uint64),
322                 ]
323
324     def __init__(self):
325         super(self.__class__, self).__init__()
326         self.type = PERF_TYPE_TRACEPOINT
327         self.size = ctypes.sizeof(self)
328         self.read_format = PERF_FORMAT_GROUP
329
330
331 PERF_TYPE_TRACEPOINT = 2
332 PERF_FORMAT_GROUP = 1 << 3
333
334 PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
335 PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
336
337
338 class Group(object):
339     """Represents a perf event group."""
340
341     def __init__(self):
342         self.events = []
343
344     def add_event(self, event):
345         self.events.append(event)
346
347     def read(self):
348         """Returns a dict with 'event name: value' for all events in the
349         group.
350
351         Values are read by reading from the file descriptor of the
352         event that is the group leader. See perf_event_open(2) for
353         details.
354
355         Read format for the used event configuration is:
356         struct read_format {
357             u64 nr; /* The number of events */
358             struct {
359                 u64 value; /* The value of the event */
360             } values[nr];
361         };
362
363         """
364         length = 8 * (1 + len(self.events))
365         read_format = 'xxxxxxxx' + 'Q' * len(self.events)
366         return dict(zip([event.name for event in self.events],
367                         struct.unpack(read_format,
368                                       os.read(self.events[0].fd, length))))
369
370
371 class Event(object):
372     """Represents a performance event and manages its life cycle."""
373     def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
374                  trace_filter, trace_set='kvm'):
375         self.libc = ctypes.CDLL('libc.so.6', use_errno=True)
376         self.syscall = self.libc.syscall
377         self.name = name
378         self.fd = None
379         self.setup_event(group, trace_cpu, trace_pid, trace_point,
380                          trace_filter, trace_set)
381
382     def __del__(self):
383         """Closes the event's file descriptor.
384
385         As no python file object was created for the file descriptor,
386         python will not reference count the descriptor and will not
387         close it itself automatically, so we do it.
388
389         """
390         if self.fd:
391             os.close(self.fd)
392
393     def perf_event_open(self, attr, pid, cpu, group_fd, flags):
394         """Wrapper for the sys_perf_evt_open() syscall.
395
396         Used to set up performance events, returns a file descriptor or -1
397         on error.
398
399         Attributes are:
400         - syscall number
401         - struct perf_event_attr *
402         - pid or -1 to monitor all pids
403         - cpu number or -1 to monitor all cpus
404         - The file descriptor of the group leader or -1 to create a group.
405         - flags
406
407         """
408         return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
409                             ctypes.c_int(pid), ctypes.c_int(cpu),
410                             ctypes.c_int(group_fd), ctypes.c_long(flags))
411
412     def setup_event_attribute(self, trace_set, trace_point):
413         """Returns an initialized ctype perf_event_attr struct."""
414
415         id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
416                                trace_point, 'id')
417
418         event_attr = perf_event_attr()
419         event_attr.config = int(open(id_path).read())
420         return event_attr
421
422     def setup_event(self, group, trace_cpu, trace_pid, trace_point,
423                     trace_filter, trace_set):
424         """Sets up the perf event in Linux.
425
426         Issues the syscall to register the event in the kernel and
427         then sets the optional filter.
428
429         """
430
431         event_attr = self.setup_event_attribute(trace_set, trace_point)
432
433         # First event will be group leader.
434         group_leader = -1
435
436         # All others have to pass the leader's descriptor instead.
437         if group.events:
438             group_leader = group.events[0].fd
439
440         fd = self.perf_event_open(event_attr, trace_pid,
441                                   trace_cpu, group_leader, 0)
442         if fd == -1:
443             err = ctypes.get_errno()
444             raise OSError(err, os.strerror(err),
445                           'while calling sys_perf_event_open().')
446
447         if trace_filter:
448             fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
449                         trace_filter)
450
451         self.fd = fd
452
453     def enable(self):
454         """Enables the trace event in the kernel.
455
456         Enabling the group leader makes reading counters from it and the
457         events under it possible.
458
459         """
460         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
461
462     def disable(self):
463         """Disables the trace event in the kernel.
464
465         Disabling the group leader makes reading all counters under it
466         impossible.
467
468         """
469         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
470
471     def reset(self):
472         """Resets the count of the trace event in the kernel."""
473         fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
474
475
476 class Provider(object):
477     """Encapsulates functionalities used by all providers."""
478     @staticmethod
479     def is_field_wanted(fields_filter, field):
480         """Indicate whether field is valid according to fields_filter."""
481         if not fields_filter:
482             return True
483         return re.match(fields_filter, field) is not None
484
485     @staticmethod
486     def walkdir(path):
487         """Returns os.walk() data for specified directory.
488
489         As it is only a wrapper it returns the same 3-tuple of (dirpath,
490         dirnames, filenames).
491         """
492         return next(os.walk(path))
493
494
495 class TracepointProvider(Provider):
496     """Data provider for the stats class.
497
498     Manages the events/groups from which it acquires its data.
499
500     """
501     def __init__(self, pid, fields_filter):
502         self.group_leaders = []
503         self.filters = self.get_filters()
504         self.update_fields(fields_filter)
505         self.pid = pid
506
507     @staticmethod
508     def get_filters():
509         """Returns a dict of trace events, their filter ids and
510         the values that can be filtered.
511
512         Trace events can be filtered for special values by setting a
513         filter string via an ioctl. The string normally has the format
514         identifier==value. For each filter a new event will be created, to
515         be able to distinguish the events.
516
517         """
518         filters = {}
519         filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
520         if ARCH.exit_reasons:
521             filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
522         return filters
523
524     def get_available_fields(self):
525         """Returns a list of available event's of format 'event name(filter
526         name)'.
527
528         All available events have directories under
529         /sys/kernel/debug/tracing/events/ which export information
530         about the specific event. Therefore, listing the dirs gives us
531         a list of all available events.
532
533         Some events like the vm exit reasons can be filtered for
534         specific values. To take account for that, the routine below
535         creates special fields with the following format:
536         event name(filter name)
537
538         """
539         path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
540         fields = self.walkdir(path)[1]
541         extra = []
542         for field in fields:
543             if field in self.filters:
544                 filter_name_, filter_dicts = self.filters[field]
545                 for name in filter_dicts:
546                     extra.append(field + '(' + name + ')')
547         fields += extra
548         return fields
549
550     def update_fields(self, fields_filter):
551         """Refresh fields, applying fields_filter"""
552         self.fields = [field for field in self.get_available_fields()
553                        if self.is_field_wanted(fields_filter, field)]
554
555     @staticmethod
556     def get_online_cpus():
557         """Returns a list of cpu id integers."""
558         def parse_int_list(list_string):
559             """Returns an int list from a string of comma separated integers and
560             integer ranges."""
561             integers = []
562             members = list_string.split(',')
563
564             for member in members:
565                 if '-' not in member:
566                     integers.append(int(member))
567                 else:
568                     int_range = member.split('-')
569                     integers.extend(range(int(int_range[0]),
570                                           int(int_range[1]) + 1))
571
572             return integers
573
574         with open('/sys/devices/system/cpu/online') as cpu_list:
575             cpu_string = cpu_list.readline()
576             return parse_int_list(cpu_string)
577
578     def setup_traces(self):
579         """Creates all event and group objects needed to be able to retrieve
580         data."""
581         fields = self.get_available_fields()
582         if self._pid > 0:
583             # Fetch list of all threads of the monitored pid, as qemu
584             # starts a thread for each vcpu.
585             path = os.path.join('/proc', str(self._pid), 'task')
586             groupids = self.walkdir(path)[1]
587         else:
588             groupids = self.get_online_cpus()
589
590         # The constant is needed as a buffer for python libs, std
591         # streams and other files that the script opens.
592         newlim = len(groupids) * len(fields) + 50
593         try:
594             softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
595
596             if hardlim < newlim:
597                 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
598                 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
599             else:
600                 # Raising the soft limit is sufficient.
601                 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
602
603         except ValueError:
604             sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
605
606         for groupid in groupids:
607             group = Group()
608             for name in fields:
609                 tracepoint = name
610                 tracefilter = None
611                 match = re.match(r'(.*)\((.*)\)', name)
612                 if match:
613                     tracepoint, sub = match.groups()
614                     tracefilter = ('%s==%d\0' %
615                                    (self.filters[tracepoint][0],
616                                     self.filters[tracepoint][1][sub]))
617
618                 # From perf_event_open(2):
619                 # pid > 0 and cpu == -1
620                 # This measures the specified process/thread on any CPU.
621                 #
622                 # pid == -1 and cpu >= 0
623                 # This measures all processes/threads on the specified CPU.
624                 trace_cpu = groupid if self._pid == 0 else -1
625                 trace_pid = int(groupid) if self._pid != 0 else -1
626
627                 group.add_event(Event(name=name,
628                                       group=group,
629                                       trace_cpu=trace_cpu,
630                                       trace_pid=trace_pid,
631                                       trace_point=tracepoint,
632                                       trace_filter=tracefilter))
633
634             self.group_leaders.append(group)
635
636     @property
637     def fields(self):
638         return self._fields
639
640     @fields.setter
641     def fields(self, fields):
642         """Enables/disables the (un)wanted events"""
643         self._fields = fields
644         for group in self.group_leaders:
645             for index, event in enumerate(group.events):
646                 if event.name in fields:
647                     event.reset()
648                     event.enable()
649                 else:
650                     # Do not disable the group leader.
651                     # It would disable all of its events.
652                     if index != 0:
653                         event.disable()
654
655     @property
656     def pid(self):
657         return self._pid
658
659     @pid.setter
660     def pid(self, pid):
661         """Changes the monitored pid by setting new traces."""
662         self._pid = pid
663         # The garbage collector will get rid of all Event/Group
664         # objects and open files after removing the references.
665         self.group_leaders = []
666         self.setup_traces()
667         self.fields = self._fields
668
669     def read(self, by_guest=0):
670         """Returns 'event name: current value' for all enabled events."""
671         ret = defaultdict(int)
672         for group in self.group_leaders:
673             for name, val in group.read().items():
674                 if name in self._fields:
675                     ret[name] += val
676         return ret
677
678     def reset(self):
679         """Reset all field counters"""
680         for group in self.group_leaders:
681             for event in group.events:
682                 event.reset()
683
684
685 class DebugfsProvider(Provider):
686     """Provides data from the files that KVM creates in the kvm debugfs
687     folder."""
688     def __init__(self, pid, fields_filter, include_past):
689         self.update_fields(fields_filter)
690         self._baseline = {}
691         self.do_read = True
692         self.paths = []
693         self.pid = pid
694         if include_past:
695             self.restore()
696
697     def get_available_fields(self):
698         """"Returns a list of available fields.
699
700         The fields are all available KVM debugfs files
701
702         """
703         return self.walkdir(PATH_DEBUGFS_KVM)[2]
704
705     def update_fields(self, fields_filter):
706         """Refresh fields, applying fields_filter"""
707         self._fields = [field for field in self.get_available_fields()
708                         if self.is_field_wanted(fields_filter, field)]
709
710     @property
711     def fields(self):
712         return self._fields
713
714     @fields.setter
715     def fields(self, fields):
716         self._fields = fields
717         self.reset()
718
719     @property
720     def pid(self):
721         return self._pid
722
723     @pid.setter
724     def pid(self, pid):
725         self._pid = pid
726         if pid != 0:
727             vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
728             if len(vms) == 0:
729                 self.do_read = False
730
731             self.paths = filter(lambda x: "{}-".format(pid) in x, vms)
732
733         else:
734             self.paths = []
735             self.do_read = True
736         self.reset()
737
738     def read(self, reset=0, by_guest=0):
739         """Returns a dict with format:'file name / field -> current value'.
740
741         Parameter 'reset':
742           0   plain read
743           1   reset field counts to 0
744           2   restore the original field counts
745
746         """
747         results = {}
748
749         # If no debugfs filtering support is available, then don't read.
750         if not self.do_read:
751             return results
752
753         paths = self.paths
754         if self._pid == 0:
755             paths = []
756             for entry in os.walk(PATH_DEBUGFS_KVM):
757                 for dir in entry[1]:
758                     paths.append(dir)
759         for path in paths:
760             for field in self._fields:
761                 value = self.read_field(field, path)
762                 key = path + field
763                 if reset == 1:
764                     self._baseline[key] = value
765                 if reset == 2:
766                     self._baseline[key] = 0
767                 if self._baseline.get(key, -1) == -1:
768                     self._baseline[key] = value
769                 increment = (results.get(field, 0) + value -
770                              self._baseline.get(key, 0))
771                 if by_guest:
772                     pid = key.split('-')[0]
773                     if pid in results:
774                         results[pid] += increment
775                     else:
776                         results[pid] = increment
777                 else:
778                     results[field] = increment
779
780         return results
781
782     def read_field(self, field, path):
783         """Returns the value of a single field from a specific VM."""
784         try:
785             return int(open(os.path.join(PATH_DEBUGFS_KVM,
786                                          path,
787                                          field))
788                        .read())
789         except IOError:
790             return 0
791
792     def reset(self):
793         """Reset field counters"""
794         self._baseline = {}
795         self.read(1)
796
797     def restore(self):
798         """Reset field counters"""
799         self._baseline = {}
800         self.read(2)
801
802
803 class Stats(object):
804     """Manages the data providers and the data they provide.
805
806     It is used to set filters on the provider's data and collect all
807     provider data.
808
809     """
810     def __init__(self, options):
811         self.providers = self.get_providers(options)
812         self._pid_filter = options.pid
813         self._fields_filter = options.fields
814         self.values = {}
815
816     @staticmethod
817     def get_providers(options):
818         """Returns a list of data providers depending on the passed options."""
819         providers = []
820
821         if options.debugfs:
822             providers.append(DebugfsProvider(options.pid, options.fields,
823                                              options.dbgfs_include_past))
824         if options.tracepoints or not providers:
825             providers.append(TracepointProvider(options.pid, options.fields))
826
827         return providers
828
829     def update_provider_filters(self):
830         """Propagates fields filters to providers."""
831         # As we reset the counters when updating the fields we can
832         # also clear the cache of old values.
833         self.values = {}
834         for provider in self.providers:
835             provider.update_fields(self._fields_filter)
836
837     def reset(self):
838         self.values = {}
839         for provider in self.providers:
840             provider.reset()
841
842     @property
843     def fields_filter(self):
844         return self._fields_filter
845
846     @fields_filter.setter
847     def fields_filter(self, fields_filter):
848         if fields_filter != self._fields_filter:
849             self._fields_filter = fields_filter
850             self.update_provider_filters()
851
852     @property
853     def pid_filter(self):
854         return self._pid_filter
855
856     @pid_filter.setter
857     def pid_filter(self, pid):
858         if pid != self._pid_filter:
859             self._pid_filter = pid
860             self.values = {}
861             for provider in self.providers:
862                 provider.pid = self._pid_filter
863
864     def get(self, by_guest=0):
865         """Returns a dict with field -> (value, delta to last value) of all
866         provider data."""
867         for provider in self.providers:
868             new = provider.read(by_guest=by_guest)
869             for key in new if by_guest else provider.fields:
870                 oldval = self.values.get(key, (0, 0))[0]
871                 newval = new.get(key, 0)
872                 newdelta = newval - oldval
873                 self.values[key] = (newval, newdelta)
874         return self.values
875
876     def toggle_display_guests(self, to_pid):
877         """Toggle between collection of stats by individual event and by
878         guest pid
879
880         Events reported by DebugfsProvider change when switching to/from
881         reading by guest values. Hence we have to remove the excess event
882         names from self.values.
883
884         """
885         if any(isinstance(ins, TracepointProvider) for ins in self.providers):
886             return 1
887         if to_pid:
888             for provider in self.providers:
889                 if isinstance(provider, DebugfsProvider):
890                     for key in provider.fields:
891                         if key in self.values.keys():
892                             del self.values[key]
893         else:
894             oldvals = self.values.copy()
895             for key in oldvals:
896                 if key.isdigit():
897                     del self.values[key]
898         # Update oldval (see get())
899         self.get(to_pid)
900         return 0
901
902 DELAY_DEFAULT = 3.0
903 MAX_GUEST_NAME_LEN = 48
904 MAX_REGEX_LEN = 44
905 DEFAULT_REGEX = r'^[^\(]*$'
906 SORT_DEFAULT = 0
907
908
909 class Tui(object):
910     """Instruments curses to draw a nice text ui."""
911     def __init__(self, stats):
912         self.stats = stats
913         self.screen = None
914         self._delay_initial = 0.25
915         self._delay_regular = DELAY_DEFAULT
916         self._sorting = SORT_DEFAULT
917         self._display_guests = 0
918
919     def __enter__(self):
920         """Initialises curses for later use.  Based on curses.wrapper
921            implementation from the Python standard library."""
922         self.screen = curses.initscr()
923         curses.noecho()
924         curses.cbreak()
925
926         # The try/catch works around a minor bit of
927         # over-conscientiousness in the curses module, the error
928         # return from C start_color() is ignorable.
929         try:
930             curses.start_color()
931         except curses.error:
932             pass
933
934         # Hide cursor in extra statement as some monochrome terminals
935         # might support hiding but not colors.
936         try:
937             curses.curs_set(0)
938         except curses.error:
939             pass
940
941         curses.use_default_colors()
942         return self
943
944     def __exit__(self, *exception):
945         """Resets the terminal to its normal state.  Based on curses.wrapper
946            implementation from the Python standard library."""
947         if self.screen:
948             self.screen.keypad(0)
949             curses.echo()
950             curses.nocbreak()
951             curses.endwin()
952
953     @staticmethod
954     def get_all_gnames():
955         """Returns a list of (pid, gname) tuples of all running guests"""
956         res = []
957         try:
958             child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
959                                      stdout=subprocess.PIPE)
960         except:
961             raise Exception
962         for line in child.stdout:
963             line = line.decode(ENCODING).lstrip().split(' ', 1)
964             # perform a sanity check before calling the more expensive
965             # function to possibly extract the guest name
966             if ' -name ' in line[1]:
967                 res.append((line[0], Tui.get_gname_from_pid(line[0])))
968         child.stdout.close()
969
970         return res
971
972     def print_all_gnames(self, row):
973         """Print a list of all running guests along with their pids."""
974         self.screen.addstr(row, 2, '%8s  %-60s' %
975                            ('Pid', 'Guest Name (fuzzy list, might be '
976                             'inaccurate!)'),
977                            curses.A_UNDERLINE)
978         row += 1
979         try:
980             for line in self.get_all_gnames():
981                 self.screen.addstr(row, 2, '%8s  %-60s' % (line[0], line[1]))
982                 row += 1
983                 if row >= self.screen.getmaxyx()[0]:
984                     break
985         except Exception:
986             self.screen.addstr(row + 1, 2, 'Not available')
987
988     @staticmethod
989     def get_pid_from_gname(gname):
990         """Fuzzy function to convert guest name to QEMU process pid.
991
992         Returns a list of potential pids, can be empty if no match found.
993         Throws an exception on processing errors.
994
995         """
996         pids = []
997         for line in Tui.get_all_gnames():
998             if gname == line[1]:
999                 pids.append(int(line[0]))
1000
1001         return pids
1002
1003     @staticmethod
1004     def get_gname_from_pid(pid):
1005         """Returns the guest name for a QEMU process pid.
1006
1007         Extracts the guest name from the QEMU comma line by processing the
1008         '-name' option. Will also handle names specified out of sequence.
1009
1010         """
1011         name = ''
1012         try:
1013             line = open('/proc/{}/cmdline'
1014                         .format(pid), 'r').read().split('\0')
1015             parms = line[line.index('-name') + 1].split(',')
1016             while '' in parms:
1017                 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1018                 # in # ['foo', '', 'bar'], which we revert here
1019                 idx = parms.index('')
1020                 parms[idx - 1] += ',' + parms[idx + 1]
1021                 del parms[idx:idx+2]
1022             # the '-name' switch allows for two ways to specify the guest name,
1023             # where the plain name overrides the name specified via 'guest='
1024             for arg in parms:
1025                 if '=' not in arg:
1026                     name = arg
1027                     break
1028                 if arg[:6] == 'guest=':
1029                     name = arg[6:]
1030         except (ValueError, IOError, IndexError):
1031             pass
1032
1033         return name
1034
1035     def update_drilldown(self):
1036         """Sets or removes a filter that only allows fields without braces."""
1037         if not self.stats.fields_filter:
1038             self.stats.fields_filter = DEFAULT_REGEX
1039
1040         elif self.stats.fields_filter == DEFAULT_REGEX:
1041             self.stats.fields_filter = None
1042
1043     def update_pid(self, pid):
1044         """Propagates pid selection to stats object."""
1045         self.stats.pid_filter = pid
1046
1047     def refresh_header(self, pid=None):
1048         """Refreshes the header."""
1049         if pid is None:
1050             pid = self.stats.pid_filter
1051         self.screen.erase()
1052         gname = self.get_gname_from_pid(pid)
1053         if gname:
1054             gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1055                                    if len(gname) > MAX_GUEST_NAME_LEN
1056                                    else gname))
1057         if pid > 0:
1058             self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1059                                .format(pid, gname), curses.A_BOLD)
1060         else:
1061             self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1062         if self.stats.fields_filter and self.stats.fields_filter \
1063            != DEFAULT_REGEX:
1064             regex = self.stats.fields_filter
1065             if len(regex) > MAX_REGEX_LEN:
1066                 regex = regex[:MAX_REGEX_LEN] + '...'
1067             self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1068         if self._display_guests:
1069             col_name = 'Guest Name'
1070         else:
1071             col_name = 'Event'
1072         self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1073                            (col_name, 'Total', '%Total', 'CurAvg/s'),
1074                            curses.A_STANDOUT)
1075         self.screen.addstr(4, 1, 'Collecting data...')
1076         self.screen.refresh()
1077
1078     def refresh_body(self, sleeptime):
1079         row = 3
1080         self.screen.move(row, 0)
1081         self.screen.clrtobot()
1082         stats = self.stats.get(self._display_guests)
1083
1084         def sortCurAvg(x):
1085             # sort by current events if available
1086             if stats[x][1]:
1087                 return (-stats[x][1], -stats[x][0])
1088             else:
1089                 return (0, -stats[x][0])
1090
1091         def sortTotal(x):
1092             # sort by totals
1093             return (0, -stats[x][0])
1094         total = 0.
1095         for key in stats.keys():
1096             if key.find('(') is -1:
1097                 total += stats[key][0]
1098         if self._sorting == SORT_DEFAULT:
1099             sortkey = sortCurAvg
1100         else:
1101             sortkey = sortTotal
1102         tavg = 0
1103         for key in sorted(stats.keys(), key=sortkey):
1104             if row >= self.screen.getmaxyx()[0] - 1:
1105                 break
1106             values = stats[key]
1107             if not values[0] and not values[1]:
1108                 break
1109             if values[0] is not None:
1110                 cur = int(round(values[1] / sleeptime)) if values[1] else ''
1111                 if self._display_guests:
1112                     key = self.get_gname_from_pid(key)
1113                 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
1114                                    (key, values[0], values[0] * 100 / total,
1115                                     cur))
1116                 if cur is not '' and key.find('(') is -1:
1117                     tavg += cur
1118             row += 1
1119         if row == 3:
1120             self.screen.addstr(4, 1, 'No matching events reported yet')
1121         else:
1122             self.screen.addstr(row, 1, '%-40s %10d        %8s' %
1123                                ('Total', total, tavg if tavg else ''),
1124                                curses.A_BOLD)
1125         self.screen.refresh()
1126
1127     def show_msg(self, text):
1128         """Display message centered text and exit on key press"""
1129         hint = 'Press any key to continue'
1130         curses.cbreak()
1131         self.screen.erase()
1132         (x, term_width) = self.screen.getmaxyx()
1133         row = 2
1134         for line in text:
1135             start = (term_width - len(line)) / 2
1136             self.screen.addstr(row, start, line)
1137             row += 1
1138         self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint,
1139                            curses.A_STANDOUT)
1140         self.screen.getkey()
1141
1142     def show_help_interactive(self):
1143         """Display help with list of interactive commands"""
1144         msg = ('   b     toggle events by guests (debugfs only, honors'
1145                ' filters)',
1146                '   c     clear filter',
1147                '   f     filter by regular expression',
1148                '   g     filter by guest name',
1149                '   h     display interactive commands reference',
1150                '   o     toggle sorting order (Total vs CurAvg/s)',
1151                '   p     filter by PID',
1152                '   q     quit',
1153                '   r     reset stats',
1154                '   s     set update interval',
1155                '   x     toggle reporting of stats for individual child trace'
1156                ' events',
1157                'Any other key refreshes statistics immediately')
1158         curses.cbreak()
1159         self.screen.erase()
1160         self.screen.addstr(0, 0, "Interactive commands reference",
1161                            curses.A_BOLD)
1162         self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1163         row = 4
1164         for line in msg:
1165             self.screen.addstr(row, 0, line)
1166             row += 1
1167         self.screen.getkey()
1168         self.refresh_header()
1169
1170     def show_filter_selection(self):
1171         """Draws filter selection mask.
1172
1173         Asks for a valid regex and sets the fields filter accordingly.
1174
1175         """
1176         while True:
1177             self.screen.erase()
1178             self.screen.addstr(0, 0,
1179                                "Show statistics for events matching a regex.",
1180                                curses.A_BOLD)
1181             self.screen.addstr(2, 0,
1182                                "Current regex: {0}"
1183                                .format(self.stats.fields_filter))
1184             self.screen.addstr(3, 0, "New regex: ")
1185             curses.echo()
1186             regex = self.screen.getstr().decode(ENCODING)
1187             curses.noecho()
1188             if len(regex) == 0:
1189                 self.stats.fields_filter = DEFAULT_REGEX
1190                 self.refresh_header()
1191                 return
1192             try:
1193                 re.compile(regex)
1194                 self.stats.fields_filter = regex
1195                 self.refresh_header()
1196                 return
1197             except re.error:
1198                 continue
1199
1200     def show_vm_selection_by_pid(self):
1201         """Draws PID selection mask.
1202
1203         Asks for a pid until a valid pid or 0 has been entered.
1204
1205         """
1206         msg = ''
1207         while True:
1208             self.screen.erase()
1209             self.screen.addstr(0, 0,
1210                                'Show statistics for specific pid.',
1211                                curses.A_BOLD)
1212             self.screen.addstr(1, 0,
1213                                'This might limit the shown data to the trace '
1214                                'statistics.')
1215             self.screen.addstr(5, 0, msg)
1216             self.print_all_gnames(7)
1217
1218             curses.echo()
1219             self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1220             pid = self.screen.getstr().decode(ENCODING)
1221             curses.noecho()
1222
1223             try:
1224                 if len(pid) > 0:
1225                     pid = int(pid)
1226                     if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1227                                                                    str(pid))):
1228                         msg = '"' + str(pid) + '": Not a running process'
1229                         continue
1230                 else:
1231                     pid = 0
1232                 self.refresh_header(pid)
1233                 self.update_pid(pid)
1234                 break
1235             except ValueError:
1236                 msg = '"' + str(pid) + '": Not a valid pid'
1237
1238     def show_set_update_interval(self):
1239         """Draws update interval selection mask."""
1240         msg = ''
1241         while True:
1242             self.screen.erase()
1243             self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1244                                DELAY_DEFAULT, curses.A_BOLD)
1245             self.screen.addstr(4, 0, msg)
1246             self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1247                                self._delay_regular)
1248             curses.echo()
1249             val = self.screen.getstr().decode(ENCODING)
1250             curses.noecho()
1251
1252             try:
1253                 if len(val) > 0:
1254                     delay = float(val)
1255                     if delay < 0.1:
1256                         msg = '"' + str(val) + '": Value must be >=0.1'
1257                         continue
1258                     if delay > 25.5:
1259                         msg = '"' + str(val) + '": Value must be <=25.5'
1260                         continue
1261                 else:
1262                     delay = DELAY_DEFAULT
1263                 self._delay_regular = delay
1264                 break
1265
1266             except ValueError:
1267                 msg = '"' + str(val) + '": Invalid value'
1268         self.refresh_header()
1269
1270     def show_vm_selection_by_guest_name(self):
1271         """Draws guest selection mask.
1272
1273         Asks for a guest name until a valid guest name or '' is entered.
1274
1275         """
1276         msg = ''
1277         while True:
1278             self.screen.erase()
1279             self.screen.addstr(0, 0,
1280                                'Show statistics for specific guest.',
1281                                curses.A_BOLD)
1282             self.screen.addstr(1, 0,
1283                                'This might limit the shown data to the trace '
1284                                'statistics.')
1285             self.screen.addstr(5, 0, msg)
1286             self.print_all_gnames(7)
1287             curses.echo()
1288             self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1289             gname = self.screen.getstr().decode(ENCODING)
1290             curses.noecho()
1291
1292             if not gname:
1293                 self.refresh_header(0)
1294                 self.update_pid(0)
1295                 break
1296             else:
1297                 pids = []
1298                 try:
1299                     pids = self.get_pid_from_gname(gname)
1300                 except:
1301                     msg = '"' + gname + '": Internal error while searching, ' \
1302                           'use pid filter instead'
1303                     continue
1304                 if len(pids) == 0:
1305                     msg = '"' + gname + '": Not an active guest'
1306                     continue
1307                 if len(pids) > 1:
1308                     msg = '"' + gname + '": Multiple matches found, use pid ' \
1309                           'filter instead'
1310                     continue
1311                 self.refresh_header(pids[0])
1312                 self.update_pid(pids[0])
1313                 break
1314
1315     def show_stats(self):
1316         """Refreshes the screen and processes user input."""
1317         sleeptime = self._delay_initial
1318         self.refresh_header()
1319         start = 0.0  # result based on init value never appears on screen
1320         while True:
1321             self.refresh_body(time.time() - start)
1322             curses.halfdelay(int(sleeptime * 10))
1323             start = time.time()
1324             sleeptime = self._delay_regular
1325             try:
1326                 char = self.screen.getkey()
1327                 if char == 'b':
1328                     self._display_guests = not self._display_guests
1329                     if self.stats.toggle_display_guests(self._display_guests):
1330                         self.show_msg(['Command not available with tracepoints'
1331                                        ' enabled', 'Restart with debugfs only '
1332                                        '(see option \'-d\') and try again!'])
1333                         self._display_guests = not self._display_guests
1334                     self.refresh_header()
1335                 if char == 'c':
1336                     self.stats.fields_filter = DEFAULT_REGEX
1337                     self.refresh_header(0)
1338                     self.update_pid(0)
1339                 if char == 'f':
1340                     curses.curs_set(1)
1341                     self.show_filter_selection()
1342                     curses.curs_set(0)
1343                     sleeptime = self._delay_initial
1344                 if char == 'g':
1345                     curses.curs_set(1)
1346                     self.show_vm_selection_by_guest_name()
1347                     curses.curs_set(0)
1348                     sleeptime = self._delay_initial
1349                 if char == 'h':
1350                     self.show_help_interactive()
1351                 if char == 'o':
1352                     self._sorting = not self._sorting
1353                 if char == 'p':
1354                     curses.curs_set(1)
1355                     self.show_vm_selection_by_pid()
1356                     curses.curs_set(0)
1357                     sleeptime = self._delay_initial
1358                 if char == 'q':
1359                     break
1360                 if char == 'r':
1361                     self.stats.reset()
1362                 if char == 's':
1363                     curses.curs_set(1)
1364                     self.show_set_update_interval()
1365                     curses.curs_set(0)
1366                     sleeptime = self._delay_initial
1367                 if char == 'x':
1368                     self.update_drilldown()
1369                     # prevents display of current values on next refresh
1370                     self.stats.get(self._display_guests)
1371             except KeyboardInterrupt:
1372                 break
1373             except curses.error:
1374                 continue
1375
1376
1377 def batch(stats):
1378     """Prints statistics in a key, value format."""
1379     try:
1380         s = stats.get()
1381         time.sleep(1)
1382         s = stats.get()
1383         for key in sorted(s.keys()):
1384             values = s[key]
1385             print('%-42s%10d%10d' % (key, values[0], values[1]))
1386     except KeyboardInterrupt:
1387         pass
1388
1389
1390 def log(stats):
1391     """Prints statistics as reiterating key block, multiple value blocks."""
1392     keys = sorted(stats.get().keys())
1393
1394     def banner():
1395         for k in keys:
1396             print(k, end=' ')
1397         print()
1398
1399     def statline():
1400         s = stats.get()
1401         for k in keys:
1402             print(' %9d' % s[k][1], end=' ')
1403         print()
1404     line = 0
1405     banner_repeat = 20
1406     while True:
1407         try:
1408             time.sleep(1)
1409             if line % banner_repeat == 0:
1410                 banner()
1411             statline()
1412             line += 1
1413         except KeyboardInterrupt:
1414             break
1415
1416
1417 def get_options():
1418     """Returns processed program arguments."""
1419     description_text = """
1420 This script displays various statistics about VMs running under KVM.
1421 The statistics are gathered from the KVM debugfs entries and / or the
1422 currently available perf traces.
1423
1424 The monitoring takes additional cpu cycles and might affect the VM's
1425 performance.
1426
1427 Requirements:
1428 - Access to:
1429     %s
1430     %s/events/*
1431     /proc/pid/task
1432 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1433   CAP_SYS_ADMIN and perf events are used.
1434 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1435   the large number of files that are possibly opened.
1436
1437 Interactive Commands:
1438    b     toggle events by guests (debugfs only, honors filters)
1439    c     clear filter
1440    f     filter by regular expression
1441    g     filter by guest name
1442    h     display interactive commands reference
1443    o     toggle sorting order (Total vs CurAvg/s)
1444    p     filter by PID
1445    q     quit
1446    r     reset stats
1447    s     set update interval
1448    x     toggle reporting of stats for individual child trace events
1449 Press any other key to refresh statistics immediately.
1450 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1451
1452     class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1453         def format_description(self, description):
1454             if description:
1455                 return description + "\n"
1456             else:
1457                 return ""
1458
1459     def cb_guest_to_pid(option, opt, val, parser):
1460         try:
1461             pids = Tui.get_pid_from_gname(val)
1462         except:
1463             sys.exit('Error while searching for guest "{}". Use "-p" to '
1464                      'specify a pid instead?'.format(val))
1465         if len(pids) == 0:
1466             sys.exit('Error: No guest by the name "{}" found'.format(val))
1467         if len(pids) > 1:
1468             sys.exit('Error: Multiple processes found (pids: {}). Use "-p" '
1469                      'to specify the desired pid'.format(" ".join(pids)))
1470         parser.values.pid = pids[0]
1471
1472     optparser = optparse.OptionParser(description=description_text,
1473                                       formatter=PlainHelpFormatter())
1474     optparser.add_option('-1', '--once', '--batch',
1475                          action='store_true',
1476                          default=False,
1477                          dest='once',
1478                          help='run in batch mode for one second',
1479                          )
1480     optparser.add_option('-i', '--debugfs-include-past',
1481                          action='store_true',
1482                          default=False,
1483                          dest='dbgfs_include_past',
1484                          help='include all available data on past events for '
1485                               'debugfs',
1486                          )
1487     optparser.add_option('-l', '--log',
1488                          action='store_true',
1489                          default=False,
1490                          dest='log',
1491                          help='run in logging mode (like vmstat)',
1492                          )
1493     optparser.add_option('-t', '--tracepoints',
1494                          action='store_true',
1495                          default=False,
1496                          dest='tracepoints',
1497                          help='retrieve statistics from tracepoints',
1498                          )
1499     optparser.add_option('-d', '--debugfs',
1500                          action='store_true',
1501                          default=False,
1502                          dest='debugfs',
1503                          help='retrieve statistics from debugfs',
1504                          )
1505     optparser.add_option('-f', '--fields',
1506                          action='store',
1507                          default=DEFAULT_REGEX,
1508                          dest='fields',
1509                          help='''fields to display (regex)
1510                                  "-f help" for a list of available events''',
1511                          )
1512     optparser.add_option('-p', '--pid',
1513                          action='store',
1514                          default=0,
1515                          type='int',
1516                          dest='pid',
1517                          help='restrict statistics to pid',
1518                          )
1519     optparser.add_option('-g', '--guest',
1520                          action='callback',
1521                          type='string',
1522                          dest='pid',
1523                          metavar='GUEST',
1524                          help='restrict statistics to guest by name',
1525                          callback=cb_guest_to_pid,
1526                          )
1527     options, unkn = optparser.parse_args(sys.argv)
1528     if len(unkn) != 1:
1529         sys.exit('Error: Extra argument(s): ' + ' '.join(unkn[1:]))
1530     try:
1531         # verify that we were passed a valid regex up front
1532         re.compile(options.fields)
1533     except re.error:
1534         sys.exit('Error: "' + options.fields + '" is not a valid regular '
1535                  'expression')
1536
1537     return options
1538
1539
1540 def check_access(options):
1541     """Exits if the current user can't access all needed directories."""
1542     if not os.path.exists('/sys/kernel/debug'):
1543         sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1544         sys.exit(1)
1545
1546     if not os.path.exists(PATH_DEBUGFS_KVM):
1547         sys.stderr.write("Please make sure, that debugfs is mounted and "
1548                          "readable by the current user:\n"
1549                          "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1550                          "Also ensure, that the kvm modules are loaded.\n")
1551         sys.exit(1)
1552
1553     if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1554                                                      not options.debugfs):
1555         sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1556                          "when using the option -t (default).\n"
1557                          "If it is enabled, make {0} readable by the "
1558                          "current user.\n"
1559                          .format(PATH_DEBUGFS_TRACING))
1560         if options.tracepoints:
1561             sys.exit(1)
1562
1563         sys.stderr.write("Falling back to debugfs statistics!\n")
1564         options.debugfs = True
1565         time.sleep(5)
1566
1567     return options
1568
1569
1570 def main():
1571     options = get_options()
1572     options = check_access(options)
1573
1574     if (options.pid > 0 and
1575         not os.path.isdir(os.path.join('/proc/',
1576                                        str(options.pid)))):
1577         sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1578         sys.exit('Specified pid does not exist.')
1579
1580     stats = Stats(options)
1581
1582     if options.fields == 'help':
1583         stats.fields_filter = None
1584         event_list = []
1585         for key in stats.get().keys():
1586             event_list.append(key.split('(', 1)[0])
1587         sys.stdout.write('  ' + '\n  '.join(sorted(set(event_list))) + '\n')
1588         sys.exit(0)
1589
1590     if options.log:
1591         log(stats)
1592     elif not options.once:
1593         with Tui(stats) as tui:
1594             tui.show_stats()
1595     else:
1596         batch(stats)
1597
1598 if __name__ == "__main__":
1599     main()