]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - tools/kvm/kvm_stat/kvm_stat
tools/kvm_stat: show cursor in selection screens
[mirror_ubuntu-bionic-kernel.git] / tools / kvm / kvm_stat / kvm_stat
CommitLineData
f9bc9e65
JF
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.
fabc7128
JF
13"""The kvm_stat module outputs statistics about running KVM VMs
14
15Three 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
20The data is sampled from the KVM's debugfs entries and its perf events.
21"""
f9bc9e65
JF
22
23import curses
24import sys
25import os
26import time
27import optparse
28import ctypes
29import fcntl
30import resource
31import struct
32import re
f9ff1087 33import subprocess
f9bc9e65 34from collections import defaultdict
f9bc9e65
JF
35
36VMX_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
78SVM_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)
154AARCH64_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
193USERSPACE_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
221IOCTL_NUMBERS = {
222 'SET_FILTER': 0x40082406,
223 'ENABLE': 0x00002400,
224 'DISABLE': 0x00002401,
225 'RESET': 0x00002403,
226}
227
692c7f6d 228
f9bc9e65 229class Arch(object):
fabc7128
JF
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.
f9bc9e65
JF
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
692c7f6d 259
f9bc9e65
JF
260class 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
692c7f6d 266
f9bc9e65
JF
267class 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
c7d4fb5a 273 self.ioctl_numbers['RESET'] = 0x20002403
f9bc9e65
JF
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
c7d4fb5a 279 self.exit_reasons = {}
f9bc9e65 280
692c7f6d 281
f9bc9e65
JF
282class 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
692c7f6d 288
f9bc9e65
JF
289class 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
295ARCH = Arch.get_arch()
296
297
f9bc9e65 298class perf_event_attr(ctypes.Structure):
fabc7128
JF
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 """
f9bc9e65
JF
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
692c7f6d 326
f9bc9e65
JF
327PERF_TYPE_TRACEPOINT = 2
328PERF_FORMAT_GROUP = 1 << 3
329
330PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
331PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
332
692c7f6d 333
f9bc9e65 334class Group(object):
fabc7128
JF
335 """Represents a perf event group."""
336
f9bc9e65
JF
337 def __init__(self):
338 self.events = []
339
340 def add_event(self, event):
341 self.events.append(event)
342
343 def read(self):
fabc7128
JF
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 """
f9bc9e65
JF
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
692c7f6d 366
f9bc9e65 367class Event(object):
fabc7128 368 """Represents a performance event and manages its life cycle."""
f0cf040f
JF
369 def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
370 trace_filter, trace_set='kvm'):
099a2dfc
SR
371 self.libc = ctypes.CDLL('libc.so.6', use_errno=True)
372 self.syscall = self.libc.syscall
f9bc9e65
JF
373 self.name = name
374 self.fd = None
f0cf040f
JF
375 self.setup_event(group, trace_cpu, trace_pid, trace_point,
376 trace_filter, trace_set)
377
378 def __del__(self):
fabc7128
JF
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 """
f0cf040f
JF
386 if self.fd:
387 os.close(self.fd)
f9bc9e65 388
099a2dfc
SR
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
f9bc9e65 408 def setup_event_attribute(self, trace_set, trace_point):
fabc7128
JF
409 """Returns an initialized ctype perf_event_attr struct."""
410
f9bc9e65
JF
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
f0cf040f
JF
418 def setup_event(self, group, trace_cpu, trace_pid, trace_point,
419 trace_filter, trace_set):
fabc7128
JF
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
f9bc9e65
JF
427 event_attr = self.setup_event_attribute(trace_set, trace_point)
428
fabc7128 429 # First event will be group leader.
f9bc9e65 430 group_leader = -1
fabc7128
JF
431
432 # All others have to pass the leader's descriptor instead.
f9bc9e65
JF
433 if group.events:
434 group_leader = group.events[0].fd
435
099a2dfc
SR
436 fd = self.perf_event_open(event_attr, trace_pid,
437 trace_cpu, group_leader, 0)
f9bc9e65
JF
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):
fabc7128
JF
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 """
f9bc9e65
JF
456 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
457
458 def disable(self):
fabc7128
JF
459 """Disables the trace event in the kernel.
460
461 Disabling the group leader makes reading all counters under it
462 impossible.
463
464 """
f9bc9e65
JF
465 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
466
467 def reset(self):
fabc7128 468 """Resets the count of the trace event in the kernel."""
f9bc9e65
JF
469 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
470
692c7f6d 471
099a2dfc
SR
472class 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
491class TracepointProvider(Provider):
fabc7128
JF
492 """Data provider for the stats class.
493
494 Manages the events/groups from which it acquires its data.
495
496 """
c469117d 497 def __init__(self, pid, fields_filter):
f9bc9e65 498 self.group_leaders = []
099a2dfc 499 self.filters = self.get_filters()
c469117d
SR
500 self.update_fields(fields_filter)
501 self.pid = pid
f9bc9e65 502
099a2dfc
SR
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
f9bc9e65 520 def get_available_fields(self):
fabc7128
JF
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 """
f9bc9e65 535 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
099a2dfc 536 fields = self.walkdir(path)[1]
f9bc9e65
JF
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
c469117d
SR
546 def update_fields(self, fields_filter):
547 """Refresh fields, applying fields_filter"""
548 self._fields = [field for field in self.get_available_fields()
099a2dfc
SR
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)
c469117d 573
f9bc9e65 574 def setup_traces(self):
fabc7128
JF
575 """Creates all event and group objects needed to be able to retrieve
576 data."""
a1836069 577 fields = self.get_available_fields()
f0cf040f
JF
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')
099a2dfc 582 groupids = self.walkdir(path)[1]
f0cf040f 583 else:
099a2dfc 584 groupids = self.get_online_cpus()
f9bc9e65
JF
585
586 # The constant is needed as a buffer for python libs, std
587 # streams and other files that the script opens.
a1836069 588 newlim = len(groupids) * len(fields) + 50
f9bc9e65
JF
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
f0cf040f 602 for groupid in groupids:
f9bc9e65 603 group = Group()
a1836069 604 for name in fields:
f9bc9e65
JF
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
f0cf040f
JF
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
f9bc9e65
JF
623 group.add_event(Event(name=name,
624 group=group,
f0cf040f
JF
625 trace_cpu=trace_cpu,
626 trace_pid=trace_pid,
f9bc9e65
JF
627 trace_point=tracepoint,
628 trace_filter=tracefilter))
f0cf040f 629
f9bc9e65
JF
630 self.group_leaders.append(group)
631
f9bc9e65
JF
632 @property
633 def fields(self):
634 return self._fields
635
636 @fields.setter
637 def fields(self, fields):
fabc7128 638 """Enables/disables the (un)wanted events"""
f9bc9e65
JF
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
f0cf040f
JF
651 @property
652 def pid(self):
653 return self._pid
654
655 @pid.setter
656 def pid(self, pid):
fabc7128 657 """Changes the monitored pid by setting new traces."""
f0cf040f 658 self._pid = pid
fabc7128
JF
659 # The garbage collector will get rid of all Event/Group
660 # objects and open files after removing the references.
f0cf040f
JF
661 self.group_leaders = []
662 self.setup_traces()
663 self.fields = self._fields
664
f9bc9e65 665 def read(self):
fabc7128 666 """Returns 'event name: current value' for all enabled events."""
f9bc9e65
JF
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
9f114a03
SR
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
692c7f6d 680
099a2dfc 681class DebugfsProvider(Provider):
fabc7128
JF
682 """Provides data from the files that KVM creates in the kvm debugfs
683 folder."""
c469117d
SR
684 def __init__(self, pid, fields_filter):
685 self.update_fields(fields_filter)
9f114a03 686 self._baseline = {}
f0cf040f 687 self.do_read = True
e0ba3876 688 self.paths = []
c469117d 689 self.pid = pid
f9bc9e65
JF
690
691 def get_available_fields(self):
fabc7128
JF
692 """"Returns a list of available fields.
693
694 The fields are all available KVM debugfs files
695
696 """
099a2dfc 697 return self.walkdir(PATH_DEBUGFS_KVM)[2]
f9bc9e65 698
c469117d
SR
699 def update_fields(self, fields_filter):
700 """Refresh fields, applying fields_filter"""
701 self._fields = [field for field in self.get_available_fields()
099a2dfc 702 if self.is_field_wanted(fields_filter, field)]
c469117d 703
f9bc9e65
JF
704 @property
705 def fields(self):
706 return self._fields
707
708 @fields.setter
709 def fields(self, fields):
710 self._fields = fields
9f114a03 711 self.reset()
f9bc9e65 712
f0cf040f
JF
713 @property
714 def pid(self):
715 return self._pid
716
717 @pid.setter
718 def pid(self, pid):
c469117d 719 self._pid = pid
f0cf040f 720 if pid != 0:
099a2dfc 721 vms = self.walkdir(PATH_DEBUGFS_KVM)[1]
f0cf040f
JF
722 if len(vms) == 0:
723 self.do_read = False
724
725 self.paths = filter(lambda x: "{}-".format(pid) in x, vms)
726
727 else:
9f114a03 728 self.paths = []
f0cf040f 729 self.do_read = True
9f114a03 730 self.reset()
f0cf040f 731
9f114a03 732 def read(self, reset=0):
f0cf040f
JF
733 """Returns a dict with format:'file name / field -> current value'."""
734 results = {}
735
736 # If no debugfs filtering support is available, then don't read.
737 if not self.do_read:
738 return results
739
9f114a03
SR
740 paths = self.paths
741 if self._pid == 0:
742 paths = []
743 for entry in os.walk(PATH_DEBUGFS_KVM):
744 for dir in entry[1]:
745 paths.append(dir)
746 for path in paths:
f0cf040f 747 for field in self._fields:
9f114a03
SR
748 value = self.read_field(field, path)
749 key = path + field
750 if reset:
751 self._baseline[key] = value
752 if self._baseline.get(key, -1) == -1:
753 self._baseline[key] = value
754 results[field] = (results.get(field, 0) + value -
755 self._baseline.get(key, 0))
f0cf040f
JF
756
757 return results
758
759 def read_field(self, field, path):
760 """Returns the value of a single field from a specific VM."""
761 try:
762 return int(open(os.path.join(PATH_DEBUGFS_KVM,
763 path,
764 field))
765 .read())
766 except IOError:
767 return 0
f9bc9e65 768
9f114a03
SR
769 def reset(self):
770 """Reset field counters"""
771 self._baseline = {}
772 self.read(1)
773
692c7f6d 774
f9bc9e65 775class Stats(object):
fabc7128
JF
776 """Manages the data providers and the data they provide.
777
778 It is used to set filters on the provider's data and collect all
779 provider data.
780
781 """
c469117d 782 def __init__(self, options):
099a2dfc 783 self.providers = self.get_providers(options)
c469117d
SR
784 self._pid_filter = options.pid
785 self._fields_filter = options.fields
f9bc9e65 786 self.values = {}
f9bc9e65 787
099a2dfc
SR
788 @staticmethod
789 def get_providers(options):
790 """Returns a list of data providers depending on the passed options."""
791 providers = []
792
793 if options.debugfs:
794 providers.append(DebugfsProvider(options.pid, options.fields))
795 if options.tracepoints or not providers:
796 providers.append(TracepointProvider(options.pid, options.fields))
797
798 return providers
799
f9bc9e65 800 def update_provider_filters(self):
fabc7128 801 """Propagates fields filters to providers."""
f9bc9e65
JF
802 # As we reset the counters when updating the fields we can
803 # also clear the cache of old values.
804 self.values = {}
805 for provider in self.providers:
c469117d 806 provider.update_fields(self._fields_filter)
f0cf040f 807
9f114a03
SR
808 def reset(self):
809 self.values = {}
810 for provider in self.providers:
811 provider.reset()
812
f9bc9e65
JF
813 @property
814 def fields_filter(self):
815 return self._fields_filter
816
817 @fields_filter.setter
818 def fields_filter(self, fields_filter):
9f114a03
SR
819 if fields_filter != self._fields_filter:
820 self._fields_filter = fields_filter
821 self.update_provider_filters()
f9bc9e65 822
f0cf040f
JF
823 @property
824 def pid_filter(self):
825 return self._pid_filter
826
827 @pid_filter.setter
828 def pid_filter(self, pid):
9f114a03
SR
829 if pid != self._pid_filter:
830 self._pid_filter = pid
831 self.values = {}
c469117d
SR
832 for provider in self.providers:
833 provider.pid = self._pid_filter
f0cf040f 834
f9bc9e65 835 def get(self):
fabc7128
JF
836 """Returns a dict with field -> (value, delta to last value) of all
837 provider data."""
f9bc9e65
JF
838 for provider in self.providers:
839 new = provider.read()
840 for key in provider.fields:
9f114a03 841 oldval = self.values.get(key, (0, 0))[0]
f9bc9e65 842 newval = new.get(key, 0)
9f114a03 843 newdelta = newval - oldval
f9bc9e65
JF
844 self.values[key] = (newval, newdelta)
845 return self.values
846
184b2d23
SR
847DELAY_INITIAL = 0.25
848DELAY_REGULAR = 3.0
a24e85f6 849MAX_GUEST_NAME_LEN = 48
72187dfa 850MAX_REGEX_LEN = 44
4443084f 851DEFAULT_REGEX = r'^[^\(]*$'
f9bc9e65 852
692c7f6d 853
f9bc9e65 854class Tui(object):
fabc7128 855 """Instruments curses to draw a nice text ui."""
f9bc9e65
JF
856 def __init__(self, stats):
857 self.stats = stats
858 self.screen = None
f9bc9e65
JF
859
860 def __enter__(self):
861 """Initialises curses for later use. Based on curses.wrapper
862 implementation from the Python standard library."""
863 self.screen = curses.initscr()
864 curses.noecho()
865 curses.cbreak()
866
867 # The try/catch works around a minor bit of
868 # over-conscientiousness in the curses module, the error
869 # return from C start_color() is ignorable.
870 try:
871 curses.start_color()
9fc0adfc 872 except curses.error:
f9bc9e65
JF
873 pass
874
a0b4e6a0
SR
875 # Hide cursor in extra statement as some monochrome terminals
876 # might support hiding but not colors.
877 try:
878 curses.curs_set(0)
879 except curses.error:
880 pass
881
f9bc9e65
JF
882 curses.use_default_colors()
883 return self
884
885 def __exit__(self, *exception):
773bffee 886 """Resets the terminal to its normal state. Based on curses.wrapper
f9bc9e65
JF
887 implementation from the Python standard library."""
888 if self.screen:
889 self.screen.keypad(0)
890 curses.echo()
891 curses.nocbreak()
892 curses.endwin()
893
099a2dfc
SR
894 @staticmethod
895 def get_pid_from_gname(gname):
896 """Fuzzy function to convert guest name to QEMU process pid.
897
898 Returns a list of potential pids, can be empty if no match found.
899 Throws an exception on processing errors.
900
901 """
902 pids = []
903 try:
904 child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
905 stdout=subprocess.PIPE)
906 except:
907 raise Exception
908 for line in child.stdout:
909 line = line.lstrip().split(' ', 1)
910 # perform a sanity check before calling the more expensive
911 # function to possibly extract the guest name
912 if (' -name ' in line[1] and
913 gname == self.get_gname_from_pid(line[0])):
914 pids.append(int(line[0]))
915 child.stdout.close()
916
917 return pids
918
919 @staticmethod
920 def get_gname_from_pid(pid):
921 """Returns the guest name for a QEMU process pid.
922
923 Extracts the guest name from the QEMU comma line by processing the
924 '-name' option. Will also handle names specified out of sequence.
925
926 """
927 name = ''
928 try:
929 line = open('/proc/{}/cmdline'
930 .format(pid), 'rb').read().split('\0')
931 parms = line[line.index('-name') + 1].split(',')
932 while '' in parms:
933 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
934 # in # ['foo', '', 'bar'], which we revert here
935 idx = parms.index('')
936 parms[idx - 1] += ',' + parms[idx + 1]
937 del parms[idx:idx+2]
938 # the '-name' switch allows for two ways to specify the guest name,
939 # where the plain name overrides the name specified via 'guest='
940 for arg in parms:
941 if '=' not in arg:
942 name = arg
943 break
944 if arg[:6] == 'guest=':
945 name = arg[6:]
946 except (ValueError, IOError, IndexError):
947 pass
948
949 return name
950
f9bc9e65 951 def update_drilldown(self):
fabc7128 952 """Sets or removes a filter that only allows fields without braces."""
f9bc9e65 953 if not self.stats.fields_filter:
4443084f 954 self.stats.fields_filter = DEFAULT_REGEX
f9bc9e65 955
4443084f 956 elif self.stats.fields_filter == DEFAULT_REGEX:
f9bc9e65
JF
957 self.stats.fields_filter = None
958
f0cf040f 959 def update_pid(self, pid):
fabc7128 960 """Propagates pid selection to stats object."""
f0cf040f
JF
961 self.stats.pid_filter = pid
962
184b2d23
SR
963 def refresh_header(self, pid=None):
964 """Refreshes the header."""
965 if pid is None:
966 pid = self.stats.pid_filter
f9bc9e65 967 self.screen.erase()
099a2dfc 968 gname = self.get_gname_from_pid(pid)
a24e85f6
SR
969 if gname:
970 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
971 if len(gname) > MAX_GUEST_NAME_LEN
972 else gname))
184b2d23 973 if pid > 0:
a24e85f6
SR
974 self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
975 .format(pid, gname), curses.A_BOLD)
f0cf040f
JF
976 else:
977 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
4443084f
SR
978 if self.stats.fields_filter and self.stats.fields_filter \
979 != DEFAULT_REGEX:
72187dfa
SR
980 regex = self.stats.fields_filter
981 if len(regex) > MAX_REGEX_LEN:
982 regex = regex[:MAX_REGEX_LEN] + '...'
983 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
5a7d11f8
SR
984 self.screen.addstr(2, 1, '%-40s %10s%7s %7s' %
985 ('Event', 'Total', '%Total', 'Current'))
184b2d23
SR
986 self.screen.addstr(4, 1, 'Collecting data...')
987 self.screen.refresh()
988
989 def refresh_body(self, sleeptime):
f9bc9e65 990 row = 3
184b2d23
SR
991 self.screen.move(row, 0)
992 self.screen.clrtobot()
f9bc9e65 993 stats = self.stats.get()
692c7f6d 994
f9bc9e65
JF
995 def sortkey(x):
996 if stats[x][1]:
997 return (-stats[x][1], -stats[x][0])
998 else:
999 return (0, -stats[x][0])
e55fe3cc
SR
1000 total = 0.
1001 for val in stats.values():
1002 total += val[0]
f9bc9e65
JF
1003 for key in sorted(stats.keys(), key=sortkey):
1004
1005 if row >= self.screen.getmaxyx()[0]:
1006 break
1007 values = stats[key]
1008 if not values[0] and not values[1]:
1009 break
5a7d11f8
SR
1010 if values[0] is not None:
1011 cur = int(round(values[1] / sleeptime)) if values[1] else ''
1012 self.screen.addstr(row, 1, '%-40s %10d%7.1f %7s' %
1013 (key, values[0], values[0] * 100 / total,
1014 cur))
f9bc9e65
JF
1015 row += 1
1016 self.screen.refresh()
1017
1018 def show_filter_selection(self):
fabc7128
JF
1019 """Draws filter selection mask.
1020
1021 Asks for a valid regex and sets the fields filter accordingly.
1022
1023 """
f9bc9e65
JF
1024 while True:
1025 self.screen.erase()
1026 self.screen.addstr(0, 0,
1027 "Show statistics for events matching a regex.",
1028 curses.A_BOLD)
1029 self.screen.addstr(2, 0,
1030 "Current regex: {0}"
1031 .format(self.stats.fields_filter))
1032 self.screen.addstr(3, 0, "New regex: ")
1033 curses.echo()
1034 regex = self.screen.getstr()
1035 curses.noecho()
1036 if len(regex) == 0:
4443084f 1037 self.stats.fields_filter = DEFAULT_REGEX
184b2d23 1038 self.refresh_header()
f9bc9e65
JF
1039 return
1040 try:
1041 re.compile(regex)
1042 self.stats.fields_filter = regex
184b2d23 1043 self.refresh_header()
f9bc9e65
JF
1044 return
1045 except re.error:
1046 continue
1047
f9ff1087 1048 def show_vm_selection_by_pid(self):
fabc7128
JF
1049 """Draws PID selection mask.
1050
1051 Asks for a pid until a valid pid or 0 has been entered.
1052
1053 """
0152c20f 1054 msg = ''
f0cf040f
JF
1055 while True:
1056 self.screen.erase()
1057 self.screen.addstr(0, 0,
1058 'Show statistics for specific pid.',
1059 curses.A_BOLD)
1060 self.screen.addstr(1, 0,
1061 'This might limit the shown data to the trace '
1062 'statistics.')
0152c20f 1063 self.screen.addstr(5, 0, msg)
f0cf040f
JF
1064
1065 curses.echo()
1066 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1067 pid = self.screen.getstr()
1068 curses.noecho()
1069
1070 try:
be03ea3b
SR
1071 if len(pid) > 0:
1072 pid = int(pid)
1073 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1074 str(pid))):
0152c20f 1075 msg = '"' + str(pid) + '": Not a running process'
be03ea3b
SR
1076 continue
1077 else:
1078 pid = 0
184b2d23
SR
1079 self.refresh_header(pid)
1080 self.update_pid(pid)
1081 break
f0cf040f
JF
1082
1083 except ValueError:
0152c20f 1084 msg = '"' + str(pid) + '": Not a valid pid'
f0cf040f 1085
f9ff1087
SR
1086 def show_vm_selection_by_guest_name(self):
1087 """Draws guest selection mask.
1088
1089 Asks for a guest name until a valid guest name or '' is entered.
1090
1091 """
1092 msg = ''
1093 while True:
1094 self.screen.erase()
1095 self.screen.addstr(0, 0,
1096 'Show statistics for specific guest.',
1097 curses.A_BOLD)
1098 self.screen.addstr(1, 0,
1099 'This might limit the shown data to the trace '
1100 'statistics.')
1101 self.screen.addstr(5, 0, msg)
1102 curses.echo()
1103 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1104 gname = self.screen.getstr()
1105 curses.noecho()
1106
1107 if not gname:
1108 self.refresh_header(0)
1109 self.update_pid(0)
1110 break
1111 else:
1112 pids = []
1113 try:
099a2dfc 1114 pids = self.get_pid_from_gname(gname)
f9ff1087
SR
1115 except:
1116 msg = '"' + gname + '": Internal error while searching, ' \
1117 'use pid filter instead'
1118 continue
1119 if len(pids) == 0:
1120 msg = '"' + gname + '": Not an active guest'
1121 continue
1122 if len(pids) > 1:
1123 msg = '"' + gname + '": Multiple matches found, use pid ' \
1124 'filter instead'
1125 continue
1126 self.refresh_header(pids[0])
1127 self.update_pid(pids[0])
1128 break
1129
f9bc9e65 1130 def show_stats(self):
fabc7128 1131 """Refreshes the screen and processes user input."""
184b2d23
SR
1132 sleeptime = DELAY_INITIAL
1133 self.refresh_header()
124c2fc9 1134 start = 0.0 # result based on init value never appears on screen
f9bc9e65 1135 while True:
124c2fc9 1136 self.refresh_body(time.time() - start)
f9bc9e65 1137 curses.halfdelay(int(sleeptime * 10))
124c2fc9 1138 start = time.time()
184b2d23 1139 sleeptime = DELAY_REGULAR
f9bc9e65
JF
1140 try:
1141 char = self.screen.getkey()
1142 if char == 'x':
f9bc9e65
JF
1143 self.update_drilldown()
1144 if char == 'q':
1145 break
4443084f
SR
1146 if char == 'c':
1147 self.stats.fields_filter = DEFAULT_REGEX
1148 self.refresh_header(0)
1149 self.update_pid(0)
f9bc9e65 1150 if char == 'f':
62d1b6cc 1151 curses.curs_set(1)
f9bc9e65 1152 self.show_filter_selection()
62d1b6cc 1153 curses.curs_set(0)
184b2d23 1154 sleeptime = DELAY_INITIAL
f9ff1087 1155 if char == 'g':
62d1b6cc 1156 curses.curs_set(1)
f9ff1087 1157 self.show_vm_selection_by_guest_name()
62d1b6cc 1158 curses.curs_set(0)
f9ff1087 1159 sleeptime = DELAY_INITIAL
f0cf040f 1160 if char == 'p':
62d1b6cc 1161 curses.curs_set(1)
f9ff1087 1162 self.show_vm_selection_by_pid()
62d1b6cc 1163 curses.curs_set(0)
184b2d23 1164 sleeptime = DELAY_INITIAL
9f114a03 1165 if char == 'r':
9f114a03 1166 self.stats.reset()
f9bc9e65
JF
1167 except KeyboardInterrupt:
1168 break
1169 except curses.error:
1170 continue
1171
692c7f6d 1172
f9bc9e65 1173def batch(stats):
fabc7128 1174 """Prints statistics in a key, value format."""
dadf1e78
SR
1175 try:
1176 s = stats.get()
1177 time.sleep(1)
1178 s = stats.get()
1179 for key in sorted(s.keys()):
1180 values = s[key]
1181 print '%-42s%10d%10d' % (key, values[0], values[1])
1182 except KeyboardInterrupt:
1183 pass
f9bc9e65 1184
692c7f6d 1185
f9bc9e65 1186def log(stats):
fabc7128 1187 """Prints statistics as reiterating key block, multiple value blocks."""
f9bc9e65 1188 keys = sorted(stats.get().iterkeys())
692c7f6d 1189
f9bc9e65
JF
1190 def banner():
1191 for k in keys:
1192 print '%s' % k,
1193 print
692c7f6d 1194
f9bc9e65
JF
1195 def statline():
1196 s = stats.get()
1197 for k in keys:
1198 print ' %9d' % s[k][1],
1199 print
1200 line = 0
1201 banner_repeat = 20
1202 while True:
dadf1e78
SR
1203 try:
1204 time.sleep(1)
1205 if line % banner_repeat == 0:
1206 banner()
1207 statline()
1208 line += 1
1209 except KeyboardInterrupt:
1210 break
f9bc9e65 1211
692c7f6d 1212
f9bc9e65 1213def get_options():
fabc7128 1214 """Returns processed program arguments."""
f9bc9e65
JF
1215 description_text = """
1216This script displays various statistics about VMs running under KVM.
1217The statistics are gathered from the KVM debugfs entries and / or the
1218currently available perf traces.
1219
1220The monitoring takes additional cpu cycles and might affect the VM's
1221performance.
1222
1223Requirements:
1224- Access to:
1225 /sys/kernel/debug/kvm
1226 /sys/kernel/debug/trace/events/*
1227 /proc/pid/task
1228- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1229 CAP_SYS_ADMIN and perf events are used.
1230- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1231 the large number of files that are possibly opened.
1eaa2f90
SR
1232
1233Interactive Commands:
4443084f 1234 c clear filter
1eaa2f90 1235 f filter by regular expression
f9ff1087 1236 g filter by guest name
1eaa2f90
SR
1237 p filter by PID
1238 q quit
1239 x toggle reporting of stats for individual child trace events
9f114a03 1240 r reset stats
1eaa2f90 1241Press any other key to refresh statistics immediately.
f9bc9e65
JF
1242"""
1243
1244 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1245 def format_description(self, description):
1246 if description:
1247 return description + "\n"
1248 else:
1249 return ""
1250
f9ff1087
SR
1251 def cb_guest_to_pid(option, opt, val, parser):
1252 try:
099a2dfc 1253 pids = Tui.get_pid_from_gname(val)
f9ff1087
SR
1254 except:
1255 raise optparse.OptionValueError('Error while searching for guest '
1256 '"{}", use "-p" to specify a pid '
1257 'instead'.format(val))
1258 if len(pids) == 0:
1259 raise optparse.OptionValueError('No guest by the name "{}" '
1260 'found'.format(val))
1261 if len(pids) > 1:
1262 raise optparse.OptionValueError('Multiple processes found (pids: '
1263 '{}) - use "-p" to specify a pid '
1264 'instead'.format(" ".join(pids)))
1265 parser.values.pid = pids[0]
1266
f9bc9e65
JF
1267 optparser = optparse.OptionParser(description=description_text,
1268 formatter=PlainHelpFormatter())
1269 optparser.add_option('-1', '--once', '--batch',
1270 action='store_true',
1271 default=False,
1272 dest='once',
1273 help='run in batch mode for one second',
1274 )
1275 optparser.add_option('-l', '--log',
1276 action='store_true',
1277 default=False,
1278 dest='log',
1279 help='run in logging mode (like vmstat)',
1280 )
1281 optparser.add_option('-t', '--tracepoints',
1282 action='store_true',
1283 default=False,
1284 dest='tracepoints',
1285 help='retrieve statistics from tracepoints',
1286 )
1287 optparser.add_option('-d', '--debugfs',
1288 action='store_true',
1289 default=False,
1290 dest='debugfs',
1291 help='retrieve statistics from debugfs',
1292 )
1293 optparser.add_option('-f', '--fields',
1294 action='store',
c469117d 1295 default=DEFAULT_REGEX,
f9bc9e65
JF
1296 dest='fields',
1297 help='fields to display (regex)',
1298 )
f0cf040f 1299 optparser.add_option('-p', '--pid',
e0ba3876
SR
1300 action='store',
1301 default=0,
1302 type='int',
1303 dest='pid',
1304 help='restrict statistics to pid',
1305 )
f9ff1087
SR
1306 optparser.add_option('-g', '--guest',
1307 action='callback',
1308 type='string',
1309 dest='pid',
1310 metavar='GUEST',
1311 help='restrict statistics to guest by name',
1312 callback=cb_guest_to_pid,
1313 )
f9bc9e65
JF
1314 (options, _) = optparser.parse_args(sys.argv)
1315 return options
1316
692c7f6d 1317
f9bc9e65 1318def check_access(options):
fabc7128 1319 """Exits if the current user can't access all needed directories."""
f9bc9e65
JF
1320 if not os.path.exists('/sys/kernel/debug'):
1321 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1322 sys.exit(1)
1323
1324 if not os.path.exists(PATH_DEBUGFS_KVM):
1325 sys.stderr.write("Please make sure, that debugfs is mounted and "
1326 "readable by the current user:\n"
1327 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1328 "Also ensure, that the kvm modules are loaded.\n")
1329 sys.exit(1)
1330
e0ba3876
SR
1331 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1332 not options.debugfs):
f9bc9e65
JF
1333 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1334 "when using the option -t (default).\n"
1335 "If it is enabled, make {0} readable by the "
1336 "current user.\n"
1337 .format(PATH_DEBUGFS_TRACING))
1338 if options.tracepoints:
1339 sys.exit(1)
1340
1341 sys.stderr.write("Falling back to debugfs statistics!\n")
1342 options.debugfs = True
e0ba3876 1343 time.sleep(5)
f9bc9e65
JF
1344
1345 return options
1346
692c7f6d 1347
f9bc9e65
JF
1348def main():
1349 options = get_options()
1350 options = check_access(options)
f0cf040f
JF
1351
1352 if (options.pid > 0 and
1353 not os.path.isdir(os.path.join('/proc/',
1354 str(options.pid)))):
1355 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1356 sys.exit('Specified pid does not exist.')
1357
c469117d 1358 stats = Stats(options)
f9bc9e65
JF
1359
1360 if options.log:
1361 log(stats)
1362 elif not options.once:
1363 with Tui(stats) as tui:
1364 tui.show_stats()
1365 else:
1366 batch(stats)
1367
1368if __name__ == "__main__":
1369 main()