blob: 27d217a4c4c1e5407c84d533a9be008f2baf62c0 [file] [log] [blame]
Janosch Frankf9bc9e62016-05-18 13:26:21 +02001#!/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
14import curses
15import sys
16import os
17import time
18import optparse
19import ctypes
20import fcntl
21import resource
22import struct
23import re
24from collections import defaultdict
25from time import sleep
26
27VMX_EXIT_REASONS = {
28 'EXCEPTION_NMI': 0,
29 'EXTERNAL_INTERRUPT': 1,
30 'TRIPLE_FAULT': 2,
31 'PENDING_INTERRUPT': 7,
32 'NMI_WINDOW': 8,
33 'TASK_SWITCH': 9,
34 'CPUID': 10,
35 'HLT': 12,
36 'INVLPG': 14,
37 'RDPMC': 15,
38 'RDTSC': 16,
39 'VMCALL': 18,
40 'VMCLEAR': 19,
41 'VMLAUNCH': 20,
42 'VMPTRLD': 21,
43 'VMPTRST': 22,
44 'VMREAD': 23,
45 'VMRESUME': 24,
46 'VMWRITE': 25,
47 'VMOFF': 26,
48 'VMON': 27,
49 'CR_ACCESS': 28,
50 'DR_ACCESS': 29,
51 'IO_INSTRUCTION': 30,
52 'MSR_READ': 31,
53 'MSR_WRITE': 32,
54 'INVALID_STATE': 33,
55 'MWAIT_INSTRUCTION': 36,
56 'MONITOR_INSTRUCTION': 39,
57 'PAUSE_INSTRUCTION': 40,
58 'MCE_DURING_VMENTRY': 41,
59 'TPR_BELOW_THRESHOLD': 43,
60 'APIC_ACCESS': 44,
61 'EPT_VIOLATION': 48,
62 'EPT_MISCONFIG': 49,
63 'WBINVD': 54,
64 'XSETBV': 55,
65 'APIC_WRITE': 56,
66 'INVPCID': 58,
67}
68
69SVM_EXIT_REASONS = {
70 'READ_CR0': 0x000,
71 'READ_CR3': 0x003,
72 'READ_CR4': 0x004,
73 'READ_CR8': 0x008,
74 'WRITE_CR0': 0x010,
75 'WRITE_CR3': 0x013,
76 'WRITE_CR4': 0x014,
77 'WRITE_CR8': 0x018,
78 'READ_DR0': 0x020,
79 'READ_DR1': 0x021,
80 'READ_DR2': 0x022,
81 'READ_DR3': 0x023,
82 'READ_DR4': 0x024,
83 'READ_DR5': 0x025,
84 'READ_DR6': 0x026,
85 'READ_DR7': 0x027,
86 'WRITE_DR0': 0x030,
87 'WRITE_DR1': 0x031,
88 'WRITE_DR2': 0x032,
89 'WRITE_DR3': 0x033,
90 'WRITE_DR4': 0x034,
91 'WRITE_DR5': 0x035,
92 'WRITE_DR6': 0x036,
93 'WRITE_DR7': 0x037,
94 'EXCP_BASE': 0x040,
95 'INTR': 0x060,
96 'NMI': 0x061,
97 'SMI': 0x062,
98 'INIT': 0x063,
99 'VINTR': 0x064,
100 'CR0_SEL_WRITE': 0x065,
101 'IDTR_READ': 0x066,
102 'GDTR_READ': 0x067,
103 'LDTR_READ': 0x068,
104 'TR_READ': 0x069,
105 'IDTR_WRITE': 0x06a,
106 'GDTR_WRITE': 0x06b,
107 'LDTR_WRITE': 0x06c,
108 'TR_WRITE': 0x06d,
109 'RDTSC': 0x06e,
110 'RDPMC': 0x06f,
111 'PUSHF': 0x070,
112 'POPF': 0x071,
113 'CPUID': 0x072,
114 'RSM': 0x073,
115 'IRET': 0x074,
116 'SWINT': 0x075,
117 'INVD': 0x076,
118 'PAUSE': 0x077,
119 'HLT': 0x078,
120 'INVLPG': 0x079,
121 'INVLPGA': 0x07a,
122 'IOIO': 0x07b,
123 'MSR': 0x07c,
124 'TASK_SWITCH': 0x07d,
125 'FERR_FREEZE': 0x07e,
126 'SHUTDOWN': 0x07f,
127 'VMRUN': 0x080,
128 'VMMCALL': 0x081,
129 'VMLOAD': 0x082,
130 'VMSAVE': 0x083,
131 'STGI': 0x084,
132 'CLGI': 0x085,
133 'SKINIT': 0x086,
134 'RDTSCP': 0x087,
135 'ICEBP': 0x088,
136 'WBINVD': 0x089,
137 'MONITOR': 0x08a,
138 'MWAIT': 0x08b,
139 'MWAIT_COND': 0x08c,
140 'XSETBV': 0x08d,
141 'NPF': 0x400,
142}
143
144# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
145AARCH64_EXIT_REASONS = {
146 'UNKNOWN': 0x00,
147 'WFI': 0x01,
148 'CP15_32': 0x03,
149 'CP15_64': 0x04,
150 'CP14_MR': 0x05,
151 'CP14_LS': 0x06,
152 'FP_ASIMD': 0x07,
153 'CP10_ID': 0x08,
154 'CP14_64': 0x0C,
155 'ILL_ISS': 0x0E,
156 'SVC32': 0x11,
157 'HVC32': 0x12,
158 'SMC32': 0x13,
159 'SVC64': 0x15,
160 'HVC64': 0x16,
161 'SMC64': 0x17,
162 'SYS64': 0x18,
163 'IABT': 0x20,
164 'IABT_HYP': 0x21,
165 'PC_ALIGN': 0x22,
166 'DABT': 0x24,
167 'DABT_HYP': 0x25,
168 'SP_ALIGN': 0x26,
169 'FP_EXC32': 0x28,
170 'FP_EXC64': 0x2C,
171 'SERROR': 0x2F,
172 'BREAKPT': 0x30,
173 'BREAKPT_HYP': 0x31,
174 'SOFTSTP': 0x32,
175 'SOFTSTP_HYP': 0x33,
176 'WATCHPT': 0x34,
177 'WATCHPT_HYP': 0x35,
178 'BKPT32': 0x38,
179 'VECTOR32': 0x3A,
180 'BRK64': 0x3C,
181}
182
183# From include/uapi/linux/kvm.h, KVM_EXIT_xxx
184USERSPACE_EXIT_REASONS = {
185 'UNKNOWN': 0,
186 'EXCEPTION': 1,
187 'IO': 2,
188 'HYPERCALL': 3,
189 'DEBUG': 4,
190 'HLT': 5,
191 'MMIO': 6,
192 'IRQ_WINDOW_OPEN': 7,
193 'SHUTDOWN': 8,
194 'FAIL_ENTRY': 9,
195 'INTR': 10,
196 'SET_TPR': 11,
197 'TPR_ACCESS': 12,
198 'S390_SIEIC': 13,
199 'S390_RESET': 14,
200 'DCR': 15,
201 'NMI': 16,
202 'INTERNAL_ERROR': 17,
203 'OSI': 18,
204 'PAPR_HCALL': 19,
205 'S390_UCONTROL': 20,
206 'WATCHDOG': 21,
207 'S390_TSCH': 22,
208 'EPR': 23,
209 'SYSTEM_EVENT': 24,
210}
211
212IOCTL_NUMBERS = {
213 'SET_FILTER': 0x40082406,
214 'ENABLE': 0x00002400,
215 'DISABLE': 0x00002401,
216 'RESET': 0x00002403,
217}
218
219class Arch(object):
220 """Class that encapsulates global architecture specific data like
221 syscall and ioctl numbers.
222
223 """
224 @staticmethod
225 def get_arch():
226 machine = os.uname()[4]
227
228 if machine.startswith('ppc'):
229 return ArchPPC()
230 elif machine.startswith('aarch64'):
231 return ArchA64()
232 elif machine.startswith('s390'):
233 return ArchS390()
234 else:
235 # X86_64
236 for line in open('/proc/cpuinfo'):
237 if not line.startswith('flags'):
238 continue
239
240 flags = line.split()
241 if 'vmx' in flags:
242 return ArchX86(VMX_EXIT_REASONS)
243 if 'svm' in flags:
244 return ArchX86(SVM_EXIT_REASONS)
245 return
246
247class ArchX86(Arch):
248 def __init__(self, exit_reasons):
249 self.sc_perf_evt_open = 298
250 self.ioctl_numbers = IOCTL_NUMBERS
251 self.exit_reasons = exit_reasons
252
253class ArchPPC(Arch):
254 def __init__(self):
255 self.sc_perf_evt_open = 319
256 self.ioctl_numbers = IOCTL_NUMBERS
257 self.ioctl_numbers['ENABLE'] = 0x20002400
258 self.ioctl_numbers['DISABLE'] = 0x20002401
Hemant Kumarc7d4fb52016-04-19 08:54:54 +0530259 self.ioctl_numbers['RESET'] = 0x20002403
Janosch Frankf9bc9e62016-05-18 13:26:21 +0200260
261 # PPC comes in 32 and 64 bit and some generated ioctl
262 # numbers depend on the wordsize.
263 char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
264 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
Hemant Kumarc7d4fb52016-04-19 08:54:54 +0530265 self.exit_reasons = {}
Janosch Frankf9bc9e62016-05-18 13:26:21 +0200266
267class ArchA64(Arch):
268 def __init__(self):
269 self.sc_perf_evt_open = 241
270 self.ioctl_numbers = IOCTL_NUMBERS
271 self.exit_reasons = AARCH64_EXIT_REASONS
272
273class ArchS390(Arch):
274 def __init__(self):
275 self.sc_perf_evt_open = 331
276 self.ioctl_numbers = IOCTL_NUMBERS
277 self.exit_reasons = None
278
279ARCH = Arch.get_arch()
280
281
282def walkdir(path):
283 """Returns os.walk() data for specified directory.
284
285 As it is only a wrapper it returns the same 3-tuple of (dirpath,
286 dirnames, filenames).
287 """
288 return next(os.walk(path))
289
290
291def parse_int_list(list_string):
292 """Returns an int list from a string of comma separated integers and
293 integer ranges."""
294 integers = []
295 members = list_string.split(',')
296
297 for member in members:
298 if '-' not in member:
299 integers.append(int(member))
300 else:
301 int_range = member.split('-')
302 integers.extend(range(int(int_range[0]),
303 int(int_range[1]) + 1))
304
305 return integers
306
307
308def get_online_cpus():
309 with open('/sys/devices/system/cpu/online') as cpu_list:
310 cpu_string = cpu_list.readline()
311 return parse_int_list(cpu_string)
312
313
314def get_filters():
315 filters = {}
316 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
317 if ARCH.exit_reasons:
318 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
319 return filters
320
321libc = ctypes.CDLL('libc.so.6', use_errno=True)
322syscall = libc.syscall
323
324class perf_event_attr(ctypes.Structure):
325 _fields_ = [('type', ctypes.c_uint32),
326 ('size', ctypes.c_uint32),
327 ('config', ctypes.c_uint64),
328 ('sample_freq', ctypes.c_uint64),
329 ('sample_type', ctypes.c_uint64),
330 ('read_format', ctypes.c_uint64),
331 ('flags', ctypes.c_uint64),
332 ('wakeup_events', ctypes.c_uint32),
333 ('bp_type', ctypes.c_uint32),
334 ('bp_addr', ctypes.c_uint64),
335 ('bp_len', ctypes.c_uint64),
336 ]
337
338 def __init__(self):
339 super(self.__class__, self).__init__()
340 self.type = PERF_TYPE_TRACEPOINT
341 self.size = ctypes.sizeof(self)
342 self.read_format = PERF_FORMAT_GROUP
343
344def perf_event_open(attr, pid, cpu, group_fd, flags):
345 return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
346 ctypes.c_int(pid), ctypes.c_int(cpu),
347 ctypes.c_int(group_fd), ctypes.c_long(flags))
348
349PERF_TYPE_TRACEPOINT = 2
350PERF_FORMAT_GROUP = 1 << 3
351
352PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
353PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
354
355class Group(object):
356 def __init__(self):
357 self.events = []
358
359 def add_event(self, event):
360 self.events.append(event)
361
362 def read(self):
363 length = 8 * (1 + len(self.events))
364 read_format = 'xxxxxxxx' + 'Q' * len(self.events)
365 return dict(zip([event.name for event in self.events],
366 struct.unpack(read_format,
367 os.read(self.events[0].fd, length))))
368
369class Event(object):
370 def __init__(self, name, group, trace_cpu, trace_point, trace_filter,
371 trace_set='kvm'):
372 self.name = name
373 self.fd = None
374 self.setup_event(group, trace_cpu, trace_point, trace_filter,
375 trace_set)
376
377 def setup_event_attribute(self, trace_set, trace_point):
378 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
379 trace_point, 'id')
380
381 event_attr = perf_event_attr()
382 event_attr.config = int(open(id_path).read())
383 return event_attr
384
385 def setup_event(self, group, trace_cpu, trace_point, trace_filter,
386 trace_set):
387 event_attr = self.setup_event_attribute(trace_set, trace_point)
388
389 group_leader = -1
390 if group.events:
391 group_leader = group.events[0].fd
392
393 fd = perf_event_open(event_attr, -1, trace_cpu,
394 group_leader, 0)
395 if fd == -1:
396 err = ctypes.get_errno()
397 raise OSError(err, os.strerror(err),
398 'while calling sys_perf_event_open().')
399
400 if trace_filter:
401 fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
402 trace_filter)
403
404 self.fd = fd
405
406 def enable(self):
407 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
408
409 def disable(self):
410 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
411
412 def reset(self):
413 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
414
415class TracepointProvider(object):
416 def __init__(self):
417 self.group_leaders = []
418 self.filters = get_filters()
419 self._fields = self.get_available_fields()
420 self.setup_traces()
421 self.fields = self._fields
422
423 def get_available_fields(self):
424 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
425 fields = walkdir(path)[1]
426 extra = []
427 for field in fields:
428 if field in self.filters:
429 filter_name_, filter_dicts = self.filters[field]
430 for name in filter_dicts:
431 extra.append(field + '(' + name + ')')
432 fields += extra
433 return fields
434
435 def setup_traces(self):
436 cpus = get_online_cpus()
437
438 # The constant is needed as a buffer for python libs, std
439 # streams and other files that the script opens.
440 newlim = len(cpus) * len(self._fields) + 50
441 try:
442 softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
443
444 if hardlim < newlim:
445 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
446 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
447 else:
448 # Raising the soft limit is sufficient.
449 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
450
451 except ValueError:
452 sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
453
454 for cpu in cpus:
455 group = Group()
456 for name in self._fields:
457 tracepoint = name
458 tracefilter = None
459 match = re.match(r'(.*)\((.*)\)', name)
460 if match:
461 tracepoint, sub = match.groups()
462 tracefilter = ('%s==%d\0' %
463 (self.filters[tracepoint][0],
464 self.filters[tracepoint][1][sub]))
465
466 group.add_event(Event(name=name,
467 group=group,
468 trace_cpu=cpu,
469 trace_point=tracepoint,
470 trace_filter=tracefilter))
471 self.group_leaders.append(group)
472
473 def available_fields(self):
474 return self.get_available_fields()
475
476 @property
477 def fields(self):
478 return self._fields
479
480 @fields.setter
481 def fields(self, fields):
482 self._fields = fields
483 for group in self.group_leaders:
484 for index, event in enumerate(group.events):
485 if event.name in fields:
486 event.reset()
487 event.enable()
488 else:
489 # Do not disable the group leader.
490 # It would disable all of its events.
491 if index != 0:
492 event.disable()
493
494 def read(self):
495 ret = defaultdict(int)
496 for group in self.group_leaders:
497 for name, val in group.read().iteritems():
498 if name in self._fields:
499 ret[name] += val
500 return ret
501
502class DebugfsProvider(object):
503 def __init__(self):
504 self._fields = self.get_available_fields()
505
506 def get_available_fields(self):
507 return walkdir(PATH_DEBUGFS_KVM)[2]
508
509 @property
510 def fields(self):
511 return self._fields
512
513 @fields.setter
514 def fields(self, fields):
515 self._fields = fields
516
517 def read(self):
518 def val(key):
519 return int(file(PATH_DEBUGFS_KVM + '/' + key).read())
520 return dict([(key, val(key)) for key in self._fields])
521
522class Stats(object):
523 def __init__(self, providers, fields=None):
524 self.providers = providers
525 self._fields_filter = fields
526 self.values = {}
527 self.update_provider_filters()
528
529 def update_provider_filters(self):
530 def wanted(key):
531 if not self._fields_filter:
532 return True
533 return re.match(self._fields_filter, key) is not None
534
535 # As we reset the counters when updating the fields we can
536 # also clear the cache of old values.
537 self.values = {}
538 for provider in self.providers:
539 provider_fields = [key for key in provider.get_available_fields()
540 if wanted(key)]
541 provider.fields = provider_fields
542
543 @property
544 def fields_filter(self):
545 return self._fields_filter
546
547 @fields_filter.setter
548 def fields_filter(self, fields_filter):
549 self._fields_filter = fields_filter
550 self.update_provider_filters()
551
552 def get(self):
553 for provider in self.providers:
554 new = provider.read()
555 for key in provider.fields:
556 oldval = self.values.get(key, (0, 0))
557 newval = new.get(key, 0)
558 newdelta = None
559 if oldval is not None:
560 newdelta = newval - oldval[0]
561 self.values[key] = (newval, newdelta)
562 return self.values
563
564LABEL_WIDTH = 40
565NUMBER_WIDTH = 10
566
567class Tui(object):
568 def __init__(self, stats):
569 self.stats = stats
570 self.screen = None
571 self.drilldown = False
572 self.update_drilldown()
573
574 def __enter__(self):
575 """Initialises curses for later use. Based on curses.wrapper
576 implementation from the Python standard library."""
577 self.screen = curses.initscr()
578 curses.noecho()
579 curses.cbreak()
580
581 # The try/catch works around a minor bit of
582 # over-conscientiousness in the curses module, the error
583 # return from C start_color() is ignorable.
584 try:
585 curses.start_color()
586 except:
587 pass
588
589 curses.use_default_colors()
590 return self
591
592 def __exit__(self, *exception):
593 """Resets the terminal to its normal state. Based on curses.wrappre
594 implementation from the Python standard library."""
595 if self.screen:
596 self.screen.keypad(0)
597 curses.echo()
598 curses.nocbreak()
599 curses.endwin()
600
601 def update_drilldown(self):
602 if not self.stats.fields_filter:
603 self.stats.fields_filter = r'^[^\(]*$'
604
605 elif self.stats.fields_filter == r'^[^\(]*$':
606 self.stats.fields_filter = None
607
608 def refresh(self, sleeptime):
609 self.screen.erase()
610 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
611 self.screen.addstr(2, 1, 'Event')
612 self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH -
613 len('Total'), 'Total')
614 self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 -
615 len('Current'), 'Current')
616 row = 3
617 stats = self.stats.get()
618 def sortkey(x):
619 if stats[x][1]:
620 return (-stats[x][1], -stats[x][0])
621 else:
622 return (0, -stats[x][0])
623 for key in sorted(stats.keys(), key=sortkey):
624
625 if row >= self.screen.getmaxyx()[0]:
626 break
627 values = stats[key]
628 if not values[0] and not values[1]:
629 break
630 col = 1
631 self.screen.addstr(row, col, key)
632 col += LABEL_WIDTH
633 self.screen.addstr(row, col, '%10d' % (values[0],))
634 col += NUMBER_WIDTH
635 if values[1] is not None:
636 self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
637 row += 1
638 self.screen.refresh()
639
640 def show_filter_selection(self):
641 while True:
642 self.screen.erase()
643 self.screen.addstr(0, 0,
644 "Show statistics for events matching a regex.",
645 curses.A_BOLD)
646 self.screen.addstr(2, 0,
647 "Current regex: {0}"
648 .format(self.stats.fields_filter))
649 self.screen.addstr(3, 0, "New regex: ")
650 curses.echo()
651 regex = self.screen.getstr()
652 curses.noecho()
653 if len(regex) == 0:
654 return
655 try:
656 re.compile(regex)
657 self.stats.fields_filter = regex
658 return
659 except re.error:
660 continue
661
662 def show_stats(self):
663 sleeptime = 0.25
664 while True:
665 self.refresh(sleeptime)
666 curses.halfdelay(int(sleeptime * 10))
667 sleeptime = 3
668 try:
669 char = self.screen.getkey()
670 if char == 'x':
671 self.drilldown = not self.drilldown
672 self.update_drilldown()
673 if char == 'q':
674 break
675 if char == 'f':
676 self.show_filter_selection()
677 except KeyboardInterrupt:
678 break
679 except curses.error:
680 continue
681
682def batch(stats):
683 s = stats.get()
684 time.sleep(1)
685 s = stats.get()
686 for key in sorted(s.keys()):
687 values = s[key]
688 print '%-42s%10d%10d' % (key, values[0], values[1])
689
690def log(stats):
691 keys = sorted(stats.get().iterkeys())
692 def banner():
693 for k in keys:
694 print '%s' % k,
695 print
696 def statline():
697 s = stats.get()
698 for k in keys:
699 print ' %9d' % s[k][1],
700 print
701 line = 0
702 banner_repeat = 20
703 while True:
704 time.sleep(1)
705 if line % banner_repeat == 0:
706 banner()
707 statline()
708 line += 1
709
710def get_options():
711 description_text = """
712This script displays various statistics about VMs running under KVM.
713The statistics are gathered from the KVM debugfs entries and / or the
714currently available perf traces.
715
716The monitoring takes additional cpu cycles and might affect the VM's
717performance.
718
719Requirements:
720- Access to:
721 /sys/kernel/debug/kvm
722 /sys/kernel/debug/trace/events/*
723 /proc/pid/task
724- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
725 CAP_SYS_ADMIN and perf events are used.
726- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
727 the large number of files that are possibly opened.
728"""
729
730 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
731 def format_description(self, description):
732 if description:
733 return description + "\n"
734 else:
735 return ""
736
737 optparser = optparse.OptionParser(description=description_text,
738 formatter=PlainHelpFormatter())
739 optparser.add_option('-1', '--once', '--batch',
740 action='store_true',
741 default=False,
742 dest='once',
743 help='run in batch mode for one second',
744 )
745 optparser.add_option('-l', '--log',
746 action='store_true',
747 default=False,
748 dest='log',
749 help='run in logging mode (like vmstat)',
750 )
751 optparser.add_option('-t', '--tracepoints',
752 action='store_true',
753 default=False,
754 dest='tracepoints',
755 help='retrieve statistics from tracepoints',
756 )
757 optparser.add_option('-d', '--debugfs',
758 action='store_true',
759 default=False,
760 dest='debugfs',
761 help='retrieve statistics from debugfs',
762 )
763 optparser.add_option('-f', '--fields',
764 action='store',
765 default=None,
766 dest='fields',
767 help='fields to display (regex)',
768 )
769 (options, _) = optparser.parse_args(sys.argv)
770 return options
771
772def get_providers(options):
773 providers = []
774
775 if options.tracepoints:
776 providers.append(TracepointProvider())
777 if options.debugfs:
778 providers.append(DebugfsProvider())
779 if len(providers) == 0:
780 providers.append(TracepointProvider())
781
782 return providers
783
784def check_access(options):
785 if not os.path.exists('/sys/kernel/debug'):
786 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
787 sys.exit(1)
788
789 if not os.path.exists(PATH_DEBUGFS_KVM):
790 sys.stderr.write("Please make sure, that debugfs is mounted and "
791 "readable by the current user:\n"
792 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
793 "Also ensure, that the kvm modules are loaded.\n")
794 sys.exit(1)
795
796 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints
797 or not options.debugfs):
798 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
799 "when using the option -t (default).\n"
800 "If it is enabled, make {0} readable by the "
801 "current user.\n"
802 .format(PATH_DEBUGFS_TRACING))
803 if options.tracepoints:
804 sys.exit(1)
805
806 sys.stderr.write("Falling back to debugfs statistics!\n")
807 options.debugfs = True
808 sleep(5)
809
810 return options
811
812def main():
813 options = get_options()
814 options = check_access(options)
815 providers = get_providers(options)
816 stats = Stats(providers, fields=options.fields)
817
818 if options.log:
819 log(stats)
820 elif not options.once:
821 with Tui(stats) as tui:
822 tui.show_stats()
823 else:
824 batch(stats)
825
826if __name__ == "__main__":
827 main()