Annotation of qemu/scripts/kvm/kvm_stat, revision 1.1.1.1

1.1       root        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
                     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: vendor_exit_reasons = {
                    145:     'vmx': vmx_exit_reasons,
                    146:     'svm': svm_exit_reasons,
                    147: }
                    148: 
                    149: exit_reasons = None
                    150: 
                    151: for line in file('/proc/cpuinfo').readlines():
                    152:     if line.startswith('flags'):
                    153:         for flag in line.split():
                    154:             if flag in vendor_exit_reasons:
                    155:                 exit_reasons = vendor_exit_reasons[flag]
                    156: 
                    157: filters = {
                    158:     'kvm_exit': ('exit_reason', exit_reasons)
                    159: }
                    160: 
                    161: def invert(d):
                    162:     return dict((x[1], x[0]) for x in d.iteritems())
                    163: 
                    164: for f in filters:
                    165:     filters[f] = (filters[f][0], invert(filters[f][1]))
                    166: 
                    167: import ctypes, struct, array
                    168: 
                    169: libc = ctypes.CDLL('libc.so.6')
                    170: syscall = libc.syscall
                    171: class perf_event_attr(ctypes.Structure):
                    172:     _fields_ = [('type', ctypes.c_uint32),
                    173:                 ('size', ctypes.c_uint32),
                    174:                 ('config', ctypes.c_uint64),
                    175:                 ('sample_freq', ctypes.c_uint64),
                    176:                 ('sample_type', ctypes.c_uint64),
                    177:                 ('read_format', ctypes.c_uint64),
                    178:                 ('flags', ctypes.c_uint64),
                    179:                 ('wakeup_events', ctypes.c_uint32),
                    180:                 ('bp_type', ctypes.c_uint32),
                    181:                 ('bp_addr', ctypes.c_uint64),
                    182:                 ('bp_len', ctypes.c_uint64),
                    183:                 ]
                    184: def _perf_event_open(attr, pid, cpu, group_fd, flags):
                    185:     return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
                    186:                    ctypes.c_int(cpu), ctypes.c_int(group_fd),
                    187:                    ctypes.c_long(flags))
                    188: 
                    189: PERF_TYPE_HARDWARE              = 0
                    190: PERF_TYPE_SOFTWARE              = 1
                    191: PERF_TYPE_TRACEPOINT            = 2
                    192: PERF_TYPE_HW_CACHE              = 3
                    193: PERF_TYPE_RAW                   = 4
                    194: PERF_TYPE_BREAKPOINT            = 5
                    195: 
                    196: PERF_SAMPLE_IP                  = 1 << 0
                    197: PERF_SAMPLE_TID                 = 1 << 1
                    198: PERF_SAMPLE_TIME                = 1 << 2
                    199: PERF_SAMPLE_ADDR                = 1 << 3
                    200: PERF_SAMPLE_READ                = 1 << 4
                    201: PERF_SAMPLE_CALLCHAIN           = 1 << 5
                    202: PERF_SAMPLE_ID                  = 1 << 6
                    203: PERF_SAMPLE_CPU                 = 1 << 7
                    204: PERF_SAMPLE_PERIOD              = 1 << 8
                    205: PERF_SAMPLE_STREAM_ID           = 1 << 9
                    206: PERF_SAMPLE_RAW                 = 1 << 10
                    207: 
                    208: PERF_FORMAT_TOTAL_TIME_ENABLED  = 1 << 0
                    209: PERF_FORMAT_TOTAL_TIME_RUNNING  = 1 << 1
                    210: PERF_FORMAT_ID                  = 1 << 2
                    211: PERF_FORMAT_GROUP               = 1 << 3
                    212: 
                    213: import re
                    214: 
                    215: sys_tracing = '/sys/kernel/debug/tracing'
                    216: 
                    217: class Group(object):
                    218:     def __init__(self, cpu):
                    219:         self.events = []
                    220:         self.group_leader = None
                    221:         self.cpu = cpu
                    222:     def add_event(self, name, event_set, tracepoint, filter = None):
                    223:         self.events.append(Event(group = self,
                    224:                                  name = name, event_set = event_set,
                    225:                                  tracepoint = tracepoint, filter = filter))
                    226:         if len(self.events) == 1:
                    227:             self.file = os.fdopen(self.events[0].fd)
                    228:     def read(self):
                    229:         bytes = 8 * (1 + len(self.events))
                    230:         fmt = 'xxxxxxxx' + 'q' * len(self.events)
                    231:         return dict(zip([event.name for event in self.events],
                    232:                         struct.unpack(fmt, self.file.read(bytes))))
                    233: 
                    234: class Event(object):
                    235:     def __init__(self, group, name, event_set, tracepoint, filter = None):
                    236:         self.name = name
                    237:         attr = perf_event_attr()
                    238:         attr.type = PERF_TYPE_TRACEPOINT
                    239:         attr.size = ctypes.sizeof(attr)
                    240:         id_path = os.path.join(sys_tracing, 'events', event_set,
                    241:                                tracepoint, 'id')
                    242:         id = int(file(id_path).read())
                    243:         attr.config = id
                    244:         attr.sample_type = (PERF_SAMPLE_RAW
                    245:                             | PERF_SAMPLE_TIME
                    246:                             | PERF_SAMPLE_CPU)
                    247:         attr.sample_period = 1
                    248:         attr.read_format = PERF_FORMAT_GROUP
                    249:         group_leader = -1
                    250:         if group.events:
                    251:             group_leader = group.events[0].fd
                    252:         fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
                    253:         if fd == -1:
                    254:             raise Exception('perf_event_open failed')
                    255:         if filter:
                    256:             import fcntl
                    257:             fcntl.ioctl(fd, 0x40082406, filter)
                    258:         self.fd = fd
                    259:     def enable(self):
                    260:         import fcntl
                    261:         fcntl.ioctl(self.fd, 0x00002400, 0)
                    262:     def disable(self):
                    263:         import fcntl
                    264:         fcntl.ioctl(self.fd, 0x00002401, 0)
                    265: 
                    266: class TracepointProvider(object):
                    267:     def __init__(self):
                    268:         path = os.path.join(sys_tracing, 'events', 'kvm')
                    269:         fields = [f
                    270:                   for f in os.listdir(path)
                    271:                   if os.path.isdir(os.path.join(path, f))]
                    272:         extra = []
                    273:         for f in fields:
                    274:             if f in filters:
                    275:                 subfield, values = filters[f]
                    276:                 for name, number in values.iteritems():
                    277:                     extra.append(f + '(' + name + ')')
                    278:         fields += extra
                    279:         self._setup(fields)
                    280:         self.select(fields)
                    281:     def fields(self):
                    282:         return self._fields
                    283:     def _setup(self, _fields):
                    284:         self._fields = _fields
                    285:         cpure = r'cpu([0-9]+)'
                    286:         self.cpus = [int(re.match(cpure, x).group(1))
                    287:                      for x in os.listdir('/sys/devices/system/cpu')
                    288:                      if re.match(cpure, x)]
                    289:         import resource
                    290:         nfiles = len(self.cpus) * 1000
                    291:         resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
                    292:         events = []
                    293:         self.group_leaders = []
                    294:         for cpu in self.cpus:
                    295:             group = Group(cpu)
                    296:             for name in _fields:
                    297:                 tracepoint = name
                    298:                 filter = None
                    299:                 m = re.match(r'(.*)\((.*)\)', name)
                    300:                 if m:
                    301:                     tracepoint, sub = m.groups()
                    302:                     filter = '%s==%d\0' % (filters[tracepoint][0],
                    303:                                            filters[tracepoint][1][sub])
                    304:                 event = group.add_event(name, event_set = 'kvm',
                    305:                                         tracepoint = tracepoint,
                    306:                                         filter = filter)
                    307:             self.group_leaders.append(group)
                    308:     def select(self, fields):
                    309:         for group in self.group_leaders:
                    310:             for event in group.events:
                    311:                 if event.name in fields:
                    312:                     event.enable()
                    313:                 else:
                    314:                     event.disable()
                    315:     def read(self):
                    316:         from collections import defaultdict
                    317:         ret = defaultdict(int)
                    318:         for group in self.group_leaders:
                    319:             for name, val in group.read().iteritems():
                    320:                 ret[name] += val
                    321:         return ret
                    322: 
                    323: class Stats:
                    324:     def __init__(self, provider, fields = None):
                    325:         self.provider = provider
                    326:         self.fields_filter = fields
                    327:         self._update()
                    328:     def _update(self):
                    329:         def wanted(key):
                    330:             import re
                    331:             if not self.fields_filter:
                    332:                 return True
                    333:             return re.match(self.fields_filter, key) is not None
                    334:         self.values = dict([(key, None)
                    335:                             for key in provider.fields()
                    336:                             if wanted(key)])
                    337:         self.provider.select(self.values.keys())
                    338:     def set_fields_filter(self, fields_filter):
                    339:         self.fields_filter = fields_filter
                    340:         self._update()
                    341:     def get(self):
                    342:         new = self.provider.read()
                    343:         for key in self.provider.fields():
                    344:             oldval = self.values.get(key, (0, 0))
                    345:             newval = new[key]
                    346:             newdelta = None
                    347:             if oldval is not None:
                    348:                 newdelta = newval - oldval[0]
                    349:             self.values[key] = (newval, newdelta)
                    350:         return self.values
                    351: 
                    352: if not os.access('/sys/kernel/debug', os.F_OK):
                    353:     print 'Please enable CONFIG_DEBUG_FS in your kernel'
                    354:     sys.exit(1)
                    355: if not os.access('/sys/kernel/debug/kvm', os.F_OK):
                    356:     print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
                    357:     print "and ensure the kvm modules are loaded"
                    358:     sys.exit(1)
                    359: 
                    360: label_width = 40
                    361: number_width = 10
                    362: 
                    363: def tui(screen, stats):
                    364:     curses.use_default_colors()
                    365:     curses.noecho()
                    366:     drilldown = False
                    367:     fields_filter = stats.fields_filter
                    368:     def update_drilldown():
                    369:         if not fields_filter:
                    370:             if drilldown:
                    371:                 stats.set_fields_filter(None)
                    372:             else:
                    373:                 stats.set_fields_filter(r'^[^\(]*$')
                    374:     update_drilldown()
                    375:     def refresh(sleeptime):
                    376:         screen.erase()
                    377:         screen.addstr(0, 0, 'kvm statistics')
                    378:         row = 2
                    379:         s = stats.get()
                    380:         def sortkey(x):
                    381:             if s[x][1]:
                    382:                 return (-s[x][1], -s[x][0])
                    383:             else:
                    384:                 return (0, -s[x][0])
                    385:         for key in sorted(s.keys(), key = sortkey):
                    386:             if row >= screen.getmaxyx()[0]:
                    387:                 break
                    388:             values = s[key]
                    389:             if not values[0] and not values[1]:
                    390:                 break
                    391:             col = 1
                    392:             screen.addstr(row, col, key)
                    393:             col += label_width
                    394:             screen.addstr(row, col, '%10d' % (values[0],))
                    395:             col += number_width
                    396:             if values[1] is not None:
                    397:                 screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
                    398:             row += 1
                    399:         screen.refresh()
                    400: 
                    401:     sleeptime = 0.25
                    402:     while True:
                    403:         refresh(sleeptime)
                    404:         curses.halfdelay(int(sleeptime * 10))
                    405:         sleeptime = 3
                    406:         try:
                    407:             c = screen.getkey()
                    408:             if c == 'x':
                    409:                 drilldown = not drilldown
                    410:                 update_drilldown()
                    411:             if c == 'q':
                    412:                 break
                    413:         except KeyboardInterrupt:
                    414:             break
                    415:         except curses.error:
                    416:             continue
                    417: 
                    418: def batch(stats):
                    419:     s = stats.get()
                    420:     time.sleep(1)
                    421:     s = stats.get()
                    422:     for key in sorted(s.keys()):
                    423:         values = s[key]
                    424:         print '%-22s%10d%10d' % (key, values[0], values[1])
                    425: 
                    426: def log(stats):
                    427:     keys = sorted(stats.get().iterkeys())
                    428:     def banner():
                    429:         for k in keys:
                    430:             print '%10s' % k[0:9],
                    431:         print
                    432:     def statline():
                    433:         s = stats.get()
                    434:         for k in keys:
                    435:             print ' %9d' % s[k][1],
                    436:         print
                    437:     line = 0
                    438:     banner_repeat = 20
                    439:     while True:
                    440:         time.sleep(1)
                    441:         if line % banner_repeat == 0:
                    442:             banner()
                    443:         statline()
                    444:         line += 1
                    445: 
                    446: options = optparse.OptionParser()
                    447: options.add_option('-1', '--once', '--batch',
                    448:                    action = 'store_true',
                    449:                    default = False,
                    450:                    dest = 'once',
                    451:                    help = 'run in batch mode for one second',
                    452:                    )
                    453: options.add_option('-l', '--log',
                    454:                    action = 'store_true',
                    455:                    default = False,
                    456:                    dest = 'log',
                    457:                    help = 'run in logging mode (like vmstat)',
                    458:                    )
                    459: options.add_option('-f', '--fields',
                    460:                    action = 'store',
                    461:                    default = None,
                    462:                    dest = 'fields',
                    463:                    help = 'fields to display (regex)',
                    464:                    )
                    465: (options, args) = options.parse_args(sys.argv)
                    466: 
                    467: try:
                    468:     provider = TracepointProvider()
                    469: except:
                    470:     provider = DebugfsProvider()
                    471: 
                    472: stats = Stats(provider, fields = options.fields)
                    473: 
                    474: if options.log:
                    475:     log(stats)
                    476: elif not options.once:
                    477:     import curses.wrapper
                    478:     curses.wrapper(tui, stats)
                    479: else:
                    480:     batch(stats)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.