GNU Linux-libre 4.14.265-gnu1
[releases.git] / tools / kvm / kvm_stat / kvm_stat
1 #!/usr/bin/env 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
23 import curses
24 import sys
25 import os
26 import time
27 import optparse
28 import ctypes
29 import fcntl
30 import resource
31 import struct
32 import re
33 import subprocess
34 from collections import defaultdict
35
36 VMX_EXIT_REASONS = {
37     'EXCEPTION_NMI':        0,
38     'EXTERNAL_INTERRUPT':   1,
39     'TRIPLE_FAULT':         2,
40     'PENDING_INTERRUPT':    7,
41     'NMI_WINDOW':           8,
42     'TASK_SWITCH':          9,
43     'CPUID':                10,
44     'HLT':                  12,
45     'INVLPG':               14,
46     'RDPMC':                15,
47     'RDTSC':                16,
48     'VMCALL':               18,
49     'VMCLEAR':              19,
50     'VMLAUNCH':             20,
51     'VMPTRLD':              21,
52     'VMPTRST':              22,
53     'VMREAD':               23,
54     'VMRESUME':             24,
55     'VMWRITE':              25,
56     'VMOFF':                26,
57     'VMON':                 27,
58     'CR_ACCESS':            28,
59     'DR_ACCESS':            29,
60     'IO_INSTRUCTION':       30,
61     'MSR_READ':             31,
62     'MSR_WRITE':            32,
63     'INVALID_STATE':        33,
64     'MWAIT_INSTRUCTION':    36,
65     'MONITOR_INSTRUCTION':  39,
66     'PAUSE_INSTRUCTION':    40,
67     'MCE_DURING_VMENTRY':   41,
68     'TPR_BELOW_THRESHOLD':  43,
69     'APIC_ACCESS':          44,
70     'EPT_VIOLATION':        48,
71     'EPT_MISCONFIG':        49,
72     'WBINVD':               54,
73     'XSETBV':               55,
74     'APIC_WRITE':           56,
75     'INVPCID':              58,
76 }
77
78 SVM_EXIT_REASONS = {
79     'READ_CR0':       0x000,
80     'READ_CR3':       0x003,
81     'READ_CR4':       0x004,
82     'READ_CR8':       0x008,
83     'WRITE_CR0':      0x010,
84     'WRITE_CR3':      0x013,
85     'WRITE_CR4':      0x014,
86     'WRITE_CR8':      0x018,
87     'READ_DR0':       0x020,
88     'READ_DR1':       0x021,
89     'READ_DR2':       0x022,
90     'READ_DR3':       0x023,
91     'READ_DR4':       0x024,
92     'READ_DR5':       0x025,
93     'READ_DR6':       0x026,
94     'READ_DR7':       0x027,
95     'WRITE_DR0':      0x030,
96     'WRITE_DR1':      0x031,
97     'WRITE_DR2':      0x032,
98     'WRITE_DR3':      0x033,
99     'WRITE_DR4':      0x034,
100     'WRITE_DR5':      0x035,
101     'WRITE_DR6':      0x036,
102     'WRITE_DR7':      0x037,
103     'EXCP_BASE':      0x040,
104     'INTR':           0x060,
105     'NMI':            0x061,
106     'SMI':            0x062,
107     'INIT':           0x063,
108     'VINTR':          0x064,
109     'CR0_SEL_WRITE':  0x065,
110     'IDTR_READ':      0x066,
111     'GDTR_READ':      0x067,
112     'LDTR_READ':      0x068,
113     'TR_READ':        0x069,
114     'IDTR_WRITE':     0x06a,
115     'GDTR_WRITE':     0x06b,
116     'LDTR_WRITE':     0x06c,
117     'TR_WRITE':       0x06d,
118     'RDTSC':          0x06e,
119     'RDPMC':          0x06f,
120     'PUSHF':          0x070,
121     'POPF':           0x071,
122     'CPUID':          0x072,
123     'RSM':            0x073,
124     'IRET':           0x074,
125     'SWINT':          0x075,
126     'INVD':           0x076,
127     'PAUSE':          0x077,
128     'HLT':            0x078,
129     'INVLPG':         0x079,
130     'INVLPGA':        0x07a,
131     'IOIO':           0x07b,
132     'MSR':            0x07c,
133     'TASK_SWITCH':    0x07d,
134     'FERR_FREEZE':    0x07e,
135     'SHUTDOWN':       0x07f,
136     'VMRUN':          0x080,
137     'VMMCALL':        0x081,
138     'VMLOAD':         0x082,
139     'VMSAVE':         0x083,
140     'STGI':           0x084,
141     'CLGI':           0x085,
142     'SKINIT':         0x086,
143     'RDTSCP':         0x087,
144     'ICEBP':          0x088,
145     'WBINVD':         0x089,
146     'MONITOR':        0x08a,
147     'MWAIT':          0x08b,
148     'MWAIT_COND':     0x08c,
149     'XSETBV':         0x08d,
150     'NPF':            0x400,
151 }
152
153 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
154 AARCH64_EXIT_REASONS = {
155     'UNKNOWN':      0x00,
156     'WFI':          0x01,
157     'CP15_32':      0x03,
158     'CP15_64':      0x04,
159     'CP14_MR':      0x05,
160     'CP14_LS':      0x06,
161     'FP_ASIMD':     0x07,
162     'CP10_ID':      0x08,
163     'CP14_64':      0x0C,
164     'ILL_ISS':      0x0E,
165     'SVC32':        0x11,
166     'HVC32':        0x12,
167     'SMC32':        0x13,
168     'SVC64':        0x15,
169     'HVC64':        0x16,
170     'SMC64':        0x17,
171     'SYS64':        0x18,
172     'IABT':         0x20,
173     'IABT_HYP':     0x21,
174     'PC_ALIGN':     0x22,
175     'DABT':         0x24,
176     'DABT_HYP':     0x25,
177     'SP_ALIGN':     0x26,
178     'FP_EXC32':     0x28,
179     'FP_EXC64':     0x2C,
180     'SERROR':       0x2F,
181     'BREAKPT':      0x30,
182     'BREAKPT_HYP':  0x31,
183     'SOFTSTP':      0x32,
184     'SOFTSTP_HYP':  0x33,
185     'WATCHPT':      0x34,
186     'WATCHPT_HYP':  0x35,
187     'BKPT32':       0x38,
188     'VECTOR32':     0x3A,
189     'BRK64':        0x3C,
190 }
191
192 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
193 USERSPACE_EXIT_REASONS = {
194     'UNKNOWN':          0,
195     'EXCEPTION':        1,
196     'IO':               2,
197     'HYPERCALL':        3,
198     'DEBUG':            4,
199     'HLT':              5,
200     'MMIO':             6,
201     'IRQ_WINDOW_OPEN':  7,
202     'SHUTDOWN':         8,
203     'FAIL_ENTRY':       9,
204     'INTR':             10,
205     'SET_TPR':          11,
206     'TPR_ACCESS':       12,
207     'S390_SIEIC':       13,
208     'S390_RESET':       14,
209     'DCR':              15,
210     'NMI':              16,
211     'INTERNAL_ERROR':   17,
212     'OSI':              18,
213     'PAPR_HCALL':       19,
214     'S390_UCONTROL':    20,
215     'WATCHDOG':         21,
216     'S390_TSCH':        22,
217     'EPR':              23,
218     'SYSTEM_EVENT':     24,
219 }
220
221 IOCTL_NUMBERS = {
222     'SET_FILTER':  0x40082406,
223     'ENABLE':      0x00002400,
224     'DISABLE':     0x00002401,
225     'RESET':       0x00002403,
226 }
227
228
229 class Arch(object):
230     """Encapsulates global architecture specific data.
231
232     Contains the performance event open syscall and ioctl numbers, as
233     well as the VM exit reasons for the architecture it runs on.
234
235     """
236     @staticmethod
237     def get_arch():
238         machine = os.uname()[4]
239
240         if machine.startswith('ppc'):
241             return ArchPPC()
242         elif machine.startswith('aarch64'):
243             return ArchA64()
244         elif machine.startswith('s390'):
245             return ArchS390()
246         else:
247             # X86_64
248             for line in open('/proc/cpuinfo'):
249                 if not line.startswith('flags'):
250                     continue
251
252                 flags = line.split()
253                 if 'vmx' in flags:
254                     return ArchX86(VMX_EXIT_REASONS)
255                 if 'svm' in flags:
256                     return ArchX86(SVM_EXIT_REASONS)
257                 return
258
259
260 class ArchX86(Arch):
261     def __init__(self, exit_reasons):
262         self.sc_perf_evt_open = 298
263         self.ioctl_numbers = IOCTL_NUMBERS
264         self.exit_reason_field = 'exit_reason'
265         self.exit_reasons = exit_reasons
266
267
268 class ArchPPC(Arch):
269     def __init__(self):
270         self.sc_perf_evt_open = 319
271         self.ioctl_numbers = IOCTL_NUMBERS
272         self.ioctl_numbers['ENABLE'] = 0x20002400
273         self.ioctl_numbers['DISABLE'] = 0x20002401
274         self.ioctl_numbers['RESET'] = 0x20002403
275
276         # PPC comes in 32 and 64 bit and some generated ioctl
277         # numbers depend on the wordsize.
278         char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
279         self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
280         self.exit_reason_field = 'exit_nr'
281         self.exit_reasons = {}
282
283
284 class ArchA64(Arch):
285     def __init__(self):
286         self.sc_perf_evt_open = 241
287         self.ioctl_numbers = IOCTL_NUMBERS
288         self.exit_reason_field = 'esr_ec'
289         self.exit_reasons = AARCH64_EXIT_REASONS
290
291
292 class ArchS390(Arch):
293     def __init__(self):
294         self.sc_perf_evt_open = 331
295         self.ioctl_numbers = IOCTL_NUMBERS
296         self.exit_reason_field = None
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 or fields_filter == "help":
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_reason_field and ARCH.exit_reasons:
521             filters['kvm_exit'] = (ARCH.exit_reason_field, 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().iteritems():
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 = list(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 _verify_paths(self):
739         """Remove invalid paths"""
740         for path in self.paths:
741             if not os.path.exists(os.path.join(PATH_DEBUGFS_KVM, path)):
742                 self.paths.remove(path)
743                 continue
744
745     def read(self, reset=0, by_guest=0):
746         """Returns a dict with format:'file name / field -> current value'.
747
748         Parameter 'reset':
749           0   plain read
750           1   reset field counts to 0
751           2   restore the original field counts
752
753         """
754         results = {}
755
756         # If no debugfs filtering support is available, then don't read.
757         if not self.do_read:
758             return results
759         self._verify_paths()
760
761         paths = self.paths
762         if self._pid == 0:
763             paths = []
764             for entry in os.walk(PATH_DEBUGFS_KVM):
765                 for dir in entry[1]:
766                     paths.append(dir)
767         for path in paths:
768             for field in self._fields:
769                 value = self.read_field(field, path)
770                 key = path + field
771                 if reset == 1:
772                     self._baseline[key] = value
773                 if reset == 2:
774                     self._baseline[key] = 0
775                 if self._baseline.get(key, -1) == -1:
776                     self._baseline[key] = value
777                 increment = (results.get(field, 0) + value -
778                              self._baseline.get(key, 0))
779                 if by_guest:
780                     pid = key.split('-')[0]
781                     if pid in results:
782                         results[pid] += increment
783                     else:
784                         results[pid] = increment
785                 else:
786                     results[field] = increment
787
788         return results
789
790     def read_field(self, field, path):
791         """Returns the value of a single field from a specific VM."""
792         try:
793             return int(open(os.path.join(PATH_DEBUGFS_KVM,
794                                          path,
795                                          field))
796                        .read())
797         except IOError:
798             return 0
799
800     def reset(self):
801         """Reset field counters"""
802         self._baseline = {}
803         self.read(1)
804
805     def restore(self):
806         """Reset field counters"""
807         self._baseline = {}
808         self.read(2)
809
810
811 class Stats(object):
812     """Manages the data providers and the data they provide.
813
814     It is used to set filters on the provider's data and collect all
815     provider data.
816
817     """
818     def __init__(self, options):
819         self.providers = self.get_providers(options)
820         self._pid_filter = options.pid
821         self._fields_filter = options.fields
822         self.values = {}
823
824     @staticmethod
825     def get_providers(options):
826         """Returns a list of data providers depending on the passed options."""
827         providers = []
828
829         if options.debugfs:
830             providers.append(DebugfsProvider(options.pid, options.fields,
831                                              options.dbgfs_include_past))
832         if options.tracepoints or not providers:
833             providers.append(TracepointProvider(options.pid, options.fields))
834
835         return providers
836
837     def update_provider_filters(self):
838         """Propagates fields filters to providers."""
839         # As we reset the counters when updating the fields we can
840         # also clear the cache of old values.
841         self.values = {}
842         for provider in self.providers:
843             provider.update_fields(self._fields_filter)
844
845     def reset(self):
846         self.values = {}
847         for provider in self.providers:
848             provider.reset()
849
850     @property
851     def fields_filter(self):
852         return self._fields_filter
853
854     @fields_filter.setter
855     def fields_filter(self, fields_filter):
856         if fields_filter != self._fields_filter:
857             self._fields_filter = fields_filter
858             self.update_provider_filters()
859
860     @property
861     def pid_filter(self):
862         return self._pid_filter
863
864     @pid_filter.setter
865     def pid_filter(self, pid):
866         if pid != self._pid_filter:
867             self._pid_filter = pid
868             self.values = {}
869             for provider in self.providers:
870                 provider.pid = self._pid_filter
871
872     def get(self, by_guest=0):
873         """Returns a dict with field -> (value, delta to last value) of all
874         provider data."""
875         for provider in self.providers:
876             new = provider.read(by_guest=by_guest)
877             for key in new if by_guest else provider.fields:
878                 oldval = self.values.get(key, (0, 0))[0]
879                 newval = new.get(key, 0)
880                 newdelta = newval - oldval
881                 self.values[key] = (newval, newdelta)
882         return self.values
883
884     def toggle_display_guests(self, to_pid):
885         """Toggle between collection of stats by individual event and by
886         guest pid
887
888         Events reported by DebugfsProvider change when switching to/from
889         reading by guest values. Hence we have to remove the excess event
890         names from self.values.
891
892         """
893         if any(isinstance(ins, TracepointProvider) for ins in self.providers):
894             return 1
895         if to_pid:
896             for provider in self.providers:
897                 if isinstance(provider, DebugfsProvider):
898                     for key in provider.fields:
899                         if key in self.values.keys():
900                             del self.values[key]
901         else:
902             oldvals = self.values.copy()
903             for key in oldvals:
904                 if key.isdigit():
905                     del self.values[key]
906         # Update oldval (see get())
907         self.get(to_pid)
908         return 0
909
910 DELAY_DEFAULT = 3.0
911 MAX_GUEST_NAME_LEN = 48
912 MAX_REGEX_LEN = 44
913 DEFAULT_REGEX = r'^[^\(]*$'
914 SORT_DEFAULT = 0
915
916
917 class Tui(object):
918     """Instruments curses to draw a nice text ui."""
919     def __init__(self, stats):
920         self.stats = stats
921         self.screen = None
922         self._delay_initial = 0.25
923         self._delay_regular = DELAY_DEFAULT
924         self._sorting = SORT_DEFAULT
925         self._display_guests = 0
926
927     def __enter__(self):
928         """Initialises curses for later use.  Based on curses.wrapper
929            implementation from the Python standard library."""
930         self.screen = curses.initscr()
931         curses.noecho()
932         curses.cbreak()
933
934         # The try/catch works around a minor bit of
935         # over-conscientiousness in the curses module, the error
936         # return from C start_color() is ignorable.
937         try:
938             curses.start_color()
939         except curses.error:
940             pass
941
942         # Hide cursor in extra statement as some monochrome terminals
943         # might support hiding but not colors.
944         try:
945             curses.curs_set(0)
946         except curses.error:
947             pass
948
949         curses.use_default_colors()
950         return self
951
952     def __exit__(self, *exception):
953         """Resets the terminal to its normal state.  Based on curses.wrapper
954            implementation from the Python standard library."""
955         if self.screen:
956             self.screen.keypad(0)
957             curses.echo()
958             curses.nocbreak()
959             curses.endwin()
960
961     def get_all_gnames(self):
962         """Returns a list of (pid, gname) tuples of all running guests"""
963         res = []
964         try:
965             child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
966                                      stdout=subprocess.PIPE)
967         except:
968             raise Exception
969         for line in child.stdout:
970             line = line.lstrip().split(' ', 1)
971             # perform a sanity check before calling the more expensive
972             # function to possibly extract the guest name
973             if ' -name ' in line[1]:
974                 res.append((line[0], self.get_gname_from_pid(line[0])))
975         child.stdout.close()
976
977         return res
978
979     def print_all_gnames(self, row):
980         """Print a list of all running guests along with their pids."""
981         self.screen.addstr(row, 2, '%8s  %-60s' %
982                            ('Pid', 'Guest Name (fuzzy list, might be '
983                             'inaccurate!)'),
984                            curses.A_UNDERLINE)
985         row += 1
986         try:
987             for line in self.get_all_gnames():
988                 self.screen.addstr(row, 2, '%8s  %-60s' % (line[0], line[1]))
989                 row += 1
990                 if row >= self.screen.getmaxyx()[0]:
991                     break
992         except Exception:
993             self.screen.addstr(row + 1, 2, 'Not available')
994
995     def get_pid_from_gname(self, gname):
996         """Fuzzy function to convert guest name to QEMU process pid.
997
998         Returns a list of potential pids, can be empty if no match found.
999         Throws an exception on processing errors.
1000
1001         """
1002         pids = []
1003         for line in self.get_all_gnames():
1004             if gname == line[1]:
1005                 pids.append(int(line[0]))
1006
1007         return pids
1008
1009     @staticmethod
1010     def get_gname_from_pid(pid):
1011         """Returns the guest name for a QEMU process pid.
1012
1013         Extracts the guest name from the QEMU comma line by processing the
1014         '-name' option. Will also handle names specified out of sequence.
1015
1016         """
1017         name = ''
1018         try:
1019             line = open('/proc/{}/cmdline'
1020                         .format(pid), 'rb').read().split('\0')
1021             parms = line[line.index('-name') + 1].split(',')
1022             while '' in parms:
1023                 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
1024                 # in # ['foo', '', 'bar'], which we revert here
1025                 idx = parms.index('')
1026                 parms[idx - 1] += ',' + parms[idx + 1]
1027                 del parms[idx:idx+2]
1028             # the '-name' switch allows for two ways to specify the guest name,
1029             # where the plain name overrides the name specified via 'guest='
1030             for arg in parms:
1031                 if '=' not in arg:
1032                     name = arg
1033                     break
1034                 if arg[:6] == 'guest=':
1035                     name = arg[6:]
1036         except (ValueError, IOError, IndexError):
1037             pass
1038
1039         return name
1040
1041     def update_drilldown(self):
1042         """Sets or removes a filter that only allows fields without braces."""
1043         if not self.stats.fields_filter:
1044             self.stats.fields_filter = DEFAULT_REGEX
1045
1046         elif self.stats.fields_filter == DEFAULT_REGEX:
1047             self.stats.fields_filter = None
1048
1049     def update_pid(self, pid):
1050         """Propagates pid selection to stats object."""
1051         self.stats.pid_filter = pid
1052
1053     def refresh_header(self, pid=None):
1054         """Refreshes the header."""
1055         if pid is None:
1056             pid = self.stats.pid_filter
1057         self.screen.erase()
1058         gname = self.get_gname_from_pid(pid)
1059         if gname:
1060             gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
1061                                    if len(gname) > MAX_GUEST_NAME_LEN
1062                                    else gname))
1063         if pid > 0:
1064             self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1065                                .format(pid, gname), curses.A_BOLD)
1066         else:
1067             self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
1068         if self.stats.fields_filter and self.stats.fields_filter \
1069            != DEFAULT_REGEX:
1070             regex = self.stats.fields_filter
1071             if len(regex) > MAX_REGEX_LEN:
1072                 regex = regex[:MAX_REGEX_LEN] + '...'
1073             self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
1074         if self._display_guests:
1075             col_name = 'Guest Name'
1076         else:
1077             col_name = 'Event'
1078         self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1079                            (col_name, 'Total', '%Total', 'CurAvg/s'),
1080                            curses.A_STANDOUT)
1081         self.screen.addstr(4, 1, 'Collecting data...')
1082         self.screen.refresh()
1083
1084     def refresh_body(self, sleeptime):
1085         row = 3
1086         self.screen.move(row, 0)
1087         self.screen.clrtobot()
1088         stats = self.stats.get(self._display_guests)
1089
1090         def sortCurAvg(x):
1091             # sort by current events if available
1092             if stats[x][1]:
1093                 return (-stats[x][1], -stats[x][0])
1094             else:
1095                 return (0, -stats[x][0])
1096
1097         def sortTotal(x):
1098             # sort by totals
1099             return (0, -stats[x][0])
1100         total = 0.
1101         for val in stats.values():
1102             total += val[0]
1103         if self._sorting == SORT_DEFAULT:
1104             sortkey = sortCurAvg
1105         else:
1106             sortkey = sortTotal
1107         for key in sorted(stats.keys(), key=sortkey):
1108
1109             if row >= self.screen.getmaxyx()[0]:
1110                 break
1111             values = stats[key]
1112             if not values[0] and not values[1]:
1113                 break
1114             if values[0] is not None:
1115                 cur = int(round(values[1] / sleeptime)) if values[1] else ''
1116                 if self._display_guests:
1117                     key = self.get_gname_from_pid(key)
1118                 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
1119                                    (key, values[0], values[0] * 100 / total,
1120                                     cur))
1121             row += 1
1122         if row == 3:
1123             self.screen.addstr(4, 1, 'No matching events reported yet')
1124         self.screen.refresh()
1125
1126     def show_msg(self, text):
1127         """Display message centered text and exit on key press"""
1128         hint = 'Press any key to continue'
1129         curses.cbreak()
1130         self.screen.erase()
1131         (x, term_width) = self.screen.getmaxyx()
1132         row = 2
1133         for line in text:
1134             start = (term_width - len(line)) // 2
1135             self.screen.addstr(row, start, line)
1136             row += 1
1137         self.screen.addstr(row + 1, (term_width - len(hint)) // 2, hint,
1138                            curses.A_STANDOUT)
1139         self.screen.getkey()
1140
1141     def show_help_interactive(self):
1142         """Display help with list of interactive commands"""
1143         msg = ('   b     toggle events by guests (debugfs only, honors'
1144                ' filters)',
1145                '   c     clear filter',
1146                '   f     filter by regular expression',
1147                '   g     filter by guest name',
1148                '   h     display interactive commands reference',
1149                '   o     toggle sorting order (Total vs CurAvg/s)',
1150                '   p     filter by PID',
1151                '   q     quit',
1152                '   r     reset stats',
1153                '   s     set update interval',
1154                '   x     toggle reporting of stats for individual child trace'
1155                ' events',
1156                'Any other key refreshes statistics immediately')
1157         curses.cbreak()
1158         self.screen.erase()
1159         self.screen.addstr(0, 0, "Interactive commands reference",
1160                            curses.A_BOLD)
1161         self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1162         row = 4
1163         for line in msg:
1164             self.screen.addstr(row, 0, line)
1165             row += 1
1166         self.screen.getkey()
1167         self.refresh_header()
1168
1169     def show_filter_selection(self):
1170         """Draws filter selection mask.
1171
1172         Asks for a valid regex and sets the fields filter accordingly.
1173
1174         """
1175         while True:
1176             self.screen.erase()
1177             self.screen.addstr(0, 0,
1178                                "Show statistics for events matching a regex.",
1179                                curses.A_BOLD)
1180             self.screen.addstr(2, 0,
1181                                "Current regex: {0}"
1182                                .format(self.stats.fields_filter))
1183             self.screen.addstr(3, 0, "New regex: ")
1184             curses.echo()
1185             regex = self.screen.getstr()
1186             curses.noecho()
1187             if len(regex) == 0:
1188                 self.stats.fields_filter = DEFAULT_REGEX
1189                 self.refresh_header()
1190                 return
1191             try:
1192                 re.compile(regex)
1193                 self.stats.fields_filter = regex
1194                 self.refresh_header()
1195                 return
1196             except re.error:
1197                 continue
1198
1199     def show_vm_selection_by_pid(self):
1200         """Draws PID selection mask.
1201
1202         Asks for a pid until a valid pid or 0 has been entered.
1203
1204         """
1205         msg = ''
1206         while True:
1207             self.screen.erase()
1208             self.screen.addstr(0, 0,
1209                                'Show statistics for specific pid.',
1210                                curses.A_BOLD)
1211             self.screen.addstr(1, 0,
1212                                'This might limit the shown data to the trace '
1213                                'statistics.')
1214             self.screen.addstr(5, 0, msg)
1215             self.print_all_gnames(7)
1216
1217             curses.echo()
1218             self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1219             pid = self.screen.getstr()
1220             curses.noecho()
1221
1222             try:
1223                 if len(pid) > 0:
1224                     pid = int(pid)
1225                     if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1226                                                                    str(pid))):
1227                         msg = '"' + str(pid) + '": Not a running process'
1228                         continue
1229                 else:
1230                     pid = 0
1231                 self.refresh_header(pid)
1232                 self.update_pid(pid)
1233                 break
1234             except ValueError:
1235                 msg = '"' + str(pid) + '": Not a valid pid'
1236
1237     def show_set_update_interval(self):
1238         """Draws update interval selection mask."""
1239         msg = ''
1240         while True:
1241             self.screen.erase()
1242             self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1243                                DELAY_DEFAULT, curses.A_BOLD)
1244             self.screen.addstr(4, 0, msg)
1245             self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1246                                self._delay_regular)
1247             curses.echo()
1248             val = self.screen.getstr()
1249             curses.noecho()
1250
1251             try:
1252                 if len(val) > 0:
1253                     delay = float(val)
1254                     if delay < 0.1:
1255                         msg = '"' + str(val) + '": Value must be >=0.1'
1256                         continue
1257                     if delay > 25.5:
1258                         msg = '"' + str(val) + '": Value must be <=25.5'
1259                         continue
1260                 else:
1261                     delay = DELAY_DEFAULT
1262                 self._delay_regular = delay
1263                 break
1264
1265             except ValueError:
1266                 msg = '"' + str(val) + '": Invalid value'
1267         self.refresh_header()
1268
1269     def show_vm_selection_by_guest_name(self):
1270         """Draws guest selection mask.
1271
1272         Asks for a guest name until a valid guest name or '' is entered.
1273
1274         """
1275         msg = ''
1276         while True:
1277             self.screen.erase()
1278             self.screen.addstr(0, 0,
1279                                'Show statistics for specific guest.',
1280                                curses.A_BOLD)
1281             self.screen.addstr(1, 0,
1282                                'This might limit the shown data to the trace '
1283                                'statistics.')
1284             self.screen.addstr(5, 0, msg)
1285             self.print_all_gnames(7)
1286             curses.echo()
1287             self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1288             gname = self.screen.getstr()
1289             curses.noecho()
1290
1291             if not gname:
1292                 self.refresh_header(0)
1293                 self.update_pid(0)
1294                 break
1295             else:
1296                 pids = []
1297                 try:
1298                     pids = self.get_pid_from_gname(gname)
1299                 except:
1300                     msg = '"' + gname + '": Internal error while searching, ' \
1301                           'use pid filter instead'
1302                     continue
1303                 if len(pids) == 0:
1304                     msg = '"' + gname + '": Not an active guest'
1305                     continue
1306                 if len(pids) > 1:
1307                     msg = '"' + gname + '": Multiple matches found, use pid ' \
1308                           'filter instead'
1309                     continue
1310                 self.refresh_header(pids[0])
1311                 self.update_pid(pids[0])
1312                 break
1313
1314     def show_stats(self):
1315         """Refreshes the screen and processes user input."""
1316         sleeptime = self._delay_initial
1317         self.refresh_header()
1318         start = 0.0  # result based on init value never appears on screen
1319         while True:
1320             self.refresh_body(time.time() - start)
1321             curses.halfdelay(int(sleeptime * 10))
1322             start = time.time()
1323             sleeptime = self._delay_regular
1324             try:
1325                 char = self.screen.getkey()
1326                 if char == 'b':
1327                     self._display_guests = not self._display_guests
1328                     if self.stats.toggle_display_guests(self._display_guests):
1329                         self.show_msg(['Command not available with tracepoints'
1330                                        ' enabled', 'Restart with debugfs only '
1331                                        '(see option \'-d\') and try again!'])
1332                         self._display_guests = not self._display_guests
1333                     self.refresh_header()
1334                 if char == 'c':
1335                     self.stats.fields_filter = DEFAULT_REGEX
1336                     self.refresh_header(0)
1337                     self.update_pid(0)
1338                 if char == 'f':
1339                     curses.curs_set(1)
1340                     self.show_filter_selection()
1341                     curses.curs_set(0)
1342                     sleeptime = self._delay_initial
1343                 if char == 'g':
1344                     curses.curs_set(1)
1345                     self.show_vm_selection_by_guest_name()
1346                     curses.curs_set(0)
1347                     sleeptime = self._delay_initial
1348                 if char == 'h':
1349                     self.show_help_interactive()
1350                 if char == 'o':
1351                     self._sorting = not self._sorting
1352                 if char == 'p':
1353                     curses.curs_set(1)
1354                     self.show_vm_selection_by_pid()
1355                     curses.curs_set(0)
1356                     sleeptime = self._delay_initial
1357                 if char == 'q':
1358                     break
1359                 if char == 'r':
1360                     self.stats.reset()
1361                 if char == 's':
1362                     curses.curs_set(1)
1363                     self.show_set_update_interval()
1364                     curses.curs_set(0)
1365                     sleeptime = self._delay_initial
1366                 if char == 'x':
1367                     self.update_drilldown()
1368                     # prevents display of current values on next refresh
1369                     self.stats.get()
1370             except KeyboardInterrupt:
1371                 break
1372             except curses.error:
1373                 continue
1374
1375
1376 def batch(stats):
1377     """Prints statistics in a key, value format."""
1378     try:
1379         s = stats.get()
1380         time.sleep(1)
1381         s = stats.get()
1382         for key in sorted(s.keys()):
1383             values = s[key]
1384             print '%-42s%10d%10d' % (key, values[0], values[1])
1385     except KeyboardInterrupt:
1386         pass
1387
1388
1389 def log(stats):
1390     """Prints statistics as reiterating key block, multiple value blocks."""
1391     keys = sorted(stats.get().iterkeys())
1392
1393     def banner():
1394         for k in keys:
1395             print '%s' % k,
1396         print
1397
1398     def statline():
1399         s = stats.get()
1400         for k in keys:
1401             print ' %9d' % s[k][1],
1402         print
1403     line = 0
1404     banner_repeat = 20
1405     while True:
1406         try:
1407             time.sleep(1)
1408             if line % banner_repeat == 0:
1409                 banner()
1410             statline()
1411             line += 1
1412         except KeyboardInterrupt:
1413             break
1414
1415
1416 def get_options():
1417     """Returns processed program arguments."""
1418     description_text = """
1419 This script displays various statistics about VMs running under KVM.
1420 The statistics are gathered from the KVM debugfs entries and / or the
1421 currently available perf traces.
1422
1423 The monitoring takes additional cpu cycles and might affect the VM's
1424 performance.
1425
1426 Requirements:
1427 - Access to:
1428     %s
1429     %s/events/*
1430     /proc/pid/task
1431 - /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1432   CAP_SYS_ADMIN and perf events are used.
1433 - CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1434   the large number of files that are possibly opened.
1435
1436 Interactive Commands:
1437    b     toggle events by guests (debugfs only, honors filters)
1438    c     clear filter
1439    f     filter by regular expression
1440    g     filter by guest name
1441    h     display interactive commands reference
1442    o     toggle sorting order (Total vs CurAvg/s)
1443    p     filter by PID
1444    q     quit
1445    r     reset stats
1446    s     set update interval
1447    x     toggle reporting of stats for individual child trace events
1448 Press any other key to refresh statistics immediately.
1449 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
1450
1451     class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1452         def format_description(self, description):
1453             if description:
1454                 return description + "\n"
1455             else:
1456                 return ""
1457
1458     def cb_guest_to_pid(option, opt, val, parser):
1459         try:
1460             pids = Tui.get_pid_from_gname(val)
1461         except:
1462             raise optparse.OptionValueError('Error while searching for guest '
1463                                             '"{}", use "-p" to specify a pid '
1464                                             'instead'.format(val))
1465         if len(pids) == 0:
1466             raise optparse.OptionValueError('No guest by the name "{}" '
1467                                             'found'.format(val))
1468         if len(pids) > 1:
1469             raise optparse.OptionValueError('Multiple processes found (pids: '
1470                                             '{}) - use "-p" to specify a pid '
1471                                             'instead'.format(" ".join(pids)))
1472         parser.values.pid = pids[0]
1473
1474     optparser = optparse.OptionParser(description=description_text,
1475                                       formatter=PlainHelpFormatter())
1476     optparser.add_option('-1', '--once', '--batch',
1477                          action='store_true',
1478                          default=False,
1479                          dest='once',
1480                          help='run in batch mode for one second',
1481                          )
1482     optparser.add_option('-i', '--debugfs-include-past',
1483                          action='store_true',
1484                          default=False,
1485                          dest='dbgfs_include_past',
1486                          help='include all available data on past events for '
1487                               'debugfs',
1488                          )
1489     optparser.add_option('-l', '--log',
1490                          action='store_true',
1491                          default=False,
1492                          dest='log',
1493                          help='run in logging mode (like vmstat)',
1494                          )
1495     optparser.add_option('-t', '--tracepoints',
1496                          action='store_true',
1497                          default=False,
1498                          dest='tracepoints',
1499                          help='retrieve statistics from tracepoints',
1500                          )
1501     optparser.add_option('-d', '--debugfs',
1502                          action='store_true',
1503                          default=False,
1504                          dest='debugfs',
1505                          help='retrieve statistics from debugfs',
1506                          )
1507     optparser.add_option('-f', '--fields',
1508                          action='store',
1509                          default=DEFAULT_REGEX,
1510                          dest='fields',
1511                          help='''fields to display (regex)
1512                                  "-f help" for a list of available events''',
1513                          )
1514     optparser.add_option('-p', '--pid',
1515                          action='store',
1516                          default=0,
1517                          type='int',
1518                          dest='pid',
1519                          help='restrict statistics to pid',
1520                          )
1521     optparser.add_option('-g', '--guest',
1522                          action='callback',
1523                          type='string',
1524                          dest='pid',
1525                          metavar='GUEST',
1526                          help='restrict statistics to guest by name',
1527                          callback=cb_guest_to_pid,
1528                          )
1529     (options, _) = optparser.parse_args(sys.argv)
1530     return options
1531
1532
1533 def check_access(options):
1534     """Exits if the current user can't access all needed directories."""
1535     if not os.path.exists('/sys/kernel/debug'):
1536         sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1537         sys.exit(1)
1538
1539     if not os.path.exists(PATH_DEBUGFS_KVM):
1540         sys.stderr.write("Please make sure, that debugfs is mounted and "
1541                          "readable by the current user:\n"
1542                          "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1543                          "Also ensure, that the kvm modules are loaded.\n")
1544         sys.exit(1)
1545
1546     if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1547                                                      not options.debugfs):
1548         sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1549                          "when using the option -t (default).\n"
1550                          "If it is enabled, make {0} readable by the "
1551                          "current user.\n"
1552                          .format(PATH_DEBUGFS_TRACING))
1553         if options.tracepoints:
1554             sys.exit(1)
1555
1556         sys.stderr.write("Falling back to debugfs statistics!\n")
1557         options.debugfs = True
1558         time.sleep(5)
1559
1560     return options
1561
1562
1563 def main():
1564     options = get_options()
1565     options = check_access(options)
1566
1567     if (options.pid > 0 and
1568         not os.path.isdir(os.path.join('/proc/',
1569                                        str(options.pid)))):
1570         sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1571         sys.exit('Specified pid does not exist.')
1572
1573     stats = Stats(options)
1574
1575     if options.fields == "help":
1576         event_list = "\n"
1577         s = stats.get()
1578         for key in s.keys():
1579             if key.find('(') != -1:
1580                 key = key[0:key.find('(')]
1581             if event_list.find('\n' + key + '\n') == -1:
1582                 event_list += key + '\n'
1583         sys.stdout.write(event_list)
1584         return ""
1585
1586     if options.log:
1587         log(stats)
1588     elif not options.once:
1589         with Tui(stats) as tui:
1590             tui.show_stats()
1591     else:
1592         batch(stats)
1593
1594 if __name__ == "__main__":
1595     main()