summaryrefslogtreecommitdiff
path: root/seivots-summary
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2011-06-30 20:10:15 +0100
committerLars Wirzenius <liw@liw.fi>2011-06-30 20:10:15 +0100
commitc84ada0bb970781cc422113a9dd319315db1571f (patch)
tree380125def58e8e656429406eedaca3f44a266a5f /seivots-summary
parent1c172f8225d3d2a9f790dddee47af5b839740bad (diff)
downloadseivot-c84ada0bb970781cc422113a9dd319315db1571f.tar.gz
Add script to summarize a bunch of seivot files.
Diffstat (limited to 'seivots-summary')
-rwxr-xr-xseivots-summary238
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()
+