2
0

kvm_stat 15 KB

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