diff options
author | Lars Wirzenius <liw@liw.fi> | 2011-06-30 20:10:15 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2011-06-30 20:10:15 +0100 |
commit | c84ada0bb970781cc422113a9dd319315db1571f (patch) | |
tree | 380125def58e8e656429406eedaca3f44a266a5f /seivots-summary | |
parent | 1c172f8225d3d2a9f790dddee47af5b839740bad (diff) | |
download | seivot-c84ada0bb970781cc422113a9dd319315db1571f.tar.gz |
Add script to summarize a bunch of seivot files.
Diffstat (limited to 'seivots-summary')
-rwxr-xr-x | seivots-summary | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/seivots-summary b/seivots-summary new file mode 100755 index 0000000..1cb3fdf --- /dev/null +++ b/seivots-summary @@ -0,0 +1,238 @@ +#!/usr/bin/python +# Copyright 2011 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +import cliapp +import ConfigParser +import logging +import os + + +class Table(object): + + '''Represent tabular data for formatting purposes.''' + + sep = ' ' + + def __init__(self): + self.caption = None + self.columns = [] + self.rows = [] + + def add_column(self, heading1, heading2, format): + self.columns.append((heading1, heading2, format)) + + def add_row(self, data): + assert len(data) == len(self.columns) + self.rows.append(data) + + def write_plaintext(self, f): + if self.caption: + f.write('%s\n%s\n\n' % (self.caption, '-' * len(self.caption))) + + cells = [] + cells.append([h1 for h1, h2, format in self.columns]) + cells.append([h2 for h1, h2, format in self.columns]) + for row in self.rows: + cells.append([self.format_cell(row[i], self.columns[i][2]) + for i in range(len(self.columns))]) + + widths = self.compute_column_widths(cells) + + f.write('%s\n' % self.format_headings(widths, 0)) + f.write('%s\n' % self.format_headings(widths, 1)) + for row in self.rows: + f.write('%s\n' % self.format_row(row, widths)) + + def format_cell(self, data, format): + return format % data + + def compute_column_widths(self, cells): + widths = [0] * len(self.columns) + for row in cells: + for i, data in enumerate(row): + widths[i] = max(widths[i], len(data)) + return widths + + def format_headings(self, widths, which): + headings = ['%*s' % (widths[i], self.columns[i][which]) + for i in range(len(widths))] + return self.sep.join(headings) + + def format_row(self, row, widths): + cells = ['%*s' % (widths[i], self.columns[i][2] % row[i]) + for i in range(len(widths))] + return self.sep.join(cells) + + +class SeivotsSummary(cliapp.Application): + + def process_args(self, args): + seivots = [self.read_seivot(x) for x in args] + + sizes = self.find_initial_sizes(seivots) + for size in sorted(sizes): + group = self.find_seivots_in_size_group(seivots, size) + group.sort(key=self.getkey) + for op in ['backup', 'restore', 'list_files', 'forget']: + table = self.make_table(group, op) + table.caption = '%s: %.0f MiB' % (op, self.disksize(size)) + table.write_plaintext(self.output) + self.output.write('\n') + + def make_table(self, group, op): + cols = { + 'obnam-revno': ('obnam', 'revno', '%s', self.get_obnam_revision), + 'larch-revno': ('larch', 'revno', '%s', self.get_larch_revision), + 'gen0-speed': ('gen0', 'Mbit/s', '%.1f', self.get_gen0_speed), + 'gen0-time': ('gen0', 's', '%.1f', self.get_gen0_time), + 'gen0-ram': ('gen0', 'MiB', '%.1f', self.get_gen0_ram), + 'slowest-speed': ('slowest', 'Mbit/s', '%.1f', + self.get_slowest_speed), + 'slowest-time': ('slowest', 's', '%.1f', self.get_slowest_time), + 'biggest-ram': ('biggest', 'MiB', '%.1f', self.get_biggest_ram), + 'repo-size': ('repo size', 'MiB', '%.1f', self.get_repo_size), + 'repo-writes': ('r-writes', 'MiB', '%.1f', self.get_repo_writes), + 'repo-reads': ('r-reads', 'MiB', '%.1f', self.get_repo_reads), + 'branch': ('branch', '', '%s', self.get_branch), + } + + prefix = ('obnam-revno', 'larch-revno') + which = { + 'backup': + ('gen0-speed', 'gen0-ram', 'slowest-speed', 'biggest-ram', + 'repo-size', 'repo-writes', 'repo-reads'), + 'restore': + ('gen0-speed', 'gen0-ram', 'slowest-speed', 'biggest-ram', + 'repo-writes', 'repo-reads'), + 'list_files': + ('gen0-time', 'gen0-ram', 'slowest-time', 'biggest-ram', + 'repo-writes', 'repo-reads'), + 'forget': + ('gen0-time', 'gen0-ram', 'slowest-time', 'biggest-ram', + 'repo-writes', 'repo-reads'), + } + suffix = ('branch',) + colnames = prefix + which[op] + suffix + + table = Table() + for colname in colnames: + h1, h2, fmt, func = cols[colname] + table.add_column(h1, h2, fmt) + + for seivot in group: + row = [] + for colname in colnames: + logging.debug('colname: %s' % colname) + h1, h2, fmt, func = cols[colname] + value = func(seivot, op) + logging.debug('value: %s' % repr(value)) + row.append(value) + table.add_row(row) + + return table + + def read_seivot(self, filename): + cp = ConfigParser.ConfigParser() + cp.read([filename]) + cp.set('meta', 'branch', os.path.basename(os.path.dirname(filename))) + return cp + + def getkey(self, seivot): + return (seivot.get('meta', 'revision'), + seivot.get('meta', 'larch-revision')) + + def get_size(self, seivot): + return seivot.getint('0', 'backup.new-data') + + def find_initial_sizes(self, seivots): + return list(set(self.get_size(s) for s in seivots)) + + def find_seivots_in_size_group(self, seivots, size): + return [s for s in seivots if self.get_size(s) == size] + + def values(self, op, suffix, seivot): + for section in seivot.sections(): + if section not in ['meta', '0']: + yield section, seivot.getfloat(section, '%s.%s' % (op,suffix)) + + def find_slowest_incremental(self, op, seivot): + v = list(self.values(op, 'real', seivot)) + if not v: + return '0' + return min(v, key=lambda pair: pair[1])[0] + + def get_obnam_revision(self, seivot, op): + return seivot.get('meta', 'revision') + + def get_larch_revision(self, seivot, op): + return seivot.get('meta', 'larch-revision') + + def get_branch(self, seivot, op): + return seivot.get('meta', 'branch') + + def get_gen0_speed(self, seivot, op): + bytes = seivot.getfloat('0', 'backup.new-data') + secs = self.get_gen0_time(seivot, op) + return self.xferspeed(bytes, secs) + + def get_gen0_time(self, seivot, op): + return seivot.getfloat('0', '%s.real' % op) + + def get_gen0_ram(self, seivot, op): + return self.ramsize(seivot.getfloat('0', '%s.maxrss' % op) * 1024) + + def get_slowest_speed(self, seivot, op): + gen = self.find_slowest_incremental(op, seivot) + bytes = seivot.getfloat(gen, 'backup.new-data') + secs = seivot.getfloat(gen, '%s.real' % op) + return self.xferspeed(bytes, secs) + + def get_slowest_time(self, seivot, op): + gen = self.find_slowest_incremental(op, seivot) + secs = seivot.getfloat(gen, '%s.real' % op) + return secs + + def get_biggest_ram(self, seivot, op): + pair = min(self.values(op, 'maxrss', seivot), + key=lambda pair: pair[1]) + kilobytes = float(pair[1]) + return self.ramsize(kilobytes * 1024) + + def get_repo_size(self, seivot, op): + return self.disksize(seivot.getfloat('0', 'backup.new-data')) + + def get_repo_writes(self, seivot, op): + bytes = seivot.getfloat('0', '%s.repo-bytes-written' % op) + return self.disksize(bytes) + + def get_repo_reads(self, seivot, op): + bytes = seivot.getfloat('0', '%s.repo-bytes-read' % op) + return self.disksize(bytes) + + def xferspeed(self, bytes, seconds): + return 8.0 * bytes / seconds / (1000**2) + + def ramsize(self, bytes): + return float(bytes) / (1024**2) + + def disksize(self, bytes): + return float(bytes) / (1024**2) + + +if __name__ == '__main__': + SeivotsSummary().run() + |