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