3 # top-like utility for displaying kvm statistics
5 # Copyright 2006-2008 Qumranet Technologies
6 # Copyright 2008-2011 Red Hat, Inc.
9 # Avi Kivity <avi@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2. See
12 # the COPYING file in the top-level directory.
15 import sys
, os
, time
, optparse
17 class DebugfsProvider(object):
19 self
.base
= '/sys/kernel/debug/kvm'
20 self
._fields
= os
.listdir(self
.base
)
23 def select(self
, fields
):
27 return int(file(self
.base
+ '/' + key
).read())
28 return dict([(key
, val(key
)) for key
in self
._fields
])
32 1: 'EXTERNAL_INTERRUPT',
34 7: 'PENDING_INTERRUPT',
58 36: 'MWAIT_INSTRUCTION',
59 39: 'MONITOR_INSTRUCTION',
60 40: 'PAUSE_INSTRUCTION',
61 41: 'MCE_DURING_VMENTRY',
62 43: 'TPR_BELOW_THRESHOLD',
101 0x065: 'CR0_SEL_WRITE',
125 0x07d: 'TASK_SWITCH',
126 0x07e: 'FERR_FREEZE',
144 s390_exit_reasons
= {
152 0x007: 'IRQ_WINDOW_OPEN',
162 0x017: 'INTERNAL_ERROR',
167 vendor_exit_reasons
= {
168 'vmx': vmx_exit_reasons
,
169 'svm': svm_exit_reasons
,
170 'IBM/S390': s390_exit_reasons
,
177 sc_perf_evt_open
= 298
181 for line
in file('/proc/cpuinfo').readlines():
182 if line
.startswith('flags') or line
.startswith('vendor_id'):
183 for flag
in line
.split():
184 if flag
in vendor_exit_reasons
:
185 exit_reasons
= vendor_exit_reasons
[flag
]
186 if flag
in syscall_numbers
:
187 sc_perf_evt_open
= syscall_numbers
[flag
]
189 'kvm_exit': ('exit_reason', exit_reasons
)
193 return dict((x
[1], x
[0]) for x
in d
.iteritems())
196 filters
[f
] = (filters
[f
][0], invert(filters
[f
][1]))
198 import ctypes
, struct
, array
200 libc
= ctypes
.CDLL('libc.so.6')
201 syscall
= libc
.syscall
202 class perf_event_attr(ctypes
.Structure
):
203 _fields_
= [('type', ctypes
.c_uint32
),
204 ('size', ctypes
.c_uint32
),
205 ('config', ctypes
.c_uint64
),
206 ('sample_freq', ctypes
.c_uint64
),
207 ('sample_type', ctypes
.c_uint64
),
208 ('read_format', ctypes
.c_uint64
),
209 ('flags', ctypes
.c_uint64
),
210 ('wakeup_events', ctypes
.c_uint32
),
211 ('bp_type', ctypes
.c_uint32
),
212 ('bp_addr', ctypes
.c_uint64
),
213 ('bp_len', ctypes
.c_uint64
),
215 def _perf_event_open(attr
, pid
, cpu
, group_fd
, flags
):
216 return syscall(sc_perf_evt_open
, ctypes
.pointer(attr
), ctypes
.c_int(pid
),
217 ctypes
.c_int(cpu
), ctypes
.c_int(group_fd
),
218 ctypes
.c_long(flags
))
220 PERF_TYPE_HARDWARE
= 0
221 PERF_TYPE_SOFTWARE
= 1
222 PERF_TYPE_TRACEPOINT
= 2
223 PERF_TYPE_HW_CACHE
= 3
225 PERF_TYPE_BREAKPOINT
= 5
227 PERF_SAMPLE_IP
= 1 << 0
228 PERF_SAMPLE_TID
= 1 << 1
229 PERF_SAMPLE_TIME
= 1 << 2
230 PERF_SAMPLE_ADDR
= 1 << 3
231 PERF_SAMPLE_READ
= 1 << 4
232 PERF_SAMPLE_CALLCHAIN
= 1 << 5
233 PERF_SAMPLE_ID
= 1 << 6
234 PERF_SAMPLE_CPU
= 1 << 7
235 PERF_SAMPLE_PERIOD
= 1 << 8
236 PERF_SAMPLE_STREAM_ID
= 1 << 9
237 PERF_SAMPLE_RAW
= 1 << 10
239 PERF_FORMAT_TOTAL_TIME_ENABLED
= 1 << 0
240 PERF_FORMAT_TOTAL_TIME_RUNNING
= 1 << 1
241 PERF_FORMAT_ID
= 1 << 2
242 PERF_FORMAT_GROUP
= 1 << 3
246 sys_tracing
= '/sys/kernel/debug/tracing'
249 def __init__(self
, cpu
):
251 self
.group_leader
= None
253 def add_event(self
, name
, event_set
, tracepoint
, filter = None):
254 self
.events
.append(Event(group
= self
,
255 name
= name
, event_set
= event_set
,
256 tracepoint
= tracepoint
, filter = filter))
257 if len(self
.events
) == 1:
258 self
.file = os
.fdopen(self
.events
[0].fd
)
260 bytes
= 8 * (1 + len(self
.events
))
261 fmt
= 'xxxxxxxx' + 'q' * len(self
.events
)
262 return dict(zip([event
.name
for event
in self
.events
],
263 struct
.unpack(fmt
, self
.file.read(bytes
))))
266 def __init__(self
, group
, name
, event_set
, tracepoint
, filter = None):
268 attr
= perf_event_attr()
269 attr
.type = PERF_TYPE_TRACEPOINT
270 attr
.size
= ctypes
.sizeof(attr
)
271 id_path
= os
.path
.join(sys_tracing
, 'events', event_set
,
273 id = int(file(id_path
).read())
275 attr
.sample_type
= (PERF_SAMPLE_RAW
278 attr
.sample_period
= 1
279 attr
.read_format
= PERF_FORMAT_GROUP
282 group_leader
= group
.events
[0].fd
283 fd
= _perf_event_open(attr
, -1, group
.cpu
, group_leader
, 0)
285 raise Exception('perf_event_open failed')
288 fcntl
.ioctl(fd
, 0x40082406, filter)
292 fcntl
.ioctl(self
.fd
, 0x00002400, 0)
295 fcntl
.ioctl(self
.fd
, 0x00002401, 0)
297 class TracepointProvider(object):
299 path
= os
.path
.join(sys_tracing
, 'events', 'kvm')
301 for f
in os
.listdir(path
)
302 if os
.path
.isdir(os
.path
.join(path
, f
))]
306 subfield
, values
= filters
[f
]
307 for name
, number
in values
.iteritems():
308 extra
.append(f
+ '(' + name
+ ')')
314 def _setup(self
, _fields
):
315 self
._fields
= _fields
316 cpure
= r
'cpu([0-9]+)'
317 self
.cpus
= [int(re
.match(cpure
, x
).group(1))
318 for x
in os
.listdir('/sys/devices/system/cpu')
319 if re
.match(cpure
, x
)]
321 nfiles
= len(self
.cpus
) * 1000
322 resource
.setrlimit(resource
.RLIMIT_NOFILE
, (nfiles
, nfiles
))
324 self
.group_leaders
= []
325 for cpu
in self
.cpus
:
330 m
= re
.match(r
'(.*)\((.*)\)', name
)
332 tracepoint
, sub
= m
.groups()
333 filter = '%s==%d\0' % (filters
[tracepoint
][0],
334 filters
[tracepoint
][1][sub
])
335 event
= group
.add_event(name
, event_set
= 'kvm',
336 tracepoint
= tracepoint
,
338 self
.group_leaders
.append(group
)
339 def select(self
, fields
):
340 for group
in self
.group_leaders
:
341 for event
in group
.events
:
342 if event
.name
in fields
:
347 from collections
import defaultdict
348 ret
= defaultdict(int)
349 for group
in self
.group_leaders
:
350 for name
, val
in group
.read().iteritems():
355 def __init__(self
, providers
, fields
= None):
356 self
.providers
= providers
357 self
.fields_filter
= fields
362 if not self
.fields_filter
:
364 return re
.match(self
.fields_filter
, key
) is not None
367 provider_fields
= [key
for key
in d
.fields() if wanted(key
)]
368 for key
in provider_fields
:
369 self
.values
[key
] = None
370 d
.select(provider_fields
)
371 def set_fields_filter(self
, fields_filter
):
372 self
.fields_filter
= fields_filter
377 for key
in d
.fields():
378 oldval
= self
.values
.get(key
, (0, 0))
381 if oldval
is not None:
382 newdelta
= newval
- oldval
[0]
383 self
.values
[key
] = (newval
, newdelta
)
386 if not os
.access('/sys/kernel/debug', os
.F_OK
):
387 print 'Please enable CONFIG_DEBUG_FS in your kernel'
389 if not os
.access('/sys/kernel/debug/kvm', os
.F_OK
):
390 print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
391 print "and ensure the kvm modules are loaded"
397 def tui(screen
, stats
):
398 curses
.use_default_colors()
401 fields_filter
= stats
.fields_filter
402 def update_drilldown():
403 if not fields_filter
:
405 stats
.set_fields_filter(None)
407 stats
.set_fields_filter(r
'^[^\(]*$')
409 def refresh(sleeptime
):
411 screen
.addstr(0, 0, 'kvm statistics')
416 return (-s
[x
][1], -s
[x
][0])
419 for key
in sorted(s
.keys(), key
= sortkey
):
420 if row
>= screen
.getmaxyx()[0]:
423 if not values
[0] and not values
[1]:
426 screen
.addstr(row
, col
, key
)
428 screen
.addstr(row
, col
, '%10d' % (values
[0],))
430 if values
[1] is not None:
431 screen
.addstr(row
, col
, '%8d' % (values
[1] / sleeptime
,))
438 curses
.halfdelay(int(sleeptime
* 10))
443 drilldown
= not drilldown
447 except KeyboardInterrupt:
456 for key
in sorted(s
.keys()):
458 print '%-22s%10d%10d' % (key
, values
[0], values
[1])
461 keys
= sorted(stats
.get().iterkeys())
464 print '%10s' % k
[0:9],
469 print ' %9d' % s
[k
][1],
475 if line
% banner_repeat
== 0:
480 options
= optparse
.OptionParser()
481 options
.add_option('-1', '--once', '--batch',
482 action
= 'store_true',
485 help = 'run in batch mode for one second',
487 options
.add_option('-l', '--log',
488 action
= 'store_true',
491 help = 'run in logging mode (like vmstat)',
493 options
.add_option('-t', '--tracepoints',
494 action
= 'store_true',
496 dest
= 'tracepoints',
497 help = 'retrieve statistics from tracepoints',
499 options
.add_option('-d', '--debugfs',
500 action
= 'store_true',
503 help = 'retrieve statistics from debugfs',
505 options
.add_option('-f', '--fields',
509 help = 'fields to display (regex)',
511 (options
, args
) = options
.parse_args(sys
.argv
)
514 if options
.tracepoints
:
515 providers
.append(TracepointProvider())
517 providers
.append(DebugfsProvider())
519 if len(providers
) == 0:
521 providers
= [TracepointProvider()]
523 providers
= [DebugfsProvider()]
525 stats
= Stats(providers
, fields
= options
.fields
)
529 elif not options
.once
:
530 import curses
.wrapper
531 curses
.wrapper(tui
, stats
)