diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat index 4065b2909085..dd8f00cfb8b4 100755 --- a/tools/kvm/kvm_stat/kvm_stat +++ b/tools/kvm/kvm_stat/kvm_stat @@ -662,7 +662,7 @@ class TracepointProvider(Provider): self.setup_traces() self.fields = self._fields - def read(self): + def read(self, by_guest=0): """Returns 'event name: current value' for all enabled events.""" ret = defaultdict(int) for group in self.group_leaders: @@ -731,7 +731,7 @@ class DebugfsProvider(Provider): self.do_read = True self.reset() - def read(self, reset=0): + def read(self, reset=0, by_guest=0): """Returns a dict with format:'file name / field -> current value'. Parameter 'reset': @@ -762,8 +762,16 @@ class DebugfsProvider(Provider): self._baseline[key] = 0 if self._baseline.get(key, -1) == -1: self._baseline[key] = value - results[field] = (results.get(field, 0) + value - - self._baseline.get(key, 0)) + increment = (results.get(field, 0) + value - + self._baseline.get(key, 0)) + if by_guest: + pid = key.split('-')[0] + if pid in results: + results[pid] += increment + else: + results[pid] = increment + else: + results[field] = increment return results @@ -849,18 +857,44 @@ class Stats(object): for provider in self.providers: provider.pid = self._pid_filter - def get(self): + def get(self, by_guest=0): """Returns a dict with field -> (value, delta to last value) of all provider data.""" for provider in self.providers: - new = provider.read() - for key in provider.fields: + new = provider.read(by_guest=by_guest) + for key in new if by_guest else provider.fields: oldval = self.values.get(key, (0, 0))[0] newval = new.get(key, 0) newdelta = newval - oldval self.values[key] = (newval, newdelta) return self.values + def toggle_display_guests(self, to_pid): + """Toggle between collection of stats by individual event and by + guest pid + + Events reported by DebugfsProvider change when switching to/from + reading by guest values. Hence we have to remove the excess event + names from self.values. + + """ + if any(isinstance(ins, TracepointProvider) for ins in self.providers): + return 1 + if to_pid: + for provider in self.providers: + if isinstance(provider, DebugfsProvider): + for key in provider.fields: + if key in self.values.keys(): + del self.values[key] + else: + oldvals = self.values.copy() + for key in oldvals: + if key.isdigit(): + del self.values[key] + # Update oldval (see get()) + self.get(to_pid) + return 0 + DELAY_DEFAULT = 3.0 MAX_GUEST_NAME_LEN = 48 MAX_REGEX_LEN = 44 @@ -876,6 +910,7 @@ class Tui(object): self._delay_initial = 0.25 self._delay_regular = DELAY_DEFAULT self._sorting = SORT_DEFAULT + self._display_guests = 0 def __enter__(self): """Initialises curses for later use. Based on curses.wrapper @@ -1024,8 +1059,12 @@ class Tui(object): if len(regex) > MAX_REGEX_LEN: regex = regex[:MAX_REGEX_LEN] + '...' self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex)) + if self._display_guests: + col_name = 'Guest Name' + else: + col_name = 'Event' self.screen.addstr(2, 1, '%-40s %10s%7s %8s' % - ('Event', 'Total', '%Total', 'CurAvg/s'), + (col_name, 'Total', '%Total', 'CurAvg/s'), curses.A_STANDOUT) self.screen.addstr(4, 1, 'Collecting data...') self.screen.refresh() @@ -1034,7 +1073,7 @@ class Tui(object): row = 3 self.screen.move(row, 0) self.screen.clrtobot() - stats = self.stats.get() + stats = self.stats.get(self._display_guests) def sortCurAvg(x): # sort by current events if available @@ -1062,6 +1101,8 @@ class Tui(object): break if values[0] is not None: cur = int(round(values[1] / sleeptime)) if values[1] else '' + if self._display_guests: + key = self.get_gname_from_pid(key) self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % (key, values[0], values[0] * 100 / total, cur)) @@ -1070,9 +1111,26 @@ class Tui(object): self.screen.addstr(4, 1, 'No matching events reported yet') self.screen.refresh() + def show_msg(self, text): + """Display message centered text and exit on key press""" + hint = 'Press any key to continue' + curses.cbreak() + self.screen.erase() + (x, term_width) = self.screen.getmaxyx() + row = 2 + for line in text: + start = (term_width - len(line)) / 2 + self.screen.addstr(row, start, line) + row += 1 + self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint, + curses.A_STANDOUT) + self.screen.getkey() + def show_help_interactive(self): """Display help with list of interactive commands""" - msg = (' c clear filter', + msg = (' b toggle events by guests (debugfs only, honors' + ' filters)', + ' c clear filter', ' f filter by regular expression', ' g filter by guest name', ' h display interactive commands reference', @@ -1253,6 +1311,14 @@ class Tui(object): sleeptime = self._delay_regular try: char = self.screen.getkey() + if char == 'b': + self._display_guests = not self._display_guests + if self.stats.toggle_display_guests(self._display_guests): + self.show_msg(['Command not available with tracepoints' + ' enabled', 'Restart with debugfs only ' + '(see option \'-d\') and try again!']) + self._display_guests = not self._display_guests + self.refresh_header() if char == 'c': self.stats.fields_filter = DEFAULT_REGEX self.refresh_header(0) @@ -1356,6 +1422,7 @@ Requirements: the large number of files that are possibly opened. Interactive Commands: + b toggle events by guests (debugfs only, honors filters) c clear filter f filter by regular expression g filter by guest name diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt index 851372d263cc..e5cf836be8a1 100644 --- a/tools/kvm/kvm_stat/kvm_stat.txt +++ b/tools/kvm/kvm_stat/kvm_stat.txt @@ -29,6 +29,8 @@ meaning of events. INTERACTIVE COMMANDS -------------------- [horizontal] +*b*:: toggle events by guests (debugfs only, honors filters) + *c*:: clear filter *f*:: filter by regular expression