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