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