]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/kvm/kvm_stat/kvm_stat
tools/kvm_stat: add new interactive command 'h'
[mirror_ubuntu-jammy-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))
38e89c37
SR
984 self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
985 ('Event', 'Total', '%Total', 'CurAvg/s'),
f6d75310 986 curses.A_STANDOUT)
184b2d23
SR
987 self.screen.addstr(4, 1, 'Collecting data...')
988 self.screen.refresh()
989
990 def refresh_body(self, sleeptime):
f9bc9e65 991 row = 3
184b2d23
SR
992 self.screen.move(row, 0)
993 self.screen.clrtobot()
f9bc9e65 994 stats = self.stats.get()
692c7f6d 995
f9bc9e65
JF
996 def sortkey(x):
997 if stats[x][1]:
998 return (-stats[x][1], -stats[x][0])
999 else:
1000 return (0, -stats[x][0])
e55fe3cc
SR
1001 total = 0.
1002 for val in stats.values():
1003 total += val[0]
f9bc9e65
JF
1004 for key in sorted(stats.keys(), key=sortkey):
1005
1006 if row >= self.screen.getmaxyx()[0]:
1007 break
1008 values = stats[key]
1009 if not values[0] and not values[1]:
1010 break
5a7d11f8
SR
1011 if values[0] is not None:
1012 cur = int(round(values[1] / sleeptime)) if values[1] else ''
38e89c37 1013 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
5a7d11f8
SR
1014 (key, values[0], values[0] * 100 / total,
1015 cur))
f9bc9e65 1016 row += 1
57253937
SR
1017 if row == 3:
1018 self.screen.addstr(4, 1, 'No matching events reported yet')
f9bc9e65
JF
1019 self.screen.refresh()
1020
1fdea7b2
SR
1021 def show_help_interactive(self):
1022 """Display help with list of interactive commands"""
1023 msg = (' c clear filter',
1024 ' f filter by regular expression',
1025 ' g filter by guest name',
1026 ' h display interactive commands reference',
1027 ' p filter by PID',
1028 ' q quit',
1029 ' r reset stats',
1030 ' x toggle reporting of stats for individual child trace'
1031 ' events',
1032 'Any other key refreshes statistics immediately')
1033 curses.cbreak()
1034 self.screen.erase()
1035 self.screen.addstr(0, 0, "Interactive commands reference",
1036 curses.A_BOLD)
1037 self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1038 row = 4
1039 for line in msg:
1040 self.screen.addstr(row, 0, line)
1041 row += 1
1042 self.screen.getkey()
1043 self.refresh_header()
1044
f9bc9e65 1045 def show_filter_selection(self):
fabc7128
JF
1046 """Draws filter selection mask.
1047
1048 Asks for a valid regex and sets the fields filter accordingly.
1049
1050 """
f9bc9e65
JF
1051 while True:
1052 self.screen.erase()
1053 self.screen.addstr(0, 0,
1054 "Show statistics for events matching a regex.",
1055 curses.A_BOLD)
1056 self.screen.addstr(2, 0,
1057 "Current regex: {0}"
1058 .format(self.stats.fields_filter))
1059 self.screen.addstr(3, 0, "New regex: ")
1060 curses.echo()
1061 regex = self.screen.getstr()
1062 curses.noecho()
1063 if len(regex) == 0:
4443084f 1064 self.stats.fields_filter = DEFAULT_REGEX
184b2d23 1065 self.refresh_header()
f9bc9e65
JF
1066 return
1067 try:
1068 re.compile(regex)
1069 self.stats.fields_filter = regex
184b2d23 1070 self.refresh_header()
f9bc9e65
JF
1071 return
1072 except re.error:
1073 continue
1074
f9ff1087 1075 def show_vm_selection_by_pid(self):
fabc7128
JF
1076 """Draws PID selection mask.
1077
1078 Asks for a pid until a valid pid or 0 has been entered.
1079
1080 """
0152c20f 1081 msg = ''
f0cf040f
JF
1082 while True:
1083 self.screen.erase()
1084 self.screen.addstr(0, 0,
1085 'Show statistics for specific pid.',
1086 curses.A_BOLD)
1087 self.screen.addstr(1, 0,
1088 'This might limit the shown data to the trace '
1089 'statistics.')
0152c20f 1090 self.screen.addstr(5, 0, msg)
f0cf040f
JF
1091
1092 curses.echo()
1093 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1094 pid = self.screen.getstr()
1095 curses.noecho()
1096
1097 try:
be03ea3b
SR
1098 if len(pid) > 0:
1099 pid = int(pid)
1100 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1101 str(pid))):
0152c20f 1102 msg = '"' + str(pid) + '": Not a running process'
be03ea3b
SR
1103 continue
1104 else:
1105 pid = 0
184b2d23
SR
1106 self.refresh_header(pid)
1107 self.update_pid(pid)
1108 break
f0cf040f
JF
1109
1110 except ValueError:
0152c20f 1111 msg = '"' + str(pid) + '": Not a valid pid'
f0cf040f 1112
f9ff1087
SR
1113 def show_vm_selection_by_guest_name(self):
1114 """Draws guest selection mask.
1115
1116 Asks for a guest name until a valid guest name or '' is entered.
1117
1118 """
1119 msg = ''
1120 while True:
1121 self.screen.erase()
1122 self.screen.addstr(0, 0,
1123 'Show statistics for specific guest.',
1124 curses.A_BOLD)
1125 self.screen.addstr(1, 0,
1126 'This might limit the shown data to the trace '
1127 'statistics.')
1128 self.screen.addstr(5, 0, msg)
1129 curses.echo()
1130 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1131 gname = self.screen.getstr()
1132 curses.noecho()
1133
1134 if not gname:
1135 self.refresh_header(0)
1136 self.update_pid(0)
1137 break
1138 else:
1139 pids = []
1140 try:
099a2dfc 1141 pids = self.get_pid_from_gname(gname)
f9ff1087
SR
1142 except:
1143 msg = '"' + gname + '": Internal error while searching, ' \
1144 'use pid filter instead'
1145 continue
1146 if len(pids) == 0:
1147 msg = '"' + gname + '": Not an active guest'
1148 continue
1149 if len(pids) > 1:
1150 msg = '"' + gname + '": Multiple matches found, use pid ' \
1151 'filter instead'
1152 continue
1153 self.refresh_header(pids[0])
1154 self.update_pid(pids[0])
1155 break
1156
f9bc9e65 1157 def show_stats(self):
fabc7128 1158 """Refreshes the screen and processes user input."""
184b2d23
SR
1159 sleeptime = DELAY_INITIAL
1160 self.refresh_header()
124c2fc9 1161 start = 0.0 # result based on init value never appears on screen
f9bc9e65 1162 while True:
124c2fc9 1163 self.refresh_body(time.time() - start)
f9bc9e65 1164 curses.halfdelay(int(sleeptime * 10))
124c2fc9 1165 start = time.time()
184b2d23 1166 sleeptime = DELAY_REGULAR
f9bc9e65
JF
1167 try:
1168 char = self.screen.getkey()
4443084f
SR
1169 if char == 'c':
1170 self.stats.fields_filter = DEFAULT_REGEX
1171 self.refresh_header(0)
1172 self.update_pid(0)
f9bc9e65 1173 if char == 'f':
62d1b6cc 1174 curses.curs_set(1)
f9bc9e65 1175 self.show_filter_selection()
62d1b6cc 1176 curses.curs_set(0)
184b2d23 1177 sleeptime = DELAY_INITIAL
f9ff1087 1178 if char == 'g':
62d1b6cc 1179 curses.curs_set(1)
f9ff1087 1180 self.show_vm_selection_by_guest_name()
62d1b6cc 1181 curses.curs_set(0)
f9ff1087 1182 sleeptime = DELAY_INITIAL
1fdea7b2
SR
1183 if char == 'h':
1184 self.show_help_interactive()
f0cf040f 1185 if char == 'p':
62d1b6cc 1186 curses.curs_set(1)
f9ff1087 1187 self.show_vm_selection_by_pid()
62d1b6cc 1188 curses.curs_set(0)
184b2d23 1189 sleeptime = DELAY_INITIAL
1fdea7b2
SR
1190 if char == 'q':
1191 break
9f114a03 1192 if char == 'r':
9f114a03 1193 self.stats.reset()
1fdea7b2
SR
1194 if char == 'x':
1195 self.update_drilldown()
f9bc9e65
JF
1196 except KeyboardInterrupt:
1197 break
1198 except curses.error:
1199 continue
1200
692c7f6d 1201
f9bc9e65 1202def batch(stats):
fabc7128 1203 """Prints statistics in a key, value format."""
dadf1e78
SR
1204 try:
1205 s = stats.get()
1206 time.sleep(1)
1207 s = stats.get()
1208 for key in sorted(s.keys()):
1209 values = s[key]
1210 print '%-42s%10d%10d' % (key, values[0], values[1])
1211 except KeyboardInterrupt:
1212 pass
f9bc9e65 1213
692c7f6d 1214
f9bc9e65 1215def log(stats):
fabc7128 1216 """Prints statistics as reiterating key block, multiple value blocks."""
f9bc9e65 1217 keys = sorted(stats.get().iterkeys())
692c7f6d 1218
f9bc9e65
JF
1219 def banner():
1220 for k in keys:
1221 print '%s' % k,
1222 print
692c7f6d 1223
f9bc9e65
JF
1224 def statline():
1225 s = stats.get()
1226 for k in keys:
1227 print ' %9d' % s[k][1],
1228 print
1229 line = 0
1230 banner_repeat = 20
1231 while True:
dadf1e78
SR
1232 try:
1233 time.sleep(1)
1234 if line % banner_repeat == 0:
1235 banner()
1236 statline()
1237 line += 1
1238 except KeyboardInterrupt:
1239 break
f9bc9e65 1240
692c7f6d 1241
f9bc9e65 1242def get_options():
fabc7128 1243 """Returns processed program arguments."""
f9bc9e65
JF
1244 description_text = """
1245This script displays various statistics about VMs running under KVM.
1246The statistics are gathered from the KVM debugfs entries and / or the
1247currently available perf traces.
1248
1249The monitoring takes additional cpu cycles and might affect the VM's
1250performance.
1251
1252Requirements:
1253- Access to:
1254 /sys/kernel/debug/kvm
1255 /sys/kernel/debug/trace/events/*
1256 /proc/pid/task
1257- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1258 CAP_SYS_ADMIN and perf events are used.
1259- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1260 the large number of files that are possibly opened.
1eaa2f90
SR
1261
1262Interactive Commands:
4443084f 1263 c clear filter
1eaa2f90 1264 f filter by regular expression
f9ff1087 1265 g filter by guest name
1fdea7b2 1266 h display interactive commands reference
1eaa2f90
SR
1267 p filter by PID
1268 q quit
9f114a03 1269 r reset stats
1fdea7b2 1270 x toggle reporting of stats for individual child trace events
1eaa2f90 1271Press any other key to refresh statistics immediately.
f9bc9e65
JF
1272"""
1273
1274 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1275 def format_description(self, description):
1276 if description:
1277 return description + "\n"
1278 else:
1279 return ""
1280
f9ff1087
SR
1281 def cb_guest_to_pid(option, opt, val, parser):
1282 try:
099a2dfc 1283 pids = Tui.get_pid_from_gname(val)
f9ff1087
SR
1284 except:
1285 raise optparse.OptionValueError('Error while searching for guest '
1286 '"{}", use "-p" to specify a pid '
1287 'instead'.format(val))
1288 if len(pids) == 0:
1289 raise optparse.OptionValueError('No guest by the name "{}" '
1290 'found'.format(val))
1291 if len(pids) > 1:
1292 raise optparse.OptionValueError('Multiple processes found (pids: '
1293 '{}) - use "-p" to specify a pid '
1294 'instead'.format(" ".join(pids)))
1295 parser.values.pid = pids[0]
1296
f9bc9e65
JF
1297 optparser = optparse.OptionParser(description=description_text,
1298 formatter=PlainHelpFormatter())
1299 optparser.add_option('-1', '--once', '--batch',
1300 action='store_true',
1301 default=False,
1302 dest='once',
1303 help='run in batch mode for one second',
1304 )
1305 optparser.add_option('-l', '--log',
1306 action='store_true',
1307 default=False,
1308 dest='log',
1309 help='run in logging mode (like vmstat)',
1310 )
1311 optparser.add_option('-t', '--tracepoints',
1312 action='store_true',
1313 default=False,
1314 dest='tracepoints',
1315 help='retrieve statistics from tracepoints',
1316 )
1317 optparser.add_option('-d', '--debugfs',
1318 action='store_true',
1319 default=False,
1320 dest='debugfs',
1321 help='retrieve statistics from debugfs',
1322 )
1323 optparser.add_option('-f', '--fields',
1324 action='store',
c469117d 1325 default=DEFAULT_REGEX,
f9bc9e65
JF
1326 dest='fields',
1327 help='fields to display (regex)',
1328 )
f0cf040f 1329 optparser.add_option('-p', '--pid',
e0ba3876
SR
1330 action='store',
1331 default=0,
1332 type='int',
1333 dest='pid',
1334 help='restrict statistics to pid',
1335 )
f9ff1087
SR
1336 optparser.add_option('-g', '--guest',
1337 action='callback',
1338 type='string',
1339 dest='pid',
1340 metavar='GUEST',
1341 help='restrict statistics to guest by name',
1342 callback=cb_guest_to_pid,
1343 )
f9bc9e65
JF
1344 (options, _) = optparser.parse_args(sys.argv)
1345 return options
1346
692c7f6d 1347
f9bc9e65 1348def check_access(options):
fabc7128 1349 """Exits if the current user can't access all needed directories."""
f9bc9e65
JF
1350 if not os.path.exists('/sys/kernel/debug'):
1351 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1352 sys.exit(1)
1353
1354 if not os.path.exists(PATH_DEBUGFS_KVM):
1355 sys.stderr.write("Please make sure, that debugfs is mounted and "
1356 "readable by the current user:\n"
1357 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1358 "Also ensure, that the kvm modules are loaded.\n")
1359 sys.exit(1)
1360
e0ba3876
SR
1361 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1362 not options.debugfs):
f9bc9e65
JF
1363 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1364 "when using the option -t (default).\n"
1365 "If it is enabled, make {0} readable by the "
1366 "current user.\n"
1367 .format(PATH_DEBUGFS_TRACING))
1368 if options.tracepoints:
1369 sys.exit(1)
1370
1371 sys.stderr.write("Falling back to debugfs statistics!\n")
1372 options.debugfs = True
e0ba3876 1373 time.sleep(5)
f9bc9e65
JF
1374
1375 return options
1376
692c7f6d 1377
f9bc9e65
JF
1378def main():
1379 options = get_options()
1380 options = check_access(options)
f0cf040f
JF
1381
1382 if (options.pid > 0 and
1383 not os.path.isdir(os.path.join('/proc/',
1384 str(options.pid)))):
1385 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1386 sys.exit('Specified pid does not exist.')
1387
c469117d 1388 stats = Stats(options)
f9bc9e65
JF
1389
1390 if options.log:
1391 log(stats)
1392 elif not options.once:
1393 with Tui(stats) as tui:
1394 tui.show_stats()
1395 else:
1396 batch(stats)
1397
1398if __name__ == "__main__":
1399 main()