Enable drilldown for kvm_exit reasons
cover Enable drilldown for kvm_exit reasons
kvm_stat currently tracks both tracepoints, and also kvm_exit by exit reason. This is somewhat cluttered and also very expensive with the current kernel filter implementation.
Make drilldown optional by adding a key to trigger it.
Avi Kivity (4):
kvm_stat: move groups and events into well defined objects
kvm_stat: add wrappers for perf_event enable and disable ioctls
kvm_stat: allow enabling/disabling events dynamicalls
kvm_stat: add 'x' key for enabling/disabling "drilldown"
kvm/kvm_stat | 149 ++++++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 103 insertions(+), 46 deletions(-)
PATCH 1/4 kvm_stat: move groups and events into well defined objects
--- kvm/kvm_stat | 107 +++++++++++++++++++++++++++++++++++---------------------- 1 files changed, 66 insertions(+), 41 deletions(-)
kvm/kvm_stat +66 -41
@@ -201,12 +201,57 @@ PERF_FORMAT_GROUP = 1 << 3 import re +sys_tracing = '/sys/kernel/debug/tracing' + +class Group(object): + def __init__(self, cpu): + self.events = [] + self.group_leader = None + self.cpu = cpu + def add_event(self, name, event_set, tracepoint, filter = None): + self.events.append(Event(group = self, + name = name, event_set = event_set, + tracepoint = tracepoint, filter = filter)) + if len(self.events) == 1: + self.file = os.fdopen(self.events[0].fd) + def read(self): + bytes = 8 * (1 + len(self.events)) + fmt = 'xxxxxxxx' + 'q' * len(self.events) + return dict(zip([event.name for event in self.events], + struct.unpack(fmt, self.file.read(bytes)))) + +class Event(object): + def __init__(self, group, name, event_set, tracepoint, filter = None): + self.name = name + attr = perf_event_attr() + attr.type = PERF_TYPE_TRACEPOINT + attr.size = ctypes.sizeof(attr) + id_path = os.path.join(sys_tracing, 'events', event_set, + tracepoint, 'id') + id = int(file(id_path).read()) + attr.config = id + attr.sample_type = (PERF_SAMPLE_RAW + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU) + attr.sample_period = 1 + attr.read_format = PERF_FORMAT_GROUP + group_leader = -1 + if group.events: + group_leader = group.events[0].fd + fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) + if fd == -1: + raise Exception('perf_event_open failed') + if filter: + import fcntl + fcntl.ioctl(fd, 0x40082406, filter) + self.fd = fd + class TracepointProvider(object): def __init__(self): - self.base = '/sys/kernel/debug/tracing/events/kvm/' + path = os.path.join(sys_tracing, 'events', 'kvm') fields = [f - for f in os.listdir(self.base) - if os.path.isdir(self.base + '/' + f)] + for f in os.listdir(path) + if os.path.isdir(os.path.join(path, f))] extra = [] for f in fields: if f in filters:
@@ -226,48 +271,28 @@ class TracepointProvider(object): import resource nfiles = len(self.cpus) * 1000 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) - fds = [] + events = [] self.group_leaders = [] for cpu in self.cpus: - group_leader = -1 - for f in _fields: - fbase, sub = f, None - m = re.match(r'(.*)\((.*)\)', f) + group = Group(cpu) + for name in _fields: + tracepoint = name + filter = None + m = re.match(r'(.*)\((.*)\)', name) if m: - fbase, sub = m.groups() - attr = perf_event_attr() - attr.type = PERF_TYPE_TRACEPOINT - attr.size = ctypes.sizeof(attr) - id = int(file(self.base + fbase + '/id').read()) - attr.config = id - attr.sample_type = (PERF_SAMPLE_RAW - | PERF_SAMPLE_TIME - | PERF_SAMPLE_CPU) - attr.sample_period = 1 - attr.read_format = PERF_FORMAT_GROUP - fd = _perf_event_open(attr, -1, cpu, group_leader, 0) - if fd == -1: - raise Exception('perf_event_open failed') - if sub: - import fcntl - filter = '%s==%d\0' % (filters[fbase][0], - filters[fbase][1][sub]) - fcntl.ioctl(fd, 0x40082406, filter) - if group_leader == -1: - group_leader = fd - fds.append(fd) - self.group_leaders.append(group_leader) - self.fds = fds - self.files = [os.fdopen(group_leader) - for group_leader in self.group_leaders] + tracepoint, sub = m.groups() + filter = '%s==%d\0' % (filters[tracepoint][0], + filters[tracepoint][1][sub]) + event = group.add_event(name, event_set = 'kvm', + tracepoint = tracepoint, + filter = filter) + self.group_leaders.append(group) def read(self): - ret = dict([(f, 0) for f in self._fields]) - bytes = 8 * (1 + len(self._fields)) - fmt = 'xxxxxxxx' + 'q' * len(self._fields) - for file in self.files: - a = struct.unpack(fmt, file.read(bytes)) - for field, val in zip(self._fields, a): - ret[field] += val + from collections import defaultdict + ret = defaultdict(int) + for group in self.group_leaders: + for name, val in group.read().iteritems(): + ret[name] += val return ret class Stats:
Signature · 1 line
1.7.1
PATCH 2/4 kvm_stat: add wrappers for perf_event enable and disable ioctls
In reply to Avi Kivity--- kvm/kvm_stat | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
kvm/kvm_stat +6 -0
@@ -245,6 +245,12 @@ class Event(object): import fcntl fcntl.ioctl(fd, 0x40082406, filter) self.fd = fd + def enable(self): + import fcntl + fcntl.ioctl(self.fd, 0x00002400, 0) + def disable(self): + import fcntl + fcntl.ioctl(self.fd, 0x00002401, 0) class TracepointProvider(object): def __init__(self):
Signature · 1 line
1.7.1
PATCH 3/4 kvm_stat: allow enabling/disabling events dynamicalls
In reply to Avi Kivity--- kvm/kvm_stat | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
kvm/kvm_stat +9 -1
@@ -265,10 +265,11 @@ class TracepointProvider(object): for name, number in values.iteritems(): extra.append(f + '(' + name + ')') fields += extra + self._setup(fields) self.select(fields) def fields(self): return self._fields - def select(self, _fields): + def _setup(self, _fields): self._fields = _fields cpure = r'cpu([0-9]+)' self.cpus = [int(re.match(cpure, x).group(1))
@@ -293,6 +294,13 @@ class TracepointProvider(object): tracepoint = tracepoint, filter = filter) self.group_leaders.append(group) + def select(self, fields): + for group in self.group_leaders: + for event in group.events: + if event.name in fields: + event.enable() + else: + event.disable() def read(self): from collections import defaultdict ret = defaultdict(int)
Signature · 1 line
1.7.1
PATCH 4/4 kvm_stat: add 'x' key for enabling/disabling "drilldown"
In reply to Avi KivityPressing 'x' enables drilldown into kvm_exit reasons. Pressing it again reverts to normal behaviour.
--- kvm/kvm_stat | 26 ++++++++++++++++++++++---- 1 files changed, 22 insertions(+), 4 deletions(-)
kvm/kvm_stat +22 -4
@@ -311,20 +311,26 @@ class TracepointProvider(object): class Stats: def __init__(self, provider, fields = None): + self.provider = provider + self.fields_filter = fields + self._update() + def _update(self): def wanted(key): import re - if not fields: + if not self.fields_filter: return True - return re.match(fields, key) != None - self.provider = provider + return re.match(self.fields_filter, key) is not None self.values = dict([(key, None) for key in provider.fields() if wanted(key)]) self.provider.select(self.values.keys()) + def set_fields_filter(self, fields_filter): + self.fields_filter = fields_filter + self._update() def get(self): new = self.provider.read() for key in self.provider.fields(): - oldval = self.values[key] + oldval = self.values.get(key, (0, 0)) newval = new[key] newdelta = None if oldval is not None:
@@ -346,6 +352,15 @@ number_width = 10 def tui(screen, stats): curses.use_default_colors() curses.noecho() + drilldown = False + fields_filter = stats.fields_filter + def update_drilldown(): + if not fields_filter: + if drilldown: + stats.set_fields_filter(None) + else: + stats.set_fields_filter(r'^[^\(]*$') + update_drilldown() def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics')
@@ -379,6 +394,9 @@ def tui(screen, stats): sleeptime = 3 try: c = screen.getkey() + if c == 'x': + drilldown = not drilldown + update_drilldown() if c == 'q': break except KeyboardInterrupt:
Signature · 1 line
1.7.1