#!/usr/bin/env python import defapp from os.path import splitext, basename from optparse import make_option as o from tempfile import NamedTemporaryFile as Tmp from gnuplot import gnuplot, FORMATS, Plot, label, curve options = [ o('-f', '--format', action='store', dest='format', type='choice', choices=FORMATS, help='output format'), o(None, '--paper', action='store_true', dest='paper'), o(None, '--wide', action='store_true', dest='wide'), o(None, '--column', action='store_true', dest='column'), o(None, '--split', action='store_true', dest='split'), o(None, '--alternate', action='store_true', dest='alternate'), o(None, '--save-script', action='store_true', dest='save_script'), ] defaults = { 'format' : 'show', 'xrange' : (0.5, 32.5), 'yrange' : (-0.05, 1.05), 'xticks' : (0, 2), 'yticks' : (0, 0.1), 'title' : None, 'xlabel' : 'task set utilization cap (prior to inflation)', 'ylabel' : 'ratio of schedulable task sets', 'paper' : False, 'split' : False, 'wide' : False, 'alternate' : False, 'column' : False, 'save_script' : False, } def decode(name): params = {} parts = name.split('_') for p in parts: kv = p.split('=') k = kv[0] v = kv[1] if len(kv) > 1 else None params[k] = v return params def get_data_tmpfile(datafile, target=None): """Removes all comments form datafile, stores result in a temp file. The temp file is returned.""" count = 0 f = open(datafile, 'r') if target: d = open(target, 'w') else: d = Tmp() for line in f: if len(line) > 1 and line[0] != '#': d.write(line) count += 1 f.close() d.flush() if count > 0: return d else: del d # removes temp file return None def scenario_heading(conf, want_period=False): dist = 'unknown distribution' if 'dist' in conf: if conf['dist'] == 'uni': dist = 'utilization uniformly ' if 'light' in conf: dist = dist + 'in [0.001, 0.1]' elif 'medium' in conf: dist = dist + 'in [0.1, 0.4]' elif 'heavy' in conf: dist = dist + 'in [0.5, 0.9]' elif 'mixed' in conf: dist = dist + 'in [0.05, 0.95]' elif conf['dist'] == 'bimo': dist = 'util. bimodially ' if 'light' in conf: dist = dist + 'in [0.001, 0.5] (8/9) and [0.5, 0.9] (1/9)' elif 'medium' in conf: dist = dist + 'in [0.001, 0.5] (6/9) and [0.5, 0.9] (3/9)' elif 'heavy' in conf: dist = dist + 'in [0.001, 0.5] (4/9) and [0.5, 0.9] (5/9)' if want_period: if '33' in conf: dist += '; period uniformly in [3, 33]' elif '250' in conf: dist += '; period uniformly in [50, 250]' else: dist += '; period uniformly in [10, 100]' return dist class SchedPlotter(defapp.App): def __init__(self): defapp.App.__init__(self, options, defaults, no_std_opts=True) def plot(self, graphs, title, name, conf, **xtra): gnuplot(graphs, title=title, xlabel=self.options.xlabel, ylabel=self.options.ylabel + (' [soft]' if 'soft' in conf else ' [hard]'), xrange=self.options.xrange, yrange=self.options.yrange, xticks=self.options.xticks, yticks=self.options.yticks, format=self.options.format, fname=name, **xtra) def plot_wide(self, graphs, title, name, conf, **xtra): tops = 'rounded size 16cm,6.5cm' gnuplot(graphs, title=title, xlabel=self.options.xlabel, ylabel=self.options.ylabel + (' [soft]' if 'soft' in conf else ' [hard]'), xrange=self.options.xrange, yrange=self.options.yrange, xticks=self.options.xticks, yticks=self.options.yticks, format=self.options.format, fname=name, term_opts=tops, **xtra) # width = 3.4in # height = 2.0in def plot_column(self, graphs, title, name, conf, **xtra): scale = 1.0 tops = 'color solid font "Helvetica,8" linewidth 1.0 ' \ 'rounded size %.1fin,%.1fin' % (scale * 7.0, scale * 2.0) gnuplot(graphs, title=title, xlabel=self.options.xlabel, ylabel=self.options.ylabel + (' [soft]' if 'soft' in conf else ' [hard]'), xrange=self.options.xrange, yrange=self.options.yrange, xticks=self.options.xticks, yticks=self.options.yticks, format=self.options.format, fname=name, key='right bottom' if '250' in conf else 'right top', term_opts=tops) def plot_paper(self, graphs, title, name, conf, **xtra): tops = 'color solid font "Helvetica,10" linewidth 1.0 rounded size 16cm,8.5cm' gnuplot(graphs, title=title, xlabel=self.options.xlabel, ylabel=self.options.ylabel + (' [soft]' if 'soft' in conf else ' [hard]'), xrange=self.options.xrange, yrange=self.options.yrange, xticks=self.options.xticks, yticks=self.options.yticks, format=self.options.format, fname=name, key='off', style='lines lw 7', term_opts=tops) def plot_spec(self, tmpfile, name, conf): title = 'G-EDF overhead speculation: ' + scenario_heading(conf) graphs = [ (tmpfile, 1, 2, 'G-EDF (100% release)'), (tmpfile, 1, 3, 'G-EDF ( 75% release)'), (tmpfile, 1, 4, 'G-EDF ( 50% release)'), (tmpfile, 1, 5, 'G-EDF ( 25% release)'), (tmpfile, 1, 6, 'G-EDF ( 0% release)'), (tmpfile, 1, 7, 'G-EDF (no overheads)'), ] if self.options.paper and self.options.format == 'pdf': self.plot_paper(graphs, title, name, conf) else: self.plot(graphs, title, name, conf) def plot_spec2(self, tmpfile, name, conf): title = 'service processor speculation: ' + scenario_heading(conf) graphs = [ (tmpfile, 1, 2, 'G-EDF ( 25% release, 100% rest)'), (tmpfile, 1, 3, 'G-EDF (100% release, 0% rest)'), (tmpfile, 1, 4, 'G-EDF (service cpu, 100% preempt)'), (tmpfile, 1, 5, 'G-EDF (service cpu, 150% preempt)'), ] self.plot(graphs, title, name, conf) def plot_spec3(self, tmpfile, name, conf): title = 'linear overhead charge speculation: ' + scenario_heading(conf) graphs = [ (tmpfile, 1, 2, 'G-EDF (100% release)'), (tmpfile, 1, 3, 'G-EDF ( 25% release)'), (tmpfile, 1, 4, 'G-EDF ( Lin release)'), ] self.plot(graphs, title, name, conf) def plot_spec4(self, tmpfile, name, conf): title = 'linear overhead charge speculation: ' + scenario_heading(conf) graphs = [ (tmpfile, 1, 2, 'G-EDF (100% release)'), (tmpfile, 1, 3, 'G-EDF-S (100% release, all cpus)'), (tmpfile, 1, 4, 'G-EDF-S (100% release, one cpu)'), (tmpfile, 1, 5, 'G-EDF (25% release)'), (tmpfile, 1, 6, 'G-EDF-S (25% release, all cpus)'), (tmpfile, 1, 7, 'G-EDF-S (25% release, one cpu)'), ] self.plot(graphs, title, name, conf) def make_plot(self, fname=None): p = Plot() p.output = "%s.%s" % (fname, self.options.format) p.format = self.options.format return p def setup_png(self, plot): # standard png options; usually correct; never tweaked for paper if self.options.format == 'png': plot.font_size = 'large' plot.size = (1024, 768) plot.xticks = (0, 1) plot.yticks = (0, 0.1) plot.default_style = "linespoints" return True else: return False def plot_irq(self, tmpfile, name, conf): p = self.make_plot(name) #### Data. titles = ['quantum-centric', 'task-centric', 'processor-centric', 'dedicated processor', 'dedicated proc. + timer multiplexing'] if self.options.alternate: extra = '; 20% ISR costs' cols = [8, 9, 10, 12, 14] p.output = 'alt_' + p.output else: extra = '; 100% ISR costs' cols = [1, 2, 3, 5, 7] p.curves += [ curve(fname=tmpfile, xcol=1, ycol=(y + 1), title=t) for (y, t) in zip(cols, titles) ] #### Styling. if not self.setup_png(p): # eps or pdf p.rounded_caps = True p.font = 'Helvetica' if self.options.paper: # workaround strange font-size bug if self.options.format == 'eps': p.font_size = '9.5pt' else: p.font_size = '5pt' p.size = ('6.5cm', '5.25cm') p.monochrome = True p.dashed_lines = True p.key = 'off' p.xticks = (0, 2) p.yrange = (-0.1, 1.1) p.yticks = (0, 0.2) p.xlabel = "task set utilization cap (prior to overhead accounting)" p.default_style = 'lines lw 2.5' else: p.font_size = '10' p.size = ('20cm', '10cm') p.monochrome = False p.dashed_lines = False p.key = 'below' p.xticks = (0, 1) p.yrange = (-0.05, 1.05) p.yticks = (0, 0.1) p.xlabel = "ucap (prior to inflation)" p.default_style = 'lines lw 6' p.ylabel = "schedulability " + (' [soft]' if 'soft' in conf else ' [hard]') p.xrange = (0.5, 32.5) p.title = scenario_heading(conf) + extra if not self.options.paper: p.title = 'Interrupt accounting under G-EDF: utilization ' + p.title if self.options.save_script: p.gnuplot_save(p.output + '.plot') else: p.gnuplot_exec() def plot_rtss09(self, tmpfile, name, conf): title = scenario_heading(conf, want_period=True) graphs = [ (tmpfile, 1, 2, 'G-EDF'), (tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 4, 'CE1'), (tmpfile, 1, 5, 'FEm'), (tmpfile, 1, 6, 'FE1'), (tmpfile, 1, 7, 'HEm'), (tmpfile, 1, 8, 'CQm'), (tmpfile, 1, 9, 'CQ1'), ] staggered = [ (tmpfile, 1, 10, 'S-CQm'), (tmpfile, 1, 11, 'S-CQ1'), ] if 'hard' in conf: graphs += staggered if self.options.paper and self.options.format == 'pdf': self.plot_paper(graphs, title, name, conf) elif self.options.wide and self.options.format == 'pdf': self.plot_wide(graphs, title, name, conf) else: self.plot(graphs, title, name, conf) def plot_rtss09_split(self, tmpfile, name, conf): title = scenario_heading(conf, want_period=True) ideal = (tmpfile, 1, 2, 'G-EDF') subgraphs = [ # dedicated vs. non-dedicated [(tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 4, 'CE1')], [(tmpfile, 1, 5, 'FEm'), (tmpfile, 1, 6, 'FE1')], [(tmpfile, 1, 8, 'CQm'), (tmpfile, 1, 9, 'CQ1')], # fine-grained vs. sequential [(tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 5, 'FEm')], [(tmpfile, 1, 4, 'CE1'), (tmpfile, 1, 6, 'FE1')], # hierarchical vs. sequential [(tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 7, 'HEm')], # quantum vs. event [(tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 8, 'CQm')], [(tmpfile, 1, 6, 'FE1'), (tmpfile, 1, 9, 'CQ1')], ] staggered = [ # dedicated vs. non-dedicated [(tmpfile, 1, 10, 'S-CQm'), (tmpfile, 1, 11, 'S-CQ1')], # staggered vs. non-staggered [(tmpfile, 1, 8, 'CQm'), (tmpfile, 1, 10, 'S-CQm')], [(tmpfile, 1, 9, 'CQ1'), (tmpfile, 1, 11, 'S-CQ1')], # quantum vs. event [(tmpfile, 1, 3, 'CEm'), (tmpfile, 1, 10, 'S-CQm')], [(tmpfile, 1, 6, 'FE1'), (tmpfile, 1, 11, 'S-CQ1')], ] if 'hard' in conf: subgraphs += staggered for g in subgraphs: graphs = [ideal] + g xname = name + '_' + '-vs-'.join([x[3] for x in g]) if self.options.paper and self.options.format == 'pdf': self.plot_paper(graphs, title, xname, conf) elif self.options.wide and self.options.format == 'pdf': self.plot_wide(graphs, title, xname, conf) else: self.plot(graphs, title, xname, conf) def plot_ospert10(self, tmpfile, name, conf): title = scenario_heading(conf, want_period=True) # cumulative graphs graphs = [ (tmpfile, 1, 2, 'G-EDF'), (tmpfile, 1, 4, 'C-EDF (L3)'), (tmpfile, 1, 6, 'C-EDF (L2)'), (tmpfile, 1, 8, 'P-EDF'), ] # weighted graphs graphs_w = [ (tmpfile, 1, 3, 'G-EDF'), (tmpfile, 1, 5, 'C-EDF (L3)'), (tmpfile, 1, 7, 'C-EDF (L2)'), (tmpfile, 1, 9, 'P-EDF'), ] self.options.xrange = (-5, 5005) self.options.xticks = (0, 250) self.options.xlabel = "cache-related preemption/migration delay (in us)" for (tag, gs) in [('cumulative', graphs), ('weighted', graphs_w)]: xname = name + '_' + tag self.options.ylabel = tag + ' schedulability' if self.options.paper and self.options.format == 'pdf': self.plot_paper(gs, title, xname, conf) elif self.options.wide and self.options.format == 'pdf': self.plot_wide(gs, title, xname, conf) elif self.options.column and self.options.format == 'pdf': self.plot_column(gs, title, xname, conf) else: self.plot(gs, title, xname, conf) def plot_file(self, datafile): bname = basename(datafile) name, ext = splitext(bname) if ext != '.csv': self.err("Warning: '%s' doesn't look like a CSV file." % bname) conf = decode(name) try: if self.options.save_script: tmpfile = get_data_tmpfile(datafile, target = "%s.data" % name ) else: tmpfile = get_data_tmpfile(datafile) except IOError: tmpfile = None if tmpfile: if 'spec' in conf: self.plot_spec(tmpfile.name, name, conf) elif 'spec2' in conf: self.plot_spec2(tmpfile.name, name, conf) elif 'spec3' in conf: self.plot_spec3(tmpfile.name, name, conf) elif 'spec4' in conf: self.plot_spec4(tmpfile.name, name, conf) elif 'irq' in conf: self.plot_irq(tmpfile.name, name, conf) elif 'rtss09' in conf: if self.options.split: self.plot_rtss09_split(tmpfile.name, name, conf) else: self.plot_rtss09(tmpfile.name, name, conf) elif 'ospert10' in conf: self.plot_ospert10(tmpfile.name, name, conf) else: self.err("Skipped '%s'; unkown experiment type." % bname) del tmpfile # removes temp file else: self.err("Skipped '%s'; it dosn't appear to contain data." % bname) def default(self, _): for i, datafile in enumerate(self.args): self.out("[%d/%d] Processing %s ..." % (i + 1, len(self.args), datafile)) self.plot_file(datafile) if __name__ == "__main__": SchedPlotter().launch()