]> Git Repo - qemu.git/blob - scripts/kvm/kvm_stat
scripts/kvm/kvm_stat: Invert dictionaries
[qemu.git] / scripts / kvm / kvm_stat
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 <[email protected]>
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.
13
14 import curses
15 import sys
16 import os
17 import time
18 import optparse
19 import ctypes
20 import fcntl
21 import resource
22 import struct
23 import re
24 from collections import defaultdict
25
26 class DebugfsProvider(object):
27     def __init__(self):
28         self.base = '/sys/kernel/debug/kvm'
29         self._fields = walkdir(self.base)[2]
30     def fields(self):
31         return self._fields
32     def select(self, fields):
33         self._fields = fields
34     def read(self):
35         def val(key):
36             return int(file(self.base + '/' + key).read())
37         return dict([(key, val(key)) for key in self._fields])
38
39 VMX_EXIT_REASONS = {
40     'EXCEPTION_NMI':        0,
41     'EXTERNAL_INTERRUPT':   1,
42     'TRIPLE_FAULT':         2,
43     'PENDING_INTERRUPT':    7,
44     'NMI_WINDOW':           8,
45     'TASK_SWITCH':          9,
46     'CPUID':                10,
47     'HLT':                  12,
48     'INVLPG':               14,
49     'RDPMC':                15,
50     'RDTSC':                16,
51     'VMCALL':               18,
52     'VMCLEAR':              19,
53     'VMLAUNCH':             20,
54     'VMPTRLD':              21,
55     'VMPTRST':              22,
56     'VMREAD':               23,
57     'VMRESUME':             24,
58     'VMWRITE':              25,
59     'VMOFF':                26,
60     'VMON':                 27,
61     'CR_ACCESS':            28,
62     'DR_ACCESS':            29,
63     'IO_INSTRUCTION':       30,
64     'MSR_READ':             31,
65     'MSR_WRITE':            32,
66     'INVALID_STATE':        33,
67     'MWAIT_INSTRUCTION':    36,
68     'MONITOR_INSTRUCTION':  39,
69     'PAUSE_INSTRUCTION':    40,
70     'MCE_DURING_VMENTRY':   41,
71     'TPR_BELOW_THRESHOLD':  43,
72     'APIC_ACCESS':          44,
73     'EPT_VIOLATION':        48,
74     'EPT_MISCONFIG':        49,
75     'WBINVD':               54,
76     'XSETBV':               55,
77     'APIC_WRITE':           56,
78     'INVPCID':              58,
79 }
80
81 SVM_EXIT_REASONS = {
82     'READ_CR0':       0x000,
83     'READ_CR3':       0x003,
84     'READ_CR4':       0x004,
85     'READ_CR8':       0x008,
86     'WRITE_CR0':      0x010,
87     'WRITE_CR3':      0x013,
88     'WRITE_CR4':      0x014,
89     'WRITE_CR8':      0x018,
90     'READ_DR0':       0x020,
91     'READ_DR1':       0x021,
92     'READ_DR2':       0x022,
93     'READ_DR3':       0x023,
94     'READ_DR4':       0x024,
95     'READ_DR5':       0x025,
96     'READ_DR6':       0x026,
97     'READ_DR7':       0x027,
98     'WRITE_DR0':      0x030,
99     'WRITE_DR1':      0x031,
100     'WRITE_DR2':      0x032,
101     'WRITE_DR3':      0x033,
102     'WRITE_DR4':      0x034,
103     'WRITE_DR5':      0x035,
104     'WRITE_DR6':      0x036,
105     'WRITE_DR7':      0x037,
106     'EXCP_BASE':      0x040,
107     'INTR':           0x060,
108     'NMI':            0x061,
109     'SMI':            0x062,
110     'INIT':           0x063,
111     'VINTR':          0x064,
112     'CR0_SEL_WRITE':  0x065,
113     'IDTR_READ':      0x066,
114     'GDTR_READ':      0x067,
115     'LDTR_READ':      0x068,
116     'TR_READ':        0x069,
117     'IDTR_WRITE':     0x06a,
118     'GDTR_WRITE':     0x06b,
119     'LDTR_WRITE':     0x06c,
120     'TR_WRITE':       0x06d,
121     'RDTSC':          0x06e,
122     'RDPMC':          0x06f,
123     'PUSHF':          0x070,
124     'POPF':           0x071,
125     'CPUID':          0x072,
126     'RSM':            0x073,
127     'IRET':           0x074,
128     'SWINT':          0x075,
129     'INVD':           0x076,
130     'PAUSE':          0x077,
131     'HLT':            0x078,
132     'INVLPG':         0x079,
133     'INVLPGA':        0x07a,
134     'IOIO':           0x07b,
135     'MSR':            0x07c,
136     'TASK_SWITCH':    0x07d,
137     'FERR_FREEZE':    0x07e,
138     'SHUTDOWN':       0x07f,
139     'VMRUN':          0x080,
140     'VMMCALL':        0x081,
141     'VMLOAD':         0x082,
142     'VMSAVE':         0x083,
143     'STGI':           0x084,
144     'CLGI':           0x085,
145     'SKINIT':         0x086,
146     'RDTSCP':         0x087,
147     'ICEBP':          0x088,
148     'WBINVD':         0x089,
149     'MONITOR':        0x08a,
150     'MWAIT':          0x08b,
151     'MWAIT_COND':     0x08c,
152     'XSETBV':         0x08d,
153     'NPF':            0x400,
154 }
155
156 # EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
157 AARCH64_EXIT_REASONS = {
158     'UNKNOWN':      0x00,
159     'WFI':          0x01,
160     'CP15_32':      0x03,
161     'CP15_64':      0x04,
162     'CP14_MR':      0x05,
163     'CP14_LS':      0x06,
164     'FP_ASIMD':     0x07,
165     'CP10_ID':      0x08,
166     'CP14_64':      0x0C,
167     'ILL_ISS':      0x0E,
168     'SVC32':        0x11,
169     'HVC32':        0x12,
170     'SMC32':        0x13,
171     'SVC64':        0x15,
172     'HVC64':        0x16,
173     'SMC64':        0x17,
174     'SYS64':        0x18,
175     'IABT':         0x20,
176     'IABT_HYP':     0x21,
177     'PC_ALIGN':     0x22,
178     'DABT':         0x24,
179     'DABT_HYP':     0x25,
180     'SP_ALIGN':     0x26,
181     'FP_EXC32':     0x28,
182     'FP_EXC64':     0x2C,
183     'SERROR':       0x2F,
184     'BREAKPT':      0x30,
185     'BREAKPT_HYP':  0x31,
186     'SOFTSTP':      0x32,
187     'SOFTSTP_HYP':  0x33,
188     'WATCHPT':      0x34,
189     'WATCHPT_HYP':  0x35,
190     'BKPT32':       0x38,
191     'VECTOR32':     0x3A,
192     'BRK64':        0x3C,
193 }
194
195 # From include/uapi/linux/kvm.h, KVM_EXIT_xxx
196 USERSPACE_EXIT_REASONS = {
197     'UNKNOWN':          0,
198     'EXCEPTION':        1,
199     'IO':               2,
200     'HYPERCALL':        3,
201     'DEBUG':            4,
202     'HLT':              5,
203     'MMIO':             6,
204     'IRQ_WINDOW_OPEN':  7,
205     'SHUTDOWN':         8,
206     'FAIL_ENTRY':       9,
207     'INTR':             10,
208     'SET_TPR':          11,
209     'TPR_ACCESS':       12,
210     'S390_SIEIC':       13,
211     'S390_RESET':       14,
212     'DCR':              15,
213     'NMI':              16,
214     'INTERNAL_ERROR':   17,
215     'OSI':              18,
216     'PAPR_HCALL':       19,
217     'S390_UCONTROL':    20,
218     'WATCHDOG':         21,
219     'S390_TSCH':        22,
220     'EPR':              23,
221     'SYSTEM_EVENT':     24,
222 }
223
224 X86_EXIT_REASONS = {
225     'vmx': VMX_EXIT_REASONS,
226     'svm': SVM_EXIT_REASONS,
227 }
228
229 SC_PERF_EVT_OPEN = None
230 EXIT_REASONS = None
231
232 IOCTL_NUMBERS = {
233     'SET_FILTER' : 0x40082406,
234     'ENABLE'     : 0x00002400,
235     'DISABLE'    : 0x00002401,
236     'RESET'      : 0x00002403,
237 }
238
239 def x86_init(flag):
240     global SC_PERF_EVT_OPEN
241     global EXIT_REASONS
242
243     SC_PERF_EVT_OPEN = 298
244     EXIT_REASONS = X86_EXIT_REASONS[flag]
245
246 def s390_init():
247     global SC_PERF_EVT_OPEN
248
249     SC_PERF_EVT_OPEN = 331
250
251 def ppc_init():
252     global SC_PERF_EVT_OPEN
253     global IOCTL_NUMBERS
254
255     SC_PERF_EVT_OPEN = 319
256
257     IOCTL_NUMBERS['ENABLE'] = 0x20002400
258     IOCTL_NUMBERS['DISABLE'] = 0x20002401
259     IOCTL_NUMBERS['SET_FILTER'] = 0x80002406 | (ctypes.sizeof(ctypes.c_char_p)
260                                                 << 16)
261
262 def aarch64_init():
263     global SC_PERF_EVT_OPEN
264     global EXIT_REASONS
265
266     SC_PERF_EVT_OPEN = 241
267     EXIT_REASONS = AARCH64_EXIT_REASONS
268
269 def detect_platform():
270     if os.uname()[4].startswith('ppc'):
271         ppc_init()
272         return
273     elif os.uname()[4].startswith('aarch64'):
274         aarch64_init()
275         return
276
277     for line in file('/proc/cpuinfo').readlines():
278         if line.startswith('flags'):
279             for flag in line.split():
280                 if flag in X86_EXIT_REASONS:
281                     x86_init(flag)
282                     return
283         elif line.startswith('vendor_id'):
284             for flag in line.split():
285                 if flag == 'IBM/S390':
286                     s390_init()
287                     return
288
289 detect_platform()
290
291
292 def walkdir(path):
293     """Returns os.walk() data for specified directory.
294
295     As it is only a wrapper it returns the same 3-tuple of (dirpath,
296     dirnames, filenames).
297     """
298     return next(os.walk(path))
299
300 filters = {}
301 filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
302 if EXIT_REASONS:
303     filters['kvm_exit'] = ('exit_reason', EXIT_REASONS)
304
305 libc = ctypes.CDLL('libc.so.6')
306 syscall = libc.syscall
307 get_errno = libc.__errno_location
308 get_errno.restype = ctypes.POINTER(ctypes.c_int)
309
310 class perf_event_attr(ctypes.Structure):
311     _fields_ = [('type', ctypes.c_uint32),
312                 ('size', ctypes.c_uint32),
313                 ('config', ctypes.c_uint64),
314                 ('sample_freq', ctypes.c_uint64),
315                 ('sample_type', ctypes.c_uint64),
316                 ('read_format', ctypes.c_uint64),
317                 ('flags', ctypes.c_uint64),
318                 ('wakeup_events', ctypes.c_uint32),
319                 ('bp_type', ctypes.c_uint32),
320                 ('bp_addr', ctypes.c_uint64),
321                 ('bp_len', ctypes.c_uint64),
322                 ]
323 def _perf_event_open(attr, pid, cpu, group_fd, flags):
324     return syscall(SC_PERF_EVT_OPEN, ctypes.pointer(attr), ctypes.c_int(pid),
325                    ctypes.c_int(cpu), ctypes.c_int(group_fd),
326                    ctypes.c_long(flags))
327
328 PERF_TYPE_TRACEPOINT = 2
329 PERF_FORMAT_GROUP = 1 << 3
330
331 sys_tracing = '/sys/kernel/debug/tracing'
332
333 class Group(object):
334     def __init__(self, cpu):
335         self.events = []
336         self.group_leader = None
337         self.cpu = cpu
338     def add_event(self, name, event_set, tracepoint, filter = None):
339         self.events.append(Event(group = self,
340                                  name = name, event_set = event_set,
341                                  tracepoint = tracepoint, filter = filter))
342         if len(self.events) == 1:
343             self.file = os.fdopen(self.events[0].fd)
344     def read(self):
345         bytes = 8 * (1 + len(self.events))
346         fmt = 'xxxxxxxx' + 'q' * len(self.events)
347         return dict(zip([event.name for event in self.events],
348                         struct.unpack(fmt, self.file.read(bytes))))
349
350 class Event(object):
351     def __init__(self, group, name, event_set, tracepoint, filter = None):
352         self.name = name
353         attr = perf_event_attr()
354         attr.type = PERF_TYPE_TRACEPOINT
355         attr.size = ctypes.sizeof(attr)
356         id_path = os.path.join(sys_tracing, 'events', event_set,
357                                tracepoint, 'id')
358         id = int(file(id_path).read())
359         attr.config = id
360         attr.sample_period = 1
361         attr.read_format = PERF_FORMAT_GROUP
362         group_leader = -1
363         if group.events:
364             group_leader = group.events[0].fd
365         fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
366         if fd == -1:
367             err = get_errno()[0]
368             raise Exception('perf_event_open failed, errno = ' + err.__str__())
369         if filter:
370             fcntl.ioctl(fd, IOCTL_NUMBERS['SET_FILTER'], filter)
371         self.fd = fd
372     def enable(self):
373         fcntl.ioctl(self.fd, IOCTL_NUMBERS['ENABLE'], 0)
374     def disable(self):
375         fcntl.ioctl(self.fd, IOCTL_NUMBERS['DISABLE'], 0)
376     def reset(self):
377         fcntl.ioctl(self.fd, IOCTL_NUMBERS['RESET'], 0)
378
379 class TracepointProvider(object):
380     def __init__(self):
381         path = os.path.join(sys_tracing, 'events', 'kvm')
382         fields = walkdir(path)[1]
383         extra = []
384         for f in fields:
385             if f in filters:
386                 subfield, values = filters[f]
387                 for name, number in values.iteritems():
388                     extra.append(f + '(' + name + ')')
389         fields += extra
390         self._setup(fields)
391         self.select(fields)
392     def fields(self):
393         return self._fields
394
395     def _online_cpus(self):
396         l = []
397         pattern = r'cpu([0-9]+)'
398         basedir = '/sys/devices/system/cpu'
399         for entry in os.listdir(basedir):
400             match = re.match(pattern, entry)
401             if not match:
402                 continue
403             path = os.path.join(basedir, entry, 'online')
404             if os.path.exists(path) and open(path).read().strip() != '1':
405                 continue
406             l.append(int(match.group(1)))
407         return l
408
409     def _setup(self, _fields):
410         self._fields = _fields
411         cpus = self._online_cpus()
412         nfiles = len(cpus) * 1000
413         resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
414         events = []
415         self.group_leaders = []
416         for cpu in cpus:
417             group = Group(cpu)
418             for name in _fields:
419                 tracepoint = name
420                 filter = None
421                 m = re.match(r'(.*)\((.*)\)', name)
422                 if m:
423                     tracepoint, sub = m.groups()
424                     filter = '%s==%d\0' % (filters[tracepoint][0],
425                                            filters[tracepoint][1][sub])
426                 event = group.add_event(name, event_set = 'kvm',
427                                         tracepoint = tracepoint,
428                                         filter = filter)
429             self.group_leaders.append(group)
430     def select(self, fields):
431         for group in self.group_leaders:
432             for event in group.events:
433                 if event.name in fields:
434                     event.reset()
435                     event.enable()
436                 else:
437                     event.disable()
438     def read(self):
439         ret = defaultdict(int)
440         for group in self.group_leaders:
441             for name, val in group.read().iteritems():
442                 ret[name] += val
443         return ret
444
445 class Stats:
446     def __init__(self, providers, fields = None):
447         self.providers = providers
448         self.fields_filter = fields
449         self._update()
450     def _update(self):
451         def wanted(key):
452             if not self.fields_filter:
453                 return True
454             return re.match(self.fields_filter, key) is not None
455         self.values = dict()
456         for d in providers:
457             provider_fields = [key for key in d.fields() if wanted(key)]
458             for key in provider_fields:
459                 self.values[key] = None
460             d.select(provider_fields)
461     def set_fields_filter(self, fields_filter):
462         self.fields_filter = fields_filter
463         self._update()
464     def get(self):
465         for d in providers:
466             new = d.read()
467             for key in d.fields():
468                 oldval = self.values.get(key, (0, 0))
469                 newval = new[key]
470                 newdelta = None
471                 if oldval is not None:
472                     newdelta = newval - oldval[0]
473                 self.values[key] = (newval, newdelta)
474         return self.values
475
476 if not os.access('/sys/kernel/debug', os.F_OK):
477     print 'Please enable CONFIG_DEBUG_FS in your kernel'
478     sys.exit(1)
479 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
480     print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
481     print "and ensure the kvm modules are loaded"
482     sys.exit(1)
483
484 LABEL_WIDTH = 40
485 NUMBER_WIDTH = 10
486
487 def tui(screen, stats):
488     curses.use_default_colors()
489     curses.noecho()
490     drilldown = False
491     fields_filter = stats.fields_filter
492     def update_drilldown():
493         if not fields_filter:
494             if drilldown:
495                 stats.set_fields_filter(None)
496             else:
497                 stats.set_fields_filter(r'^[^\(]*$')
498     update_drilldown()
499     def refresh(sleeptime):
500         screen.erase()
501         screen.addstr(0, 0, 'kvm statistics')
502         screen.addstr(2, 1, 'Event')
503         screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - len('Total'), 'Total')
504         screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 - len('Current'), 'Current')
505         row = 3
506         s = stats.get()
507         def sortkey(x):
508             if s[x][1]:
509                 return (-s[x][1], -s[x][0])
510             else:
511                 return (0, -s[x][0])
512         for key in sorted(s.keys(), key = sortkey):
513             if row >= screen.getmaxyx()[0]:
514                 break
515             values = s[key]
516             if not values[0] and not values[1]:
517                 break
518             col = 1
519             screen.addstr(row, col, key)
520             col += LABEL_WIDTH
521             screen.addstr(row, col, '%10d' % (values[0],))
522             col += NUMBER_WIDTH
523             if values[1] is not None:
524                 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
525             row += 1
526         screen.refresh()
527
528     sleeptime = 0.25
529     while True:
530         refresh(sleeptime)
531         curses.halfdelay(int(sleeptime * 10))
532         sleeptime = 3
533         try:
534             c = screen.getkey()
535             if c == 'x':
536                 drilldown = not drilldown
537                 update_drilldown()
538             if c == 'q':
539                 break
540         except KeyboardInterrupt:
541             break
542         except curses.error:
543             continue
544
545 def batch(stats):
546     s = stats.get()
547     time.sleep(1)
548     s = stats.get()
549     for key in sorted(s.keys()):
550         values = s[key]
551         print '%-22s%10d%10d' % (key, values[0], values[1])
552
553 def log(stats):
554     keys = sorted(stats.get().iterkeys())
555     def banner():
556         for k in keys:
557             print '%10s' % k[0:9],
558         print
559     def statline():
560         s = stats.get()
561         for k in keys:
562             print ' %9d' % s[k][1],
563         print
564     line = 0
565     banner_repeat = 20
566     while True:
567         time.sleep(1)
568         if line % banner_repeat == 0:
569             banner()
570         statline()
571         line += 1
572
573 options = optparse.OptionParser()
574 options.add_option('-1', '--once', '--batch',
575                    action = 'store_true',
576                    default = False,
577                    dest = 'once',
578                    help = 'run in batch mode for one second',
579                    )
580 options.add_option('-l', '--log',
581                    action = 'store_true',
582                    default = False,
583                    dest = 'log',
584                    help = 'run in logging mode (like vmstat)',
585                    )
586 options.add_option('-t', '--tracepoints',
587                    action = 'store_true',
588                    default = False,
589                    dest = 'tracepoints',
590                    help = 'retrieve statistics from tracepoints',
591                    )
592 options.add_option('-d', '--debugfs',
593                    action = 'store_true',
594                    default = False,
595                    dest = 'debugfs',
596                    help = 'retrieve statistics from debugfs',
597                    )
598 options.add_option('-f', '--fields',
599                    action = 'store',
600                    default = None,
601                    dest = 'fields',
602                    help = 'fields to display (regex)',
603                    )
604 (options, args) = options.parse_args(sys.argv)
605
606 providers = []
607 if options.tracepoints:
608     providers.append(TracepointProvider())
609 if options.debugfs:
610     providers.append(DebugfsProvider())
611
612 if len(providers) == 0:
613     try:
614         providers = [TracepointProvider()]
615     except:
616         providers = [DebugfsProvider()]
617
618 stats = Stats(providers, fields = options.fields)
619
620 if options.log:
621     log(stats)
622 elif not options.once:
623     curses.wrapper(tui, stats)
624 else:
625     batch(stats)
This page took 0.059655 seconds and 4 git commands to generate.