kvm_stat 14 KB

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