]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - tools/kvm/kvm_stat/kvm_stat
tools/kvm_stat: display regex when set to non-default
[mirror_ubuntu-bionic-kernel.git] / tools / kvm / kvm_stat / kvm_stat
CommitLineData
f9bc9e65
JF
1#!/usr/bin/python
2#
3# top-like utility for displaying kvm statistics
4#
5# Copyright 2006-2008 Qumranet Technologies
6# Copyright 2008-2011 Red Hat, Inc.
7#
8# Authors:
9# Avi Kivity <avi@redhat.com>
10#
11# This work is licensed under the terms of the GNU GPL, version 2. See
12# the COPYING file in the top-level directory.
fabc7128
JF
13"""The kvm_stat module outputs statistics about running KVM VMs
14
15Three different ways of output formatting are available:
16- as a top-like text ui
17- in a key -> value format
18- in an all keys, all values format
19
20The data is sampled from the KVM's debugfs entries and its perf events.
21"""
f9bc9e65
JF
22
23import curses
24import sys
25import os
26import time
27import optparse
28import ctypes
29import fcntl
30import resource
31import struct
32import re
33from collections import defaultdict
f9bc9e65
JF
34
35VMX_EXIT_REASONS = {
36 'EXCEPTION_NMI': 0,
37 'EXTERNAL_INTERRUPT': 1,
38 'TRIPLE_FAULT': 2,
39 'PENDING_INTERRUPT': 7,
40 'NMI_WINDOW': 8,
41 'TASK_SWITCH': 9,
42 'CPUID': 10,
43 'HLT': 12,
44 'INVLPG': 14,
45 'RDPMC': 15,
46 'RDTSC': 16,
47 'VMCALL': 18,
48 'VMCLEAR': 19,
49 'VMLAUNCH': 20,
50 'VMPTRLD': 21,
51 'VMPTRST': 22,
52 'VMREAD': 23,
53 'VMRESUME': 24,
54 'VMWRITE': 25,
55 'VMOFF': 26,
56 'VMON': 27,
57 'CR_ACCESS': 28,
58 'DR_ACCESS': 29,
59 'IO_INSTRUCTION': 30,
60 'MSR_READ': 31,
61 'MSR_WRITE': 32,
62 'INVALID_STATE': 33,
63 'MWAIT_INSTRUCTION': 36,
64 'MONITOR_INSTRUCTION': 39,
65 'PAUSE_INSTRUCTION': 40,
66 'MCE_DURING_VMENTRY': 41,
67 'TPR_BELOW_THRESHOLD': 43,
68 'APIC_ACCESS': 44,
69 'EPT_VIOLATION': 48,
70 'EPT_MISCONFIG': 49,
71 'WBINVD': 54,
72 'XSETBV': 55,
73 'APIC_WRITE': 56,
74 'INVPCID': 58,
75}
76
77SVM_EXIT_REASONS = {
78 'READ_CR0': 0x000,
79 'READ_CR3': 0x003,
80 'READ_CR4': 0x004,
81 'READ_CR8': 0x008,
82 'WRITE_CR0': 0x010,
83 'WRITE_CR3': 0x013,
84 'WRITE_CR4': 0x014,
85 'WRITE_CR8': 0x018,
86 'READ_DR0': 0x020,
87 'READ_DR1': 0x021,
88 'READ_DR2': 0x022,
89 'READ_DR3': 0x023,
90 'READ_DR4': 0x024,
91 'READ_DR5': 0x025,
92 'READ_DR6': 0x026,
93 'READ_DR7': 0x027,
94 'WRITE_DR0': 0x030,
95 'WRITE_DR1': 0x031,
96 'WRITE_DR2': 0x032,
97 'WRITE_DR3': 0x033,
98 'WRITE_DR4': 0x034,
99 'WRITE_DR5': 0x035,
100 'WRITE_DR6': 0x036,
101 'WRITE_DR7': 0x037,
102 'EXCP_BASE': 0x040,
103 'INTR': 0x060,
104 'NMI': 0x061,
105 'SMI': 0x062,
106 'INIT': 0x063,
107 'VINTR': 0x064,
108 'CR0_SEL_WRITE': 0x065,
109 'IDTR_READ': 0x066,
110 'GDTR_READ': 0x067,
111 'LDTR_READ': 0x068,
112 'TR_READ': 0x069,
113 'IDTR_WRITE': 0x06a,
114 'GDTR_WRITE': 0x06b,
115 'LDTR_WRITE': 0x06c,
116 'TR_WRITE': 0x06d,
117 'RDTSC': 0x06e,
118 'RDPMC': 0x06f,
119 'PUSHF': 0x070,
120 'POPF': 0x071,
121 'CPUID': 0x072,
122 'RSM': 0x073,
123 'IRET': 0x074,
124 'SWINT': 0x075,
125 'INVD': 0x076,
126 'PAUSE': 0x077,
127 'HLT': 0x078,
128 'INVLPG': 0x079,
129 'INVLPGA': 0x07a,
130 'IOIO': 0x07b,
131 'MSR': 0x07c,
132 'TASK_SWITCH': 0x07d,
133 'FERR_FREEZE': 0x07e,
134 'SHUTDOWN': 0x07f,
135 'VMRUN': 0x080,
136 'VMMCALL': 0x081,
137 'VMLOAD': 0x082,
138 'VMSAVE': 0x083,
139 'STGI': 0x084,
140 'CLGI': 0x085,
141 'SKINIT': 0x086,
142 'RDTSCP': 0x087,
143 'ICEBP': 0x088,
144 'WBINVD': 0x089,
145 'MONITOR': 0x08a,
146 'MWAIT': 0x08b,
147 'MWAIT_COND': 0x08c,
148 'XSETBV': 0x08d,
149 'NPF': 0x400,
150}
151
152# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
153AARCH64_EXIT_REASONS = {
154 'UNKNOWN': 0x00,
155 'WFI': 0x01,
156 'CP15_32': 0x03,
157 'CP15_64': 0x04,
158 'CP14_MR': 0x05,
159 'CP14_LS': 0x06,
160 'FP_ASIMD': 0x07,
161 'CP10_ID': 0x08,
162 'CP14_64': 0x0C,
163 'ILL_ISS': 0x0E,
164 'SVC32': 0x11,
165 'HVC32': 0x12,
166 'SMC32': 0x13,
167 'SVC64': 0x15,
168 'HVC64': 0x16,
169 'SMC64': 0x17,
170 'SYS64': 0x18,
171 'IABT': 0x20,
172 'IABT_HYP': 0x21,
173 'PC_ALIGN': 0x22,
174 'DABT': 0x24,
175 'DABT_HYP': 0x25,
176 'SP_ALIGN': 0x26,
177 'FP_EXC32': 0x28,
178 'FP_EXC64': 0x2C,
179 'SERROR': 0x2F,
180 'BREAKPT': 0x30,
181 'BREAKPT_HYP': 0x31,
182 'SOFTSTP': 0x32,
183 'SOFTSTP_HYP': 0x33,
184 'WATCHPT': 0x34,
185 'WATCHPT_HYP': 0x35,
186 'BKPT32': 0x38,
187 'VECTOR32': 0x3A,
188 'BRK64': 0x3C,
189}
190
191# From include/uapi/linux/kvm.h, KVM_EXIT_xxx
192USERSPACE_EXIT_REASONS = {
193 'UNKNOWN': 0,
194 'EXCEPTION': 1,
195 'IO': 2,
196 'HYPERCALL': 3,
197 'DEBUG': 4,
198 'HLT': 5,
199 'MMIO': 6,
200 'IRQ_WINDOW_OPEN': 7,
201 'SHUTDOWN': 8,
202 'FAIL_ENTRY': 9,
203 'INTR': 10,
204 'SET_TPR': 11,
205 'TPR_ACCESS': 12,
206 'S390_SIEIC': 13,
207 'S390_RESET': 14,
208 'DCR': 15,
209 'NMI': 16,
210 'INTERNAL_ERROR': 17,
211 'OSI': 18,
212 'PAPR_HCALL': 19,
213 'S390_UCONTROL': 20,
214 'WATCHDOG': 21,
215 'S390_TSCH': 22,
216 'EPR': 23,
217 'SYSTEM_EVENT': 24,
218}
219
220IOCTL_NUMBERS = {
221 'SET_FILTER': 0x40082406,
222 'ENABLE': 0x00002400,
223 'DISABLE': 0x00002401,
224 'RESET': 0x00002403,
225}
226
692c7f6d 227
f9bc9e65 228class Arch(object):
fabc7128
JF
229 """Encapsulates global architecture specific data.
230
231 Contains the performance event open syscall and ioctl numbers, as
232 well as the VM exit reasons for the architecture it runs on.
f9bc9e65
JF
233
234 """
235 @staticmethod
236 def get_arch():
237 machine = os.uname()[4]
238
239 if machine.startswith('ppc'):
240 return ArchPPC()
241 elif machine.startswith('aarch64'):
242 return ArchA64()
243 elif machine.startswith('s390'):
244 return ArchS390()
245 else:
246 # X86_64
247 for line in open('/proc/cpuinfo'):
248 if not line.startswith('flags'):
249 continue
250
251 flags = line.split()
252 if 'vmx' in flags:
253 return ArchX86(VMX_EXIT_REASONS)
254 if 'svm' in flags:
255 return ArchX86(SVM_EXIT_REASONS)
256 return
257
692c7f6d 258
f9bc9e65
JF
259class ArchX86(Arch):
260 def __init__(self, exit_reasons):
261 self.sc_perf_evt_open = 298
262 self.ioctl_numbers = IOCTL_NUMBERS
263 self.exit_reasons = exit_reasons
264
692c7f6d 265
f9bc9e65
JF
266class ArchPPC(Arch):
267 def __init__(self):
268 self.sc_perf_evt_open = 319
269 self.ioctl_numbers = IOCTL_NUMBERS
270 self.ioctl_numbers['ENABLE'] = 0x20002400
271 self.ioctl_numbers['DISABLE'] = 0x20002401
c7d4fb5a 272 self.ioctl_numbers['RESET'] = 0x20002403
f9bc9e65
JF
273
274 # PPC comes in 32 and 64 bit and some generated ioctl
275 # numbers depend on the wordsize.
276 char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
277 self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
c7d4fb5a 278 self.exit_reasons = {}
f9bc9e65 279
692c7f6d 280
f9bc9e65
JF
281class ArchA64(Arch):
282 def __init__(self):
283 self.sc_perf_evt_open = 241
284 self.ioctl_numbers = IOCTL_NUMBERS
285 self.exit_reasons = AARCH64_EXIT_REASONS
286
692c7f6d 287
f9bc9e65
JF
288class ArchS390(Arch):
289 def __init__(self):
290 self.sc_perf_evt_open = 331
291 self.ioctl_numbers = IOCTL_NUMBERS
292 self.exit_reasons = None
293
294ARCH = Arch.get_arch()
295
296
297def walkdir(path):
298 """Returns os.walk() data for specified directory.
299
300 As it is only a wrapper it returns the same 3-tuple of (dirpath,
301 dirnames, filenames).
302 """
303 return next(os.walk(path))
304
305
306def parse_int_list(list_string):
307 """Returns an int list from a string of comma separated integers and
308 integer ranges."""
309 integers = []
310 members = list_string.split(',')
311
312 for member in members:
313 if '-' not in member:
314 integers.append(int(member))
315 else:
316 int_range = member.split('-')
317 integers.extend(range(int(int_range[0]),
318 int(int_range[1]) + 1))
319
320 return integers
321
322
a24e85f6
SR
323def get_gname_from_pid(pid):
324 """Returns the guest name for a QEMU process pid.
325
326 Extracts the guest name from the QEMU comma line by processing the '-name'
327 option. Will also handle names specified out of sequence.
328
329 """
330 name = ''
331 try:
332 line = open('/proc/{}/cmdline'.format(pid), 'rb').read().split('\0')
333 parms = line[line.index('-name') + 1].split(',')
334 while '' in parms:
335 # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results in
336 # ['foo', '', 'bar'], which we revert here
337 idx = parms.index('')
338 parms[idx - 1] += ',' + parms[idx + 1]
339 del parms[idx:idx+2]
340 # the '-name' switch allows for two ways to specify the guest name,
341 # where the plain name overrides the name specified via 'guest='
342 for arg in parms:
343 if '=' not in arg:
344 name = arg
345 break
346 if arg[:6] == 'guest=':
347 name = arg[6:]
348 except (ValueError, IOError, IndexError):
349 pass
350
351 return name
352
353
f9bc9e65 354def get_online_cpus():
fabc7128 355 """Returns a list of cpu id integers."""
f9bc9e65
JF
356 with open('/sys/devices/system/cpu/online') as cpu_list:
357 cpu_string = cpu_list.readline()
358 return parse_int_list(cpu_string)
359
360
361def get_filters():
fabc7128
JF
362 """Returns a dict of trace events, their filter ids and
363 the values that can be filtered.
364
365 Trace events can be filtered for special values by setting a
366 filter string via an ioctl. The string normally has the format
367 identifier==value. For each filter a new event will be created, to
368 be able to distinguish the events.
369
370 """
f9bc9e65
JF
371 filters = {}
372 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
373 if ARCH.exit_reasons:
374 filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
375 return filters
376
377libc = ctypes.CDLL('libc.so.6', use_errno=True)
378syscall = libc.syscall
379
692c7f6d 380
f9bc9e65 381class perf_event_attr(ctypes.Structure):
fabc7128
JF
382 """Struct that holds the necessary data to set up a trace event.
383
384 For an extensive explanation see perf_event_open(2) and
385 include/uapi/linux/perf_event.h, struct perf_event_attr
386
387 All fields that are not initialized in the constructor are 0.
388
389 """
f9bc9e65
JF
390 _fields_ = [('type', ctypes.c_uint32),
391 ('size', ctypes.c_uint32),
392 ('config', ctypes.c_uint64),
393 ('sample_freq', ctypes.c_uint64),
394 ('sample_type', ctypes.c_uint64),
395 ('read_format', ctypes.c_uint64),
396 ('flags', ctypes.c_uint64),
397 ('wakeup_events', ctypes.c_uint32),
398 ('bp_type', ctypes.c_uint32),
399 ('bp_addr', ctypes.c_uint64),
400 ('bp_len', ctypes.c_uint64),
401 ]
402
403 def __init__(self):
404 super(self.__class__, self).__init__()
405 self.type = PERF_TYPE_TRACEPOINT
406 self.size = ctypes.sizeof(self)
407 self.read_format = PERF_FORMAT_GROUP
408
692c7f6d 409
f9bc9e65 410def perf_event_open(attr, pid, cpu, group_fd, flags):
fabc7128
JF
411 """Wrapper for the sys_perf_evt_open() syscall.
412
413 Used to set up performance events, returns a file descriptor or -1
414 on error.
415
416 Attributes are:
417 - syscall number
418 - struct perf_event_attr *
419 - pid or -1 to monitor all pids
420 - cpu number or -1 to monitor all cpus
421 - The file descriptor of the group leader or -1 to create a group.
422 - flags
423
424 """
f9bc9e65
JF
425 return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
426 ctypes.c_int(pid), ctypes.c_int(cpu),
427 ctypes.c_int(group_fd), ctypes.c_long(flags))
428
429PERF_TYPE_TRACEPOINT = 2
430PERF_FORMAT_GROUP = 1 << 3
431
432PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
433PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
434
692c7f6d 435
f9bc9e65 436class Group(object):
fabc7128
JF
437 """Represents a perf event group."""
438
f9bc9e65
JF
439 def __init__(self):
440 self.events = []
441
442 def add_event(self, event):
443 self.events.append(event)
444
445 def read(self):
fabc7128
JF
446 """Returns a dict with 'event name: value' for all events in the
447 group.
448
449 Values are read by reading from the file descriptor of the
450 event that is the group leader. See perf_event_open(2) for
451 details.
452
453 Read format for the used event configuration is:
454 struct read_format {
455 u64 nr; /* The number of events */
456 struct {
457 u64 value; /* The value of the event */
458 } values[nr];
459 };
460
461 """
f9bc9e65
JF
462 length = 8 * (1 + len(self.events))
463 read_format = 'xxxxxxxx' + 'Q' * len(self.events)
464 return dict(zip([event.name for event in self.events],
465 struct.unpack(read_format,
466 os.read(self.events[0].fd, length))))
467
692c7f6d 468
f9bc9e65 469class Event(object):
fabc7128 470 """Represents a performance event and manages its life cycle."""
f0cf040f
JF
471 def __init__(self, name, group, trace_cpu, trace_pid, trace_point,
472 trace_filter, trace_set='kvm'):
f9bc9e65
JF
473 self.name = name
474 self.fd = None
f0cf040f
JF
475 self.setup_event(group, trace_cpu, trace_pid, trace_point,
476 trace_filter, trace_set)
477
478 def __del__(self):
fabc7128
JF
479 """Closes the event's file descriptor.
480
481 As no python file object was created for the file descriptor,
482 python will not reference count the descriptor and will not
483 close it itself automatically, so we do it.
484
485 """
f0cf040f
JF
486 if self.fd:
487 os.close(self.fd)
f9bc9e65
JF
488
489 def setup_event_attribute(self, trace_set, trace_point):
fabc7128
JF
490 """Returns an initialized ctype perf_event_attr struct."""
491
f9bc9e65
JF
492 id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
493 trace_point, 'id')
494
495 event_attr = perf_event_attr()
496 event_attr.config = int(open(id_path).read())
497 return event_attr
498
f0cf040f
JF
499 def setup_event(self, group, trace_cpu, trace_pid, trace_point,
500 trace_filter, trace_set):
fabc7128
JF
501 """Sets up the perf event in Linux.
502
503 Issues the syscall to register the event in the kernel and
504 then sets the optional filter.
505
506 """
507
f9bc9e65
JF
508 event_attr = self.setup_event_attribute(trace_set, trace_point)
509
fabc7128 510 # First event will be group leader.
f9bc9e65 511 group_leader = -1
fabc7128
JF
512
513 # All others have to pass the leader's descriptor instead.
f9bc9e65
JF
514 if group.events:
515 group_leader = group.events[0].fd
516
f0cf040f
JF
517 fd = perf_event_open(event_attr, trace_pid,
518 trace_cpu, group_leader, 0)
f9bc9e65
JF
519 if fd == -1:
520 err = ctypes.get_errno()
521 raise OSError(err, os.strerror(err),
522 'while calling sys_perf_event_open().')
523
524 if trace_filter:
525 fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
526 trace_filter)
527
528 self.fd = fd
529
530 def enable(self):
fabc7128
JF
531 """Enables the trace event in the kernel.
532
533 Enabling the group leader makes reading counters from it and the
534 events under it possible.
535
536 """
f9bc9e65
JF
537 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
538
539 def disable(self):
fabc7128
JF
540 """Disables the trace event in the kernel.
541
542 Disabling the group leader makes reading all counters under it
543 impossible.
544
545 """
f9bc9e65
JF
546 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
547
548 def reset(self):
fabc7128 549 """Resets the count of the trace event in the kernel."""
f9bc9e65
JF
550 fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
551
692c7f6d 552
f9bc9e65 553class TracepointProvider(object):
fabc7128
JF
554 """Data provider for the stats class.
555
556 Manages the events/groups from which it acquires its data.
557
558 """
f9bc9e65
JF
559 def __init__(self):
560 self.group_leaders = []
561 self.filters = get_filters()
562 self._fields = self.get_available_fields()
f0cf040f 563 self._pid = 0
f9bc9e65
JF
564
565 def get_available_fields(self):
fabc7128
JF
566 """Returns a list of available event's of format 'event name(filter
567 name)'.
568
569 All available events have directories under
570 /sys/kernel/debug/tracing/events/ which export information
571 about the specific event. Therefore, listing the dirs gives us
572 a list of all available events.
573
574 Some events like the vm exit reasons can be filtered for
575 specific values. To take account for that, the routine below
576 creates special fields with the following format:
577 event name(filter name)
578
579 """
f9bc9e65
JF
580 path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
581 fields = walkdir(path)[1]
582 extra = []
583 for field in fields:
584 if field in self.filters:
585 filter_name_, filter_dicts = self.filters[field]
586 for name in filter_dicts:
587 extra.append(field + '(' + name + ')')
588 fields += extra
589 return fields
590
591 def setup_traces(self):
fabc7128
JF
592 """Creates all event and group objects needed to be able to retrieve
593 data."""
a1836069 594 fields = self.get_available_fields()
f0cf040f
JF
595 if self._pid > 0:
596 # Fetch list of all threads of the monitored pid, as qemu
597 # starts a thread for each vcpu.
598 path = os.path.join('/proc', str(self._pid), 'task')
599 groupids = walkdir(path)[1]
600 else:
601 groupids = get_online_cpus()
f9bc9e65
JF
602
603 # The constant is needed as a buffer for python libs, std
604 # streams and other files that the script opens.
a1836069 605 newlim = len(groupids) * len(fields) + 50
f9bc9e65
JF
606 try:
607 softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
608
609 if hardlim < newlim:
610 # Now we need CAP_SYS_RESOURCE, to increase the hard limit.
611 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
612 else:
613 # Raising the soft limit is sufficient.
614 resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
615
616 except ValueError:
617 sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
618
f0cf040f 619 for groupid in groupids:
f9bc9e65 620 group = Group()
a1836069 621 for name in fields:
f9bc9e65
JF
622 tracepoint = name
623 tracefilter = None
624 match = re.match(r'(.*)\((.*)\)', name)
625 if match:
626 tracepoint, sub = match.groups()
627 tracefilter = ('%s==%d\0' %
628 (self.filters[tracepoint][0],
629 self.filters[tracepoint][1][sub]))
630
f0cf040f
JF
631 # From perf_event_open(2):
632 # pid > 0 and cpu == -1
633 # This measures the specified process/thread on any CPU.
634 #
635 # pid == -1 and cpu >= 0
636 # This measures all processes/threads on the specified CPU.
637 trace_cpu = groupid if self._pid == 0 else -1
638 trace_pid = int(groupid) if self._pid != 0 else -1
639
f9bc9e65
JF
640 group.add_event(Event(name=name,
641 group=group,
f0cf040f
JF
642 trace_cpu=trace_cpu,
643 trace_pid=trace_pid,
f9bc9e65
JF
644 trace_point=tracepoint,
645 trace_filter=tracefilter))
f0cf040f 646
f9bc9e65
JF
647 self.group_leaders.append(group)
648
649 def available_fields(self):
650 return self.get_available_fields()
651
652 @property
653 def fields(self):
654 return self._fields
655
656 @fields.setter
657 def fields(self, fields):
fabc7128 658 """Enables/disables the (un)wanted events"""
f9bc9e65
JF
659 self._fields = fields
660 for group in self.group_leaders:
661 for index, event in enumerate(group.events):
662 if event.name in fields:
663 event.reset()
664 event.enable()
665 else:
666 # Do not disable the group leader.
667 # It would disable all of its events.
668 if index != 0:
669 event.disable()
670
f0cf040f
JF
671 @property
672 def pid(self):
673 return self._pid
674
675 @pid.setter
676 def pid(self, pid):
fabc7128 677 """Changes the monitored pid by setting new traces."""
f0cf040f 678 self._pid = pid
fabc7128
JF
679 # The garbage collector will get rid of all Event/Group
680 # objects and open files after removing the references.
f0cf040f
JF
681 self.group_leaders = []
682 self.setup_traces()
683 self.fields = self._fields
684
f9bc9e65 685 def read(self):
fabc7128 686 """Returns 'event name: current value' for all enabled events."""
f9bc9e65
JF
687 ret = defaultdict(int)
688 for group in self.group_leaders:
689 for name, val in group.read().iteritems():
690 if name in self._fields:
691 ret[name] += val
692 return ret
693
692c7f6d 694
f9bc9e65 695class DebugfsProvider(object):
fabc7128
JF
696 """Provides data from the files that KVM creates in the kvm debugfs
697 folder."""
f9bc9e65
JF
698 def __init__(self):
699 self._fields = self.get_available_fields()
f0cf040f
JF
700 self._pid = 0
701 self.do_read = True
e0ba3876 702 self.paths = []
f9bc9e65
JF
703
704 def get_available_fields(self):
fabc7128
JF
705 """"Returns a list of available fields.
706
707 The fields are all available KVM debugfs files
708
709 """
f9bc9e65
JF
710 return walkdir(PATH_DEBUGFS_KVM)[2]
711
712 @property
713 def fields(self):
714 return self._fields
715
716 @fields.setter
717 def fields(self, fields):
718 self._fields = fields
719
f0cf040f
JF
720 @property
721 def pid(self):
722 return self._pid
723
724 @pid.setter
725 def pid(self, pid):
726 if pid != 0:
727 self._pid = pid
728
729 vms = walkdir(PATH_DEBUGFS_KVM)[1]
730 if len(vms) == 0:
731 self.do_read = False
732
733 self.paths = filter(lambda x: "{}-".format(pid) in x, vms)
734
735 else:
736 self.paths = ['']
737 self.do_read = True
738
f9bc9e65 739 def read(self):
f0cf040f
JF
740 """Returns a dict with format:'file name / field -> current value'."""
741 results = {}
742
743 # If no debugfs filtering support is available, then don't read.
744 if not self.do_read:
745 return results
746
747 for path in self.paths:
748 for field in self._fields:
749 results[field] = results.get(field, 0) \
750 + self.read_field(field, path)
751
752 return results
753
754 def read_field(self, field, path):
755 """Returns the value of a single field from a specific VM."""
756 try:
757 return int(open(os.path.join(PATH_DEBUGFS_KVM,
758 path,
759 field))
760 .read())
761 except IOError:
762 return 0
f9bc9e65 763
692c7f6d 764
f9bc9e65 765class Stats(object):
fabc7128
JF
766 """Manages the data providers and the data they provide.
767
768 It is used to set filters on the provider's data and collect all
769 provider data.
770
771 """
f0cf040f 772 def __init__(self, providers, pid, fields=None):
f9bc9e65 773 self.providers = providers
f0cf040f 774 self._pid_filter = pid
f9bc9e65
JF
775 self._fields_filter = fields
776 self.values = {}
f0cf040f 777 self.update_provider_pid()
f9bc9e65
JF
778 self.update_provider_filters()
779
780 def update_provider_filters(self):
fabc7128 781 """Propagates fields filters to providers."""
f9bc9e65
JF
782 def wanted(key):
783 if not self._fields_filter:
784 return True
785 return re.match(self._fields_filter, key) is not None
786
787 # As we reset the counters when updating the fields we can
788 # also clear the cache of old values.
789 self.values = {}
790 for provider in self.providers:
791 provider_fields = [key for key in provider.get_available_fields()
792 if wanted(key)]
793 provider.fields = provider_fields
794
f0cf040f 795 def update_provider_pid(self):
fabc7128 796 """Propagates pid filters to providers."""
f0cf040f
JF
797 for provider in self.providers:
798 provider.pid = self._pid_filter
799
f9bc9e65
JF
800 @property
801 def fields_filter(self):
802 return self._fields_filter
803
804 @fields_filter.setter
805 def fields_filter(self, fields_filter):
806 self._fields_filter = fields_filter
807 self.update_provider_filters()
808
f0cf040f
JF
809 @property
810 def pid_filter(self):
811 return self._pid_filter
812
813 @pid_filter.setter
814 def pid_filter(self, pid):
815 self._pid_filter = pid
816 self.values = {}
817 self.update_provider_pid()
818
f9bc9e65 819 def get(self):
fabc7128
JF
820 """Returns a dict with field -> (value, delta to last value) of all
821 provider data."""
f9bc9e65
JF
822 for provider in self.providers:
823 new = provider.read()
824 for key in provider.fields:
825 oldval = self.values.get(key, (0, 0))
826 newval = new.get(key, 0)
827 newdelta = None
828 if oldval is not None:
829 newdelta = newval - oldval[0]
830 self.values[key] = (newval, newdelta)
831 return self.values
832
833LABEL_WIDTH = 40
834NUMBER_WIDTH = 10
184b2d23
SR
835DELAY_INITIAL = 0.25
836DELAY_REGULAR = 3.0
a24e85f6 837MAX_GUEST_NAME_LEN = 48
72187dfa 838MAX_REGEX_LEN = 44
f9bc9e65 839
692c7f6d 840
f9bc9e65 841class Tui(object):
fabc7128 842 """Instruments curses to draw a nice text ui."""
f9bc9e65
JF
843 def __init__(self, stats):
844 self.stats = stats
845 self.screen = None
f9bc9e65
JF
846 self.update_drilldown()
847
848 def __enter__(self):
849 """Initialises curses for later use. Based on curses.wrapper
850 implementation from the Python standard library."""
851 self.screen = curses.initscr()
852 curses.noecho()
853 curses.cbreak()
854
855 # The try/catch works around a minor bit of
856 # over-conscientiousness in the curses module, the error
857 # return from C start_color() is ignorable.
858 try:
859 curses.start_color()
9fc0adfc 860 except curses.error:
f9bc9e65
JF
861 pass
862
a0b4e6a0
SR
863 # Hide cursor in extra statement as some monochrome terminals
864 # might support hiding but not colors.
865 try:
866 curses.curs_set(0)
867 except curses.error:
868 pass
869
f9bc9e65
JF
870 curses.use_default_colors()
871 return self
872
873 def __exit__(self, *exception):
874 """Resets the terminal to its normal state. Based on curses.wrappre
875 implementation from the Python standard library."""
876 if self.screen:
877 self.screen.keypad(0)
878 curses.echo()
879 curses.nocbreak()
880 curses.endwin()
881
882 def update_drilldown(self):
fabc7128 883 """Sets or removes a filter that only allows fields without braces."""
f9bc9e65
JF
884 if not self.stats.fields_filter:
885 self.stats.fields_filter = r'^[^\(]*$'
886
887 elif self.stats.fields_filter == r'^[^\(]*$':
888 self.stats.fields_filter = None
889
f0cf040f 890 def update_pid(self, pid):
fabc7128 891 """Propagates pid selection to stats object."""
f0cf040f
JF
892 self.stats.pid_filter = pid
893
184b2d23
SR
894 def refresh_header(self, pid=None):
895 """Refreshes the header."""
896 if pid is None:
897 pid = self.stats.pid_filter
f9bc9e65 898 self.screen.erase()
a24e85f6
SR
899 gname = get_gname_from_pid(pid)
900 if gname:
901 gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...'
902 if len(gname) > MAX_GUEST_NAME_LEN
903 else gname))
184b2d23 904 if pid > 0:
a24e85f6
SR
905 self.screen.addstr(0, 0, 'kvm statistics - pid {0} {1}'
906 .format(pid, gname), curses.A_BOLD)
f0cf040f
JF
907 else:
908 self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
72187dfa
SR
909 if self.stats.fields_filter and self.stats.fields_filter != '^[^\(]*$':
910 regex = self.stats.fields_filter
911 if len(regex) > MAX_REGEX_LEN:
912 regex = regex[:MAX_REGEX_LEN] + '...'
913 self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex))
f9bc9e65
JF
914 self.screen.addstr(2, 1, 'Event')
915 self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH -
916 len('Total'), 'Total')
917 self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 -
918 len('Current'), 'Current')
184b2d23
SR
919 self.screen.addstr(4, 1, 'Collecting data...')
920 self.screen.refresh()
921
922 def refresh_body(self, sleeptime):
f9bc9e65 923 row = 3
184b2d23
SR
924 self.screen.move(row, 0)
925 self.screen.clrtobot()
f9bc9e65 926 stats = self.stats.get()
692c7f6d 927
f9bc9e65
JF
928 def sortkey(x):
929 if stats[x][1]:
930 return (-stats[x][1], -stats[x][0])
931 else:
932 return (0, -stats[x][0])
933 for key in sorted(stats.keys(), key=sortkey):
934
935 if row >= self.screen.getmaxyx()[0]:
936 break
937 values = stats[key]
938 if not values[0] and not values[1]:
939 break
940 col = 1
941 self.screen.addstr(row, col, key)
942 col += LABEL_WIDTH
943 self.screen.addstr(row, col, '%10d' % (values[0],))
944 col += NUMBER_WIDTH
945 if values[1] is not None:
946 self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
947 row += 1
948 self.screen.refresh()
949
950 def show_filter_selection(self):
fabc7128
JF
951 """Draws filter selection mask.
952
953 Asks for a valid regex and sets the fields filter accordingly.
954
955 """
f9bc9e65
JF
956 while True:
957 self.screen.erase()
958 self.screen.addstr(0, 0,
959 "Show statistics for events matching a regex.",
960 curses.A_BOLD)
961 self.screen.addstr(2, 0,
962 "Current regex: {0}"
963 .format(self.stats.fields_filter))
964 self.screen.addstr(3, 0, "New regex: ")
965 curses.echo()
966 regex = self.screen.getstr()
967 curses.noecho()
968 if len(regex) == 0:
184b2d23 969 self.refresh_header()
f9bc9e65
JF
970 return
971 try:
972 re.compile(regex)
973 self.stats.fields_filter = regex
184b2d23 974 self.refresh_header()
f9bc9e65
JF
975 return
976 except re.error:
977 continue
978
f0cf040f 979 def show_vm_selection(self):
fabc7128
JF
980 """Draws PID selection mask.
981
982 Asks for a pid until a valid pid or 0 has been entered.
983
984 """
0152c20f 985 msg = ''
f0cf040f
JF
986 while True:
987 self.screen.erase()
988 self.screen.addstr(0, 0,
989 'Show statistics for specific pid.',
990 curses.A_BOLD)
991 self.screen.addstr(1, 0,
992 'This might limit the shown data to the trace '
993 'statistics.')
0152c20f 994 self.screen.addstr(5, 0, msg)
f0cf040f
JF
995
996 curses.echo()
997 self.screen.addstr(3, 0, "Pid [0 or pid]: ")
998 pid = self.screen.getstr()
999 curses.noecho()
1000
1001 try:
be03ea3b
SR
1002 if len(pid) > 0:
1003 pid = int(pid)
1004 if pid != 0 and not os.path.isdir(os.path.join('/proc/',
1005 str(pid))):
0152c20f 1006 msg = '"' + str(pid) + '": Not a running process'
be03ea3b
SR
1007 continue
1008 else:
1009 pid = 0
184b2d23
SR
1010 self.refresh_header(pid)
1011 self.update_pid(pid)
1012 break
f0cf040f
JF
1013
1014 except ValueError:
0152c20f 1015 msg = '"' + str(pid) + '": Not a valid pid'
f0cf040f
JF
1016 continue
1017
f9bc9e65 1018 def show_stats(self):
fabc7128 1019 """Refreshes the screen and processes user input."""
184b2d23
SR
1020 sleeptime = DELAY_INITIAL
1021 self.refresh_header()
f9bc9e65 1022 while True:
184b2d23 1023 self.refresh_body(sleeptime)
f9bc9e65 1024 curses.halfdelay(int(sleeptime * 10))
184b2d23 1025 sleeptime = DELAY_REGULAR
f9bc9e65
JF
1026 try:
1027 char = self.screen.getkey()
1028 if char == 'x':
184b2d23 1029 self.refresh_header()
f9bc9e65 1030 self.update_drilldown()
184b2d23 1031 sleeptime = DELAY_INITIAL
f9bc9e65
JF
1032 if char == 'q':
1033 break
1034 if char == 'f':
1035 self.show_filter_selection()
184b2d23 1036 sleeptime = DELAY_INITIAL
f0cf040f
JF
1037 if char == 'p':
1038 self.show_vm_selection()
184b2d23 1039 sleeptime = DELAY_INITIAL
f9bc9e65
JF
1040 except KeyboardInterrupt:
1041 break
1042 except curses.error:
1043 continue
1044
692c7f6d 1045
f9bc9e65 1046def batch(stats):
fabc7128 1047 """Prints statistics in a key, value format."""
dadf1e78
SR
1048 try:
1049 s = stats.get()
1050 time.sleep(1)
1051 s = stats.get()
1052 for key in sorted(s.keys()):
1053 values = s[key]
1054 print '%-42s%10d%10d' % (key, values[0], values[1])
1055 except KeyboardInterrupt:
1056 pass
f9bc9e65 1057
692c7f6d 1058
f9bc9e65 1059def log(stats):
fabc7128 1060 """Prints statistics as reiterating key block, multiple value blocks."""
f9bc9e65 1061 keys = sorted(stats.get().iterkeys())
692c7f6d 1062
f9bc9e65
JF
1063 def banner():
1064 for k in keys:
1065 print '%s' % k,
1066 print
692c7f6d 1067
f9bc9e65
JF
1068 def statline():
1069 s = stats.get()
1070 for k in keys:
1071 print ' %9d' % s[k][1],
1072 print
1073 line = 0
1074 banner_repeat = 20
1075 while True:
dadf1e78
SR
1076 try:
1077 time.sleep(1)
1078 if line % banner_repeat == 0:
1079 banner()
1080 statline()
1081 line += 1
1082 except KeyboardInterrupt:
1083 break
f9bc9e65 1084
692c7f6d 1085
f9bc9e65 1086def get_options():
fabc7128 1087 """Returns processed program arguments."""
f9bc9e65
JF
1088 description_text = """
1089This script displays various statistics about VMs running under KVM.
1090The statistics are gathered from the KVM debugfs entries and / or the
1091currently available perf traces.
1092
1093The monitoring takes additional cpu cycles and might affect the VM's
1094performance.
1095
1096Requirements:
1097- Access to:
1098 /sys/kernel/debug/kvm
1099 /sys/kernel/debug/trace/events/*
1100 /proc/pid/task
1101- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
1102 CAP_SYS_ADMIN and perf events are used.
1103- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
1104 the large number of files that are possibly opened.
1eaa2f90
SR
1105
1106Interactive Commands:
1107 f filter by regular expression
1108 p filter by PID
1109 q quit
1110 x toggle reporting of stats for individual child trace events
1111Press any other key to refresh statistics immediately.
f9bc9e65
JF
1112"""
1113
1114 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1115 def format_description(self, description):
1116 if description:
1117 return description + "\n"
1118 else:
1119 return ""
1120
1121 optparser = optparse.OptionParser(description=description_text,
1122 formatter=PlainHelpFormatter())
1123 optparser.add_option('-1', '--once', '--batch',
1124 action='store_true',
1125 default=False,
1126 dest='once',
1127 help='run in batch mode for one second',
1128 )
1129 optparser.add_option('-l', '--log',
1130 action='store_true',
1131 default=False,
1132 dest='log',
1133 help='run in logging mode (like vmstat)',
1134 )
1135 optparser.add_option('-t', '--tracepoints',
1136 action='store_true',
1137 default=False,
1138 dest='tracepoints',
1139 help='retrieve statistics from tracepoints',
1140 )
1141 optparser.add_option('-d', '--debugfs',
1142 action='store_true',
1143 default=False,
1144 dest='debugfs',
1145 help='retrieve statistics from debugfs',
1146 )
1147 optparser.add_option('-f', '--fields',
1148 action='store',
1149 default=None,
1150 dest='fields',
1151 help='fields to display (regex)',
1152 )
f0cf040f 1153 optparser.add_option('-p', '--pid',
e0ba3876
SR
1154 action='store',
1155 default=0,
1156 type='int',
1157 dest='pid',
1158 help='restrict statistics to pid',
1159 )
f9bc9e65
JF
1160 (options, _) = optparser.parse_args(sys.argv)
1161 return options
1162
692c7f6d 1163
f9bc9e65 1164def get_providers(options):
fabc7128 1165 """Returns a list of data providers depending on the passed options."""
f9bc9e65
JF
1166 providers = []
1167
1168 if options.tracepoints:
1169 providers.append(TracepointProvider())
1170 if options.debugfs:
1171 providers.append(DebugfsProvider())
1172 if len(providers) == 0:
1173 providers.append(TracepointProvider())
1174
1175 return providers
1176
692c7f6d 1177
f9bc9e65 1178def check_access(options):
fabc7128 1179 """Exits if the current user can't access all needed directories."""
f9bc9e65
JF
1180 if not os.path.exists('/sys/kernel/debug'):
1181 sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
1182 sys.exit(1)
1183
1184 if not os.path.exists(PATH_DEBUGFS_KVM):
1185 sys.stderr.write("Please make sure, that debugfs is mounted and "
1186 "readable by the current user:\n"
1187 "('mount -t debugfs debugfs /sys/kernel/debug')\n"
1188 "Also ensure, that the kvm modules are loaded.\n")
1189 sys.exit(1)
1190
e0ba3876
SR
1191 if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or
1192 not options.debugfs):
f9bc9e65
JF
1193 sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
1194 "when using the option -t (default).\n"
1195 "If it is enabled, make {0} readable by the "
1196 "current user.\n"
1197 .format(PATH_DEBUGFS_TRACING))
1198 if options.tracepoints:
1199 sys.exit(1)
1200
1201 sys.stderr.write("Falling back to debugfs statistics!\n")
1202 options.debugfs = True
e0ba3876 1203 time.sleep(5)
f9bc9e65
JF
1204
1205 return options
1206
692c7f6d 1207
f9bc9e65
JF
1208def main():
1209 options = get_options()
1210 options = check_access(options)
f0cf040f
JF
1211
1212 if (options.pid > 0 and
1213 not os.path.isdir(os.path.join('/proc/',
1214 str(options.pid)))):
1215 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
1216 sys.exit('Specified pid does not exist.')
1217
f9bc9e65 1218 providers = get_providers(options)
f0cf040f 1219 stats = Stats(providers, options.pid, fields=options.fields)
f9bc9e65
JF
1220
1221 if options.log:
1222 log(stats)
1223 elif not options.once:
1224 with Tui(stats) as tui:
1225 tui.show_stats()
1226 else:
1227 batch(stats)
1228
1229if __name__ == "__main__":
1230 main()