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