Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 413 lines (384 sloc) 11.792 kb
68396ea @rmustacc Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
rmustacc authored
1 #!/usr/bin/python
2
3 import curses
4 import sys, os, time, optparse
5
6 class DebugfsProvider(object):
7 def __init__(self):
8 self.base = '/sys/kernel/debug/kvm'
9 self._fields = os.listdir(self.base)
10 def fields(self):
11 return self._fields
12 def select(self, fields):
13 self._fields = fields
14 def read(self):
15 def val(key):
16 return int(file(self.base + '/' + key).read())
17 return dict([(key, val(key)) for key in self._fields])
18
19 vmx_exit_reasons = {
20 0: 'EXCEPTION_NMI',
21 1: 'EXTERNAL_INTERRUPT',
22 2: 'TRIPLE_FAULT',
23 7: 'PENDING_INTERRUPT',
24 8: 'NMI_WINDOW',
25 9: 'TASK_SWITCH',
26 10: 'CPUID',
27 12: 'HLT',
28 14: 'INVLPG',
29 15: 'RDPMC',
30 16: 'RDTSC',
31 18: 'VMCALL',
32 19: 'VMCLEAR',
33 20: 'VMLAUNCH',
34 21: 'VMPTRLD',
35 22: 'VMPTRST',
36 23: 'VMREAD',
37 24: 'VMRESUME',
38 25: 'VMWRITE',
39 26: 'VMOFF',
40 27: 'VMON',
41 28: 'CR_ACCESS',
42 29: 'DR_ACCESS',
43 30: 'IO_INSTRUCTION',
44 31: 'MSR_READ',
45 32: 'MSR_WRITE',
46 33: 'INVALID_STATE',
47 36: 'MWAIT_INSTRUCTION',
48 39: 'MONITOR_INSTRUCTION',
49 40: 'PAUSE_INSTRUCTION',
50 41: 'MCE_DURING_VMENTRY',
51 43: 'TPR_BELOW_THRESHOLD',
52 44: 'APIC_ACCESS',
53 48: 'EPT_VIOLATION',
54 49: 'EPT_MISCONFIG',
55 54: 'WBINVD',
56 55: 'XSETBV',
57 }
58
59 svm_exit_reasons = {
60 0x000: 'READ_CR0',
61 0x003: 'READ_CR3',
62 0x004: 'READ_CR4',
63 0x008: 'READ_CR8',
64 0x010: 'WRITE_CR0',
65 0x013: 'WRITE_CR3',
66 0x014: 'WRITE_CR4',
67 0x018: 'WRITE_CR8',
68 0x020: 'READ_DR0',
69 0x021: 'READ_DR1',
70 0x022: 'READ_DR2',
71 0x023: 'READ_DR3',
72 0x024: 'READ_DR4',
73 0x025: 'READ_DR5',
74 0x026: 'READ_DR6',
75 0x027: 'READ_DR7',
76 0x030: 'WRITE_DR0',
77 0x031: 'WRITE_DR1',
78 0x032: 'WRITE_DR2',
79 0x033: 'WRITE_DR3',
80 0x034: 'WRITE_DR4',
81 0x035: 'WRITE_DR5',
82 0x036: 'WRITE_DR6',
83 0x037: 'WRITE_DR7',
84 0x040: 'EXCP_BASE',
85 0x060: 'INTR',
86 0x061: 'NMI',
87 0x062: 'SMI',
88 0x063: 'INIT',
89 0x064: 'VINTR',
90 0x065: 'CR0_SEL_WRITE',
91 0x066: 'IDTR_READ',
92 0x067: 'GDTR_READ',
93 0x068: 'LDTR_READ',
94 0x069: 'TR_READ',
95 0x06a: 'IDTR_WRITE',
96 0x06b: 'GDTR_WRITE',
97 0x06c: 'LDTR_WRITE',
98 0x06d: 'TR_WRITE',
99 0x06e: 'RDTSC',
100 0x06f: 'RDPMC',
101 0x070: 'PUSHF',
102 0x071: 'POPF',
103 0x072: 'CPUID',
104 0x073: 'RSM',
105 0x074: 'IRET',
106 0x075: 'SWINT',
107 0x076: 'INVD',
108 0x077: 'PAUSE',
109 0x078: 'HLT',
110 0x079: 'INVLPG',
111 0x07a: 'INVLPGA',
112 0x07b: 'IOIO',
113 0x07c: 'MSR',
114 0x07d: 'TASK_SWITCH',
115 0x07e: 'FERR_FREEZE',
116 0x07f: 'SHUTDOWN',
117 0x080: 'VMRUN',
118 0x081: 'VMMCALL',
119 0x082: 'VMLOAD',
120 0x083: 'VMSAVE',
121 0x084: 'STGI',
122 0x085: 'CLGI',
123 0x086: 'SKINIT',
124 0x087: 'RDTSCP',
125 0x088: 'ICEBP',
126 0x089: 'WBINVD',
127 0x08a: 'MONITOR',
128 0x08b: 'MWAIT',
129 0x08c: 'MWAIT_COND',
130 0x400: 'NPF',
131 }
132
133 vendor_exit_reasons = {
134 'vmx': vmx_exit_reasons,
135 'svm': svm_exit_reasons,
136 }
137
138 exit_reasons = None
139
140 for line in file('/proc/cpuinfo').readlines():
141 if line.startswith('flags'):
142 for flag in line.split():
143 if flag in vendor_exit_reasons:
144 exit_reasons = vendor_exit_reasons[flag]
145
146 filters = {
147 'kvm_exit': ('exit_reason', exit_reasons)
148 }
149
150 def invert(d):
151 return dict((x[1], x[0]) for x in d.iteritems())
152
153 for f in filters:
154 filters[f] = (filters[f][0], invert(filters[f][1]))
155
156 import ctypes, struct, array
157
158 libc = ctypes.CDLL('libc.so.6')
159 syscall = libc.syscall
160 class perf_event_attr(ctypes.Structure):
161 _fields_ = [('type', ctypes.c_uint32),
162 ('size', ctypes.c_uint32),
163 ('config', ctypes.c_uint64),
164 ('sample_freq', ctypes.c_uint64),
165 ('sample_type', ctypes.c_uint64),
166 ('read_format', ctypes.c_uint64),
167 ('flags', ctypes.c_uint64),
168 ('wakeup_events', ctypes.c_uint32),
169 ('bp_type', ctypes.c_uint32),
170 ('bp_addr', ctypes.c_uint64),
171 ('bp_len', ctypes.c_uint64),
172 ]
173 def _perf_event_open(attr, pid, cpu, group_fd, flags):
174 return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
175 ctypes.c_int(cpu), ctypes.c_int(group_fd),
176 ctypes.c_long(flags))
177
178 PERF_TYPE_HARDWARE = 0
179 PERF_TYPE_SOFTWARE = 1
180 PERF_TYPE_TRACEPOINT = 2
181 PERF_TYPE_HW_CACHE = 3
182 PERF_TYPE_RAW = 4
183 PERF_TYPE_BREAKPOINT = 5
184
185 PERF_SAMPLE_IP = 1 << 0
186 PERF_SAMPLE_TID = 1 << 1
187 PERF_SAMPLE_TIME = 1 << 2
188 PERF_SAMPLE_ADDR = 1 << 3
189 PERF_SAMPLE_READ = 1 << 4
190 PERF_SAMPLE_CALLCHAIN = 1 << 5
191 PERF_SAMPLE_ID = 1 << 6
192 PERF_SAMPLE_CPU = 1 << 7
193 PERF_SAMPLE_PERIOD = 1 << 8
194 PERF_SAMPLE_STREAM_ID = 1 << 9
195 PERF_SAMPLE_RAW = 1 << 10
196
197 PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
198 PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
199 PERF_FORMAT_ID = 1 << 2
200 PERF_FORMAT_GROUP = 1 << 3
201
202 import re
203
204 class TracepointProvider(object):
205 def __init__(self):
206 self.base = '/sys/kernel/debug/tracing/events/kvm/'
207 fields = [f
208 for f in os.listdir(self.base)
209 if os.path.isdir(self.base + '/' + f)]
210 extra = []
211 for f in fields:
212 if f in filters:
213 subfield, values = filters[f]
214 for name, number in values.iteritems():
215 extra.append(f + '(' + name + ')')
216 fields += extra
217 self.select(fields)
218 def fields(self):
219 return self._fields
220 def select(self, _fields):
221 self._fields = _fields
222 cpure = r'cpu([0-9]+)'
223 self.cpus = [int(re.match(cpure, x).group(1))
224 for x in os.listdir('/sys/devices/system/cpu')
225 if re.match(cpure, x)]
226 import resource
227 nfiles = len(self.cpus) * 1000
228 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
229 fds = []
230 self.group_leaders = []
231 for cpu in self.cpus:
232 group_leader = -1
233 for f in _fields:
234 fbase, sub = f, None
235 m = re.match(r'(.*)\((.*)\)', f)
236 if m:
237 fbase, sub = m.groups()
238 attr = perf_event_attr()
239 attr.type = PERF_TYPE_TRACEPOINT
240 attr.size = ctypes.sizeof(attr)
241 id = int(file(self.base + fbase + '/id').read())
242 attr.config = id
243 attr.sample_type = (PERF_SAMPLE_RAW
244 | PERF_SAMPLE_TIME
245 | PERF_SAMPLE_CPU)
246 attr.sample_period = 1
247 attr.read_format = PERF_FORMAT_GROUP
248 fd = _perf_event_open(attr, -1, cpu, group_leader, 0)
249 if fd == -1:
250 raise Exception('perf_event_open failed')
251 if sub:
252 import fcntl
253 filter = '%s==%d\0' % (filters[fbase][0],
254 filters[fbase][1][sub])
255 fcntl.ioctl(fd, 0x40082406, filter)
256 if group_leader == -1:
257 group_leader = fd
258 fds.append(fd)
259 self.group_leaders.append(group_leader)
260 self.fds = fds
261 self.files = [os.fdopen(group_leader)
262 for group_leader in self.group_leaders]
263 def read(self):
264 ret = dict([(f, 0) for f in self._fields])
265 bytes = 8 * (1 + len(self._fields))
266 fmt = 'xxxxxxxx' + 'q' * len(self._fields)
267 for file in self.files:
268 a = struct.unpack(fmt, file.read(bytes))
269 for field, val in zip(self._fields, a):
270 ret[field] += val
271 return ret
272
273 class Stats:
274 def __init__(self, provider, fields = None):
275 def wanted(key):
276 import re
277 if not fields:
278 return True
279 return re.match(fields, key) != None
280 self.provider = provider
281 self.values = dict([(key, None)
282 for key in provider.fields()
283 if wanted(key)])
284 self.provider.select(self.values.keys())
285 def get(self):
286 new = self.provider.read()
287 for key in self.provider.fields():
288 oldval = self.values[key]
289 newval = new[key]
290 newdelta = None
291 if oldval is not None:
292 newdelta = newval - oldval[0]
293 self.values[key] = (newval, newdelta)
294 return self.values
295
296 if not os.access('/sys/kernel/debug', os.F_OK):
297 print 'Please enable CONFIG_DEBUG_FS in your kernel'
298 sys.exit(1)
299 if not os.access('/sys/kernel/debug/kvm', os.F_OK):
300 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
301 print "and ensure the kvm modules are loaded"
302 sys.exit(1)
303
304 label_width = 40
305 number_width = 10
306
307 def tui(screen, stats):
308 curses.use_default_colors()
309 curses.noecho()
310 def refresh(sleeptime):
311 screen.erase()
312 screen.addstr(0, 0, 'kvm statistics')
313 row = 2
314 s = stats.get()
315 def sortkey(x):
316 if s[x][1]:
317 return (-s[x][1], -s[x][0])
318 else:
319 return (0, -s[x][0])
320 for key in sorted(s.keys(), key = sortkey):
321 if row >= screen.getmaxyx()[0]:
322 break
323 values = s[key]
324 if not values[0] and not values[1]:
325 break
326 col = 1
327 screen.addstr(row, col, key)
328 col += label_width
329 screen.addstr(row, col, '%10d' % (values[0],))
330 col += number_width
331 if values[1] is not None:
332 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
333 row += 1
334 screen.refresh()
335
336 sleeptime = 0.25
337 while True:
338 refresh(sleeptime)
339 curses.halfdelay(int(sleeptime * 10))
340 sleeptime = 3
341 try:
342 c = screen.getkey()
343 if c == 'q':
344 break
345 except KeyboardInterrupt:
346 break
347 except curses.error:
348 continue
349
350 def batch(stats):
351 s = stats.get()
352 time.sleep(1)
353 s = stats.get()
354 for key in sorted(s.keys()):
355 values = s[key]
356 print '%-22s%10d%10d' % (key, values[0], values[1])
357
358 def log(stats):
359 keys = sorted(stats.get().iterkeys())
360 def banner():
361 for k in keys:
362 print '%10s' % k[0:9],
363 print
364 def statline():
365 s = stats.get()
366 for k in keys:
367 print ' %9d' % s[k][1],
368 print
369 line = 0
370 banner_repeat = 20
371 while True:
372 time.sleep(1)
373 if line % banner_repeat == 0:
374 banner()
375 statline()
376 line += 1
377
378 options = optparse.OptionParser()
379 options.add_option('-1', '--once', '--batch',
380 action = 'store_true',
381 default = False,
382 dest = 'once',
383 help = 'run in batch mode for one second',
384 )
385 options.add_option('-l', '--log',
386 action = 'store_true',
387 default = False,
388 dest = 'log',
389 help = 'run in logging mode (like vmstat)',
390 )
391 options.add_option('-f', '--fields',
392 action = 'store',
393 default = None,
394 dest = 'fields',
395 help = 'fields to display (regex)',
396 )
397 (options, args) = options.parse_args(sys.argv)
398
399 try:
400 provider = TracepointProvider()
401 except:
402 provider = DebugfsProvider()
403
404 stats = Stats(provider, fields = options.fields)
405
406 if options.log:
407 log(stats)
408 elif not options.once:
409 import curses.wrapper
410 curses.wrapper(tui, stats)
411 else:
412 batch(stats)
Something went wrong with that request. Please try again.