]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/kvm/kvm_stat/kvm_stat
tools/kvm_stat: fix error on interactive command 'g'
[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
64eefad2 847DELAY_DEFAULT = 3.0
a24e85f6 848MAX_GUEST_NAME_LEN = 48
72187dfa 849MAX_REGEX_LEN = 44
4443084f 850DEFAULT_REGEX = r'^[^\(]*$'
6667ae8f 851SORT_DEFAULT = 0
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
64eefad2
SR
859 self._delay_initial = 0.25
860 self._delay_regular = DELAY_DEFAULT
6667ae8f 861 self._sorting = SORT_DEFAULT
f9bc9e65
JF
862
863 def __enter__(self):
864 """Initialises curses for later use. Based on curses.wrapper
865 implementation from the Python standard library."""
866 self.screen = curses.initscr()
867 curses.noecho()
868 curses.cbreak()
869
870 # The try/catch works around a minor bit of
871 # over-conscientiousness in the curses module, the error
872 # return from C start_color() is ignorable.
873 try:
874 curses.start_color()
9fc0adfc 875 except curses.error:
f9bc9e65
JF
876 pass
877
a0b4e6a0
SR
878 # Hide cursor in extra statement as some monochrome terminals
879 # might support hiding but not colors.
880 try:
881 curses.curs_set(0)
882 except curses.error:
883 pass
884
f9bc9e65
JF
885 curses.use_default_colors()
886 return self
887
888 def __exit__(self, *exception):
773bffee 889 """Resets the terminal to its normal state. Based on curses.wrapper
f9bc9e65
JF
890 implementation from the Python standard library."""
891 if self.screen:
892 self.screen.keypad(0)
893 curses.echo()
894 curses.nocbreak()
895 curses.endwin()
896
865279c5
SR
897 def get_all_gnames(self):
898 """Returns a list of (pid, gname) tuples of all running guests"""
899 res = []
099a2dfc
SR
900 try:
901 child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
902 stdout=subprocess.PIPE)
903 except:
904 raise Exception
905 for line in child.stdout:
906 line = line.lstrip().split(' ', 1)
907 # perform a sanity check before calling the more expensive
908 # function to possibly extract the guest name
865279c5
SR
909 if ' -name ' in line[1]:
910 res.append((line[0], self.get_gname_from_pid(line[0])))
099a2dfc
SR
911 child.stdout.close()
912
865279c5
SR
913 return res
914
915 def print_all_gnames(self, row):
916 """Print a list of all running guests along with their pids."""
917 self.screen.addstr(row, 2, '%8s %-60s' %
918 ('Pid', 'Guest Name (fuzzy list, might be '
919 'inaccurate!)'),
920 curses.A_UNDERLINE)
921 row += 1
922 try:
923 for line in self.get_all_gnames():
924 self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1]))
925 row += 1
926 if row >= self.screen.getmaxyx()[0]:
927 break
928 except Exception:
929 self.screen.addstr(row + 1, 2, 'Not available')
930
931 def get_pid_from_gname(self, gname):
932 """Fuzzy function to convert guest name to QEMU process pid.
933
934 Returns a list of potential pids, can be empty if no match found.
935 Throws an exception on processing errors.
936
937 """
938 pids = []
939 for line in self.get_all_gnames():
940 if gname == line[1]:
941 pids.append(int(line[0]))
942
099a2dfc
SR
943 return pids
944
945 @staticmethod
946 def get_gname_from_pid(pid):
947 """Returns the guest name for a QEMU process pid.
948
949 Extracts the guest name from the QEMU comma line by processing the
950 '-name' option. Will also handle names specified out of sequence.
951
952 """
953 name = ''
954 try:
955 line = open('/proc/{}/cmdline'
956 .format(pid), 'rb').read().split('\0')
957 parms = line[line.index('-name') + 1].split(',')
958 while '' in parms:
959 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
960 # in # ['foo', '', 'bar'], which we revert here
961 idx = parms.index('')
962 parms[idx - 1] += ',' + parms[idx + 1]
963 del parms[idx:idx+2]
964 # the '-name' switch allows for two ways to specify the guest name,
965 # where the plain name overrides the name specified via 'guest='
966 for arg in parms:
967 if '=' not in arg:
968 name = arg
969 break
970 if arg[:6] == 'guest=':
971 name = arg[6:]
972 except (ValueError, IOError, IndexError):
973 pass
974
975 return name
976
f9bc9e65 977 def update_drilldown(self):
fabc7128 978 """Sets or removes a filter that only allows fields without braces."""
f9bc9e65 979 if not self.stats.fields_filter:
4443084f 980 self.stats.fields_filter = DEFAULT_REGEX
f9bc9e65 981
4443084f 982 elif self.stats.fields_filter == DEFAULT_REGEX:
f9bc9e65
JF
983 self.stats.fields_filter = None
984
f0cf040f 985 def update_pid(self, pid):
fabc7128 986 """Propagates pid selection to stats object."""
f0cf040f
JF
987 self.stats.pid_filter = pid
988
184b2d23
SR
989 def refresh_header(self, pid=None):
990 """Refreshes the header."""
991 if pid is None:
992 pid = self.stats.pid_filter
f9bc9e65 993 self.screen.erase()
099a2dfc 994 gname = self.get_gname_from_pid(pid)
a24e85f6
SR
995 if gname:
996 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
997 if len(gname) > MAX_GUEST_NAME_LEN
998 else gname))
184b2d23 999 if pid > 0:
a24e85f6
SR
1000 self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
1001 .format(pid, gname), curses.A_BOLD)
f0cf040f
JF
1002 else:
1003 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
4443084f
SR
1004 if self.stats.fields_filter and self.stats.fields_filter \
1005 != DEFAULT_REGEX:
72187dfa
SR
1006 regex = self.stats.fields_filter
1007 if len(regex) > MAX_REGEX_LEN:
1008 regex = regex[:MAX_REGEX_LEN] + '...'
1009 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
38e89c37
SR
1010 self.screen.addstr(2, 1, '%-40s %10s%7s %8s' %
1011 ('Event', 'Total', '%Total', 'CurAvg/s'),
f6d75310 1012 curses.A_STANDOUT)
184b2d23
SR
1013 self.screen.addstr(4, 1, 'Collecting data...')
1014 self.screen.refresh()
1015
1016 def refresh_body(self, sleeptime):
f9bc9e65 1017 row = 3
184b2d23
SR
1018 self.screen.move(row, 0)
1019 self.screen.clrtobot()
f9bc9e65 1020 stats = self.stats.get()
692c7f6d 1021
6667ae8f
SR
1022 def sortCurAvg(x):
1023 # sort by current events if available
f9bc9e65
JF
1024 if stats[x][1]:
1025 return (-stats[x][1], -stats[x][0])
1026 else:
1027 return (0, -stats[x][0])
6667ae8f
SR
1028
1029 def sortTotal(x):
1030 # sort by totals
1031 return (0, -stats[x][0])
e55fe3cc
SR
1032 total = 0.
1033 for val in stats.values():
1034 total += val[0]
6667ae8f
SR
1035 if self._sorting == SORT_DEFAULT:
1036 sortkey = sortCurAvg
1037 else:
1038 sortkey = sortTotal
f9bc9e65
JF
1039 for key in sorted(stats.keys(), key=sortkey):
1040
1041 if row >= self.screen.getmaxyx()[0]:
1042 break
1043 values = stats[key]
1044 if not values[0] and not values[1]:
1045 break
5a7d11f8
SR
1046 if values[0] is not None:
1047 cur = int(round(values[1] / sleeptime)) if values[1] else ''
38e89c37 1048 self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' %
5a7d11f8
SR
1049 (key, values[0], values[0] * 100 / total,
1050 cur))
f9bc9e65 1051 row += 1
57253937
SR
1052 if row == 3:
1053 self.screen.addstr(4, 1, 'No matching events reported yet')
f9bc9e65
JF
1054 self.screen.refresh()
1055
1fdea7b2
SR
1056 def show_help_interactive(self):
1057 """Display help with list of interactive commands"""
1058 msg = (' c clear filter',
1059 ' f filter by regular expression',
1060 ' g filter by guest name',
1061 ' h display interactive commands reference',
6667ae8f 1062 ' o toggle sorting order (Total vs CurAvg/s)',
1fdea7b2
SR
1063 ' p filter by PID',
1064 ' q quit',
1065 ' r reset stats',
64eefad2 1066 ' s set update interval',
1fdea7b2
SR
1067 ' x toggle reporting of stats for individual child trace'
1068 ' events',
1069 'Any other key refreshes statistics immediately')
1070 curses.cbreak()
1071 self.screen.erase()
1072 self.screen.addstr(0, 0, "Interactive commands reference",
1073 curses.A_BOLD)
1074 self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT)
1075 row = 4
1076 for line in msg:
1077 self.screen.addstr(row, 0, line)
1078 row += 1
1079 self.screen.getkey()
1080 self.refresh_header()
1081
f9bc9e65 1082 def show_filter_selection(self):
fabc7128
JF
1083 """Draws filter selection mask.
1084
1085 Asks for a valid regex and sets the fields filter accordingly.
1086
1087 """
f9bc9e65
JF
1088 while True:
1089 self.screen.erase()
1090 self.screen.addstr(0, 0,
1091 "Show statistics for events matching a regex.",
1092 curses.A_BOLD)
1093 self.screen.addstr(2, 0,
1094 "Current regex: {0}"
1095 .format(self.stats.fields_filter))
1096 self.screen.addstr(3, 0, "New regex: ")
1097 curses.echo()
1098 regex = self.screen.getstr()
1099 curses.noecho()
1100 if len(regex) == 0:
4443084f 1101 self.stats.fields_filter = DEFAULT_REGEX
184b2d23 1102 self.refresh_header()
f9bc9e65
JF
1103 return
1104 try:
1105 re.compile(regex)
1106 self.stats.fields_filter = regex
184b2d23 1107 self.refresh_header()
f9bc9e65
JF
1108 return
1109 except re.error:
1110 continue
1111
f9ff1087 1112 def show_vm_selection_by_pid(self):
fabc7128
JF
1113 """Draws PID selection mask.
1114
1115 Asks for a pid until a valid pid or 0 has been entered.
1116
1117 """
0152c20f 1118 msg = ''
f0cf040f
JF
1119 while True:
1120 self.screen.erase()
1121 self.screen.addstr(0, 0,
1122 'Show statistics for specific pid.',
1123 curses.A_BOLD)
1124 self.screen.addstr(1, 0,
1125 'This might limit the shown data to the trace '
1126 'statistics.')
0152c20f 1127 self.screen.addstr(5, 0, msg)
865279c5 1128 self.print_all_gnames(7)
f0cf040f
JF
1129
1130 curses.echo()
1131 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
1132 pid = self.screen.getstr()
1133 curses.noecho()
1134
1135 try:
be03ea3b
SR
1136 if len(pid) > 0:
1137 pid = int(pid)
1138 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1139 str(pid))):
0152c20f 1140 msg = '"' + str(pid) + '": Not a running process'
be03ea3b
SR
1141 continue
1142 else:
1143 pid = 0
184b2d23
SR
1144 self.refresh_header(pid)
1145 self.update_pid(pid)
1146 break
f0cf040f 1147 except ValueError:
0152c20f 1148 msg = '"' + str(pid) + '": Not a valid pid'
f0cf040f 1149
64eefad2
SR
1150 def show_set_update_interval(self):
1151 """Draws update interval selection mask."""
1152 msg = ''
1153 while True:
1154 self.screen.erase()
1155 self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' %
1156 DELAY_DEFAULT, curses.A_BOLD)
1157 self.screen.addstr(4, 0, msg)
1158 self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
1159 self._delay_regular)
1160 curses.echo()
1161 val = self.screen.getstr()
1162 curses.noecho()
1163
1164 try:
1165 if len(val) > 0:
1166 delay = float(val)
1167 if delay < 0.1:
1168 msg = '"' + str(val) + '": Value must be >=0.1'
1169 continue
1170 if delay > 25.5:
1171 msg = '"' + str(val) + '": Value must be <=25.5'
1172 continue
1173 else:
1174 delay = DELAY_DEFAULT
1175 self._delay_regular = delay
1176 break
1177
1178 except ValueError:
1179 msg = '"' + str(val) + '": Invalid value'
1180 self.refresh_header()
1181
f9ff1087
SR
1182 def show_vm_selection_by_guest_name(self):
1183 """Draws guest selection mask.
1184
1185 Asks for a guest name until a valid guest name or '' is entered.
1186
1187 """
1188 msg = ''
1189 while True:
1190 self.screen.erase()
1191 self.screen.addstr(0, 0,
1192 'Show statistics for specific guest.',
1193 curses.A_BOLD)
1194 self.screen.addstr(1, 0,
1195 'This might limit the shown data to the trace '
1196 'statistics.')
1197 self.screen.addstr(5, 0, msg)
61f381bb 1198 self.print_all_gnames(7)
f9ff1087
SR
1199 curses.echo()
1200 self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
1201 gname = self.screen.getstr()
1202 curses.noecho()
1203
1204 if not gname:
1205 self.refresh_header(0)
1206 self.update_pid(0)
1207 break
1208 else:
1209 pids = []
1210 try:
099a2dfc 1211 pids = self.get_pid_from_gname(gname)
f9ff1087
SR
1212 except:
1213 msg = '"' + gname + '": Internal error while searching, ' \
1214 'use pid filter instead'
1215 continue
1216 if len(pids) == 0:
1217 msg = '"' + gname + '": Not an active guest'
1218 continue
1219 if len(pids) > 1:
1220 msg = '"' + gname + '": Multiple matches found, use pid ' \
1221 'filter instead'
1222 continue
1223 self.refresh_header(pids[0])
1224 self.update_pid(pids[0])
1225 break
1226
f9bc9e65 1227 def show_stats(self):
fabc7128 1228 """Refreshes the screen and processes user input."""
64eefad2 1229 sleeptime = self._delay_initial
184b2d23 1230 self.refresh_header()
124c2fc9 1231 start = 0.0 # result based on init value never appears on screen
f9bc9e65 1232 while True:
124c2fc9 1233 self.refresh_body(time.time() - start)
f9bc9e65 1234 curses.halfdelay(int(sleeptime * 10))
124c2fc9 1235 start = time.time()
64eefad2 1236 sleeptime = self._delay_regular
f9bc9e65
JF
1237 try:
1238 char = self.screen.getkey()
4443084f
SR
1239 if char == 'c':
1240 self.stats.fields_filter = DEFAULT_REGEX
1241 self.refresh_header(0)
1242 self.update_pid(0)
f9bc9e65 1243 if char == 'f':
62d1b6cc 1244 curses.curs_set(1)
f9bc9e65 1245 self.show_filter_selection()
62d1b6cc 1246 curses.curs_set(0)
64eefad2 1247 sleeptime = self._delay_initial
f9ff1087 1248 if char == 'g':
62d1b6cc 1249 curses.curs_set(1)
f9ff1087 1250 self.show_vm_selection_by_guest_name()
62d1b6cc 1251 curses.curs_set(0)
64eefad2 1252 sleeptime = self._delay_initial
1fdea7b2
SR
1253 if char == 'h':
1254 self.show_help_interactive()
6667ae8f
SR
1255 if char == 'o':
1256 self._sorting = not self._sorting
f0cf040f 1257 if char == 'p':
62d1b6cc 1258 curses.curs_set(1)
f9ff1087 1259 self.show_vm_selection_by_pid()
62d1b6cc 1260 curses.curs_set(0)
64eefad2 1261 sleeptime = self._delay_initial
1fdea7b2
SR
1262 if char == 'q':
1263 break
9f114a03 1264 if char == 'r':
9f114a03 1265 self.stats.reset()
64eefad2
SR
1266 if char == 's':
1267 curses.curs_set(1)
1268 self.show_set_update_interval()
1269 curses.curs_set(0)
1270 sleeptime = self._delay_initial
1fdea7b2
SR
1271 if char == 'x':
1272 self.update_drilldown()
f9bc9e65
JF
1273 except KeyboardInterrupt:
1274 break
1275 except curses.error:
1276 continue
1277
692c7f6d 1278
f9bc9e65 1279def batch(stats):
fabc7128 1280 """Prints statistics in a key, value format."""
dadf1e78
SR
1281 try:
1282 s = stats.get()
1283 time.sleep(1)
1284 s = stats.get()
1285 for key in sorted(s.keys()):
1286 values = s[key]
1287 print '%-42s%10d%10d' % (key, values[0], values[1])
1288 except KeyboardInterrupt:
1289 pass
f9bc9e65 1290
692c7f6d 1291
f9bc9e65 1292def log(stats):
fabc7128 1293 """Prints statistics as reiterating key block, multiple value blocks."""
f9bc9e65 1294 keys = sorted(stats.get().iterkeys())
692c7f6d 1295
f9bc9e65
JF
1296 def banner():
1297 for k in keys:
1298 print '%s' % k,
1299 print
692c7f6d 1300
f9bc9e65
JF
1301 def statline():
1302 s = stats.get()
1303 for k in keys:
1304 print ' %9d' % s[k][1],
1305 print
1306 line = 0
1307 banner_repeat = 20
1308 while True:
dadf1e78
SR
1309 try:
1310 time.sleep(1)
1311 if line % banner_repeat == 0:
1312 banner()
1313 statline()
1314 line += 1
1315 except KeyboardInterrupt:
1316 break
f9bc9e65 1317
692c7f6d 1318
f9bc9e65 1319def get_options():
fabc7128 1320 """Returns processed program arguments."""
f9bc9e65
JF
1321 description_text = """
1322This script displays various statistics about VMs running under KVM.
1323The statistics are gathered from the KVM debugfs entries and / or the
1324currently available perf traces.
1325
1326The monitoring takes additional cpu cycles and might affect the VM's
1327performance.
1328
1329Requirements:
1330- Access to:
1331 /sys/kernel/debug/kvm
1332 /sys/kernel/debug/trace/events/*
1333 /proc/pid/task
1334- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1335 CAP_SYS_ADMIN and perf events are used.
1336- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1337 the large number of files that are possibly opened.
1eaa2f90
SR
1338
1339Interactive Commands:
4443084f 1340 c clear filter
1eaa2f90 1341 f filter by regular expression
f9ff1087 1342 g filter by guest name
1fdea7b2 1343 h display interactive commands reference
6667ae8f 1344 o toggle sorting order (Total vs CurAvg/s)
1eaa2f90
SR
1345 p filter by PID
1346 q quit
9f114a03 1347 r reset stats
64eefad2 1348 s set update interval
1fdea7b2 1349 x toggle reporting of stats for individual child trace events
1eaa2f90 1350Press any other key to refresh statistics immediately.
f9bc9e65
JF
1351"""
1352
1353 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1354 def format_description(self, description):
1355 if description:
1356 return description + "\n"
1357 else:
1358 return ""
1359
f9ff1087
SR
1360 def cb_guest_to_pid(option, opt, val, parser):
1361 try:
099a2dfc 1362 pids = Tui.get_pid_from_gname(val)
f9ff1087
SR
1363 except:
1364 raise optparse.OptionValueError('Error while searching for guest '
1365 '"{}", use "-p" to specify a pid '
1366 'instead'.format(val))
1367 if len(pids) == 0:
1368 raise optparse.OptionValueError('No guest by the name "{}" '
1369 'found'.format(val))
1370 if len(pids) > 1:
1371 raise optparse.OptionValueError('Multiple processes found (pids: '
1372 '{}) - use "-p" to specify a pid '
1373 'instead'.format(" ".join(pids)))
1374 parser.values.pid = pids[0]
1375
f9bc9e65
JF
1376 optparser = optparse.OptionParser(description=description_text,
1377 formatter=PlainHelpFormatter())
1378 optparser.add_option('-1', '--once', '--batch',
1379 action='store_true',
1380 default=False,
1381 dest='once',
1382 help='run in batch mode for one second',
1383 )
1384 optparser.add_option('-l', '--log',
1385 action='store_true',
1386 default=False,
1387 dest='log',
1388 help='run in logging mode (like vmstat)',
1389 )
1390 optparser.add_option('-t', '--tracepoints',
1391 action='store_true',
1392 default=False,
1393 dest='tracepoints',
1394 help='retrieve statistics from tracepoints',
1395 )
1396 optparser.add_option('-d', '--debugfs',
1397 action='store_true',
1398 default=False,
1399 dest='debugfs',
1400 help='retrieve statistics from debugfs',
1401 )
1402 optparser.add_option('-f', '--fields',
1403 action='store',
c469117d 1404 default=DEFAULT_REGEX,
f9bc9e65
JF
1405 dest='fields',
1406 help='fields to display (regex)',
1407 )
f0cf040f 1408 optparser.add_option('-p', '--pid',
e0ba3876
SR
1409 action='store',
1410 default=0,
1411 type='int',
1412 dest='pid',
1413 help='restrict statistics to pid',
1414 )
f9ff1087
SR
1415 optparser.add_option('-g', '--guest',
1416 action='callback',
1417 type='string',
1418 dest='pid',
1419 metavar='GUEST',
1420 help='restrict statistics to guest by name',
1421 callback=cb_guest_to_pid,
1422 )
f9bc9e65
JF
1423 (options, _) = optparser.parse_args(sys.argv)
1424 return options
1425
692c7f6d 1426
f9bc9e65 1427def check_access(options):
fabc7128 1428 """Exits if the current user can't access all needed directories."""
f9bc9e65
JF
1429 if not os.path.exists('/sys/kernel/debug'):
1430 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1431 sys.exit(1)
1432
1433 if not os.path.exists(PATH_DEBUGFS_KVM):
1434 sys.stderr.write("Please make sure, that debugfs is mounted and "
1435 "readable by the current user:\n"
1436 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1437 "Also ensure, that the kvm modules are loaded.\n")
1438 sys.exit(1)
1439
e0ba3876
SR
1440 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1441 not options.debugfs):
f9bc9e65
JF
1442 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1443 "when using the option -t (default).\n"
1444 "If it is enabled, make {0} readable by the "
1445 "current user.\n"
1446 .format(PATH_DEBUGFS_TRACING))
1447 if options.tracepoints:
1448 sys.exit(1)
1449
1450 sys.stderr.write("Falling back to debugfs statistics!\n")
1451 options.debugfs = True
e0ba3876 1452 time.sleep(5)
f9bc9e65
JF
1453
1454 return options
1455
692c7f6d 1456
f9bc9e65
JF
1457def main():
1458 options = get_options()
1459 options = check_access(options)
f0cf040f
JF
1460
1461 if (options.pid > 0 and
1462 not os.path.isdir(os.path.join('/proc/',
1463 str(options.pid)))):
1464 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1465 sys.exit('Specified pid does not exist.')
1466
c469117d 1467 stats = Stats(options)
f9bc9e65
JF
1468
1469 if options.log:
1470 log(stats)
1471 elif not options.once:
1472 with Tui(stats) as tui:
1473 tui.show_stats()
1474 else:
1475 batch(stats)
1476
1477if __name__ == "__main__":
1478 main()