]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/kvm/kvm_stat/kvm_stat
mtd: nand: atmel: Relax tADL_min constraint
[mirror_ubuntu-artful-kernel.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:
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 /sys/kernel/debug/kvm
1417 /sys/kernel/debug/trace/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 """
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 )
1501 optparser.add_option('-p', '--pid',
1502 action='store',
1503 default=0,
1504 type='int',
1505 dest='pid',
1506 help='restrict statistics to pid',
1507 )
1508 optparser.add_option('-g', '--guest',
1509 action='callback',
1510 type='string',
1511 dest='pid',
1512 metavar='GUEST',
1513 help='restrict statistics to guest by name',
1514 callback=cb_guest_to_pid,
1515 )
1516 (options, _) = optparser.parse_args(sys.argv)
1517 return options
1518
1519
1520 def check_access(options):
1521 """Exits if the current user can't access all needed directories."""
1522 if not os.path.exists('/sys/kernel/debug'):
1523 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1524 sys.exit(1)
1525
1526 if not os.path.exists(PATH_DEBUGFS_KVM):
1527 sys.stderr.write("Please make sure, that debugfs is mounted and "
1528 "readable by the current user:\n"
1529 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1530 "Also ensure, that the kvm modules are loaded.\n")
1531 sys.exit(1)
1532
1533 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1534 not options.debugfs):
1535 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1536 "when using the option -t (default).\n"
1537 "If it is enabled, make {0} readable by the "
1538 "current user.\n"
1539 .format(PATH_DEBUGFS_TRACING))
1540 if options.tracepoints:
1541 sys.exit(1)
1542
1543 sys.stderr.write("Falling back to debugfs statistics!\n")
1544 options.debugfs = True
1545 time.sleep(5)
1546
1547 return options
1548
1549
1550 def main():
1551 options = get_options()
1552 options = check_access(options)
1553
1554 if (options.pid > 0 and
1555 not os.path.isdir(os.path.join('/proc/',
1556 str(options.pid)))):
1557 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1558 sys.exit('Specified pid does not exist.')
1559
1560 stats = Stats(options)
1561
1562 if options.log:
1563 log(stats)
1564 elif not options.once:
1565 with Tui(stats) as tui:
1566 tui.show_stats()
1567 else:
1568 batch(stats)
1569
1570 if __name__ == "__main__":
1571 main()