diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-08 15:42:16 -0400 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-08 15:42:16 -0400 |
| commit | d169debf732270c9571be6ea6e7d920345bffc33 (patch) | |
| tree | d86c1ff5d0e4a116fcc916717abb29a026862c74 | |
| parent | 8864a4018c9b9088f330c3ef24ed7b5313ec36a2 (diff) | |
Added parameter file options for tracers and pre/post experiment commands.
| -rw-r--r-- | common.py | 2 | ||||
| -rw-r--r-- | config/config.py | 5 | ||||
| -rw-r--r-- | run/experiment.py | 56 | ||||
| -rw-r--r-- | run/tracer.py | 52 | ||||
| -rwxr-xr-x | run_exps.py | 242 |
5 files changed, 230 insertions, 127 deletions
| @@ -7,7 +7,7 @@ import sys | |||
| 7 | from collections import defaultdict | 7 | from collections import defaultdict |
| 8 | from textwrap import dedent | 8 | from textwrap import dedent |
| 9 | 9 | ||
| 10 | def get_executable(prog, hint, optional=False): | 10 | def get_executable(prog, hint='unknown', optional=False): |
| 11 | '''Search for @prog in system PATH. Print @hint if no binary is found.''' | 11 | '''Search for @prog in system PATH. Print @hint if no binary is found.''' |
| 12 | 12 | ||
| 13 | def is_exe(fpath): | 13 | def is_exe(fpath): |
diff --git a/config/config.py b/config/config.py index 49fd234..3486a40 100644 --- a/config/config.py +++ b/config/config.py | |||
| @@ -28,7 +28,10 @@ PARAMS = {'sched' : 'scheduler', # Scheduler used by run_exps | |||
| 28 | 'copts' : 'config-options', # Required kernel configuration options | 28 | 'copts' : 'config-options', # Required kernel configuration options |
| 29 | 'cycles' : 'clock-frequency', # Frequency run_exps was run with | 29 | 'cycles' : 'clock-frequency', # Frequency run_exps was run with |
| 30 | 'tasks' : 'tasks', # Number of tasks | 30 | 'tasks' : 'tasks', # Number of tasks |
| 31 | 'trial' : 'trial' # For multiple exps with same config | 31 | 'trial' : 'trial', # For multiple exps with same config |
| 32 | 'pre' : 'pre-experiment', # Run before each experiment | ||
| 33 | 'post' : 'post-experiment', # Run after each experiment | ||
| 34 | 'trace' : 'tracers' # Tracers to run with an experiment | ||
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | '''Default values for program options.''' | 37 | '''Default values for program options.''' |
diff --git a/run/experiment.py b/run/experiment.py index 03d6ab6..3dd4866 100644 --- a/run/experiment.py +++ b/run/experiment.py | |||
| @@ -3,7 +3,6 @@ import time | |||
| 3 | import run.litmus_util as lu | 3 | import run.litmus_util as lu |
| 4 | import shutil as sh | 4 | import shutil as sh |
| 5 | from operator import methodcaller | 5 | from operator import methodcaller |
| 6 | from run.tracer import SchedTracer, LogTracer, PerfTracer, LinuxTracer, OverheadTracer | ||
| 7 | 6 | ||
| 8 | class ExperimentException(Exception): | 7 | class ExperimentException(Exception): |
| 9 | '''Used to indicate when there are problems with an experiment.''' | 8 | '''Used to indicate when there are problems with an experiment.''' |
| @@ -32,7 +31,8 @@ class Experiment(object): | |||
| 32 | '''Execute one task-set and save the results. Experiments have unique IDs.''' | 31 | '''Execute one task-set and save the results. Experiments have unique IDs.''' |
| 33 | INTERRUPTED_DIR = ".interrupted" | 32 | INTERRUPTED_DIR = ".interrupted" |
| 34 | 33 | ||
| 35 | def __init__(self, name, scheduler, working_dir, finished_dir, proc_entries, executables): | 34 | def __init__(self, name, scheduler, working_dir, finished_dir, |
| 35 | proc_entries, executables, tracer_types): | ||
| 36 | '''Run an experiment, optionally wrapped in tracing.''' | 36 | '''Run an experiment, optionally wrapped in tracing.''' |
| 37 | 37 | ||
| 38 | self.name = name | 38 | self.name = name |
| @@ -46,27 +46,17 @@ class Experiment(object): | |||
| 46 | 46 | ||
| 47 | self.__make_dirs() | 47 | self.__make_dirs() |
| 48 | self.__assign_executable_cwds() | 48 | self.__assign_executable_cwds() |
| 49 | self.__setup_tracers(tracer_types) | ||
| 49 | 50 | ||
| 50 | self.tracers = [] | 51 | |
| 51 | if SchedTracer.enabled(): | 52 | def __setup_tracers(self, tracer_types): |
| 52 | self.log("Enabling sched_trace") | 53 | tracers = [ t(self.working_dir) for t in tracer_types ] |
| 53 | self.tracers.append( SchedTracer(working_dir) ) | 54 | |
| 54 | if LinuxTracer.enabled(): | 55 | self.regular_tracers = [t for t in tracers if not t.is_exact()] |
| 55 | self.log("Enabling trace-cmd") | 56 | self.exact_tracers = [t for t in tracers if t.is_exact()] |
| 56 | self.tracers.append( LinuxTracer(working_dir) ) | 57 | |
| 57 | if LogTracer.enabled(): | 58 | for t in tracers: |
| 58 | self.log("Enabling logging") | 59 | self.log("Enabling %s" % t.get_name()) |
| 59 | self.tracers.append( LogTracer(working_dir) ) | ||
| 60 | if PerfTracer.enabled(): | ||
| 61 | self.log("Tracking CPU performance counters") | ||
| 62 | self.tracers.append( PerfTracer(working_dir) ) | ||
| 63 | |||
| 64 | # Overhead trace must be handled seperately, see __run_tasks | ||
| 65 | if OverheadTracer.enabled(): | ||
| 66 | self.log("Enabling overhead tracing") | ||
| 67 | self.overhead_trace = OverheadTracer(working_dir) | ||
| 68 | else: | ||
| 69 | self.overhead_trace = None | ||
| 70 | 60 | ||
| 71 | def __make_dirs(self): | 61 | def __make_dirs(self): |
| 72 | interrupted = None | 62 | interrupted = None |
| @@ -111,11 +101,10 @@ class Experiment(object): | |||
| 111 | raise Exception("Too much time has passed waiting for tasks!") | 101 | raise Exception("Too much time has passed waiting for tasks!") |
| 112 | time.sleep(1) | 102 | time.sleep(1) |
| 113 | 103 | ||
| 114 | # Overhead tracer must be started right after release or overhead | 104 | # Exact tracers (like overheads) must be started right after release or |
| 115 | # measurements will be full of irrelevant records | 105 | # measurements will be full of irrelevant records |
| 116 | if self.overhead_trace: | 106 | self.log("Starting %d released tracers" % len(self.exact_tracers)) |
| 117 | self.log("Starting overhead trace") | 107 | map(methodcaller('start_tracing'), self.exact_tracers) |
| 118 | self.overhead_trace.start_tracing() | ||
| 119 | 108 | ||
| 120 | self.log("Releasing %d tasks" % len(self.executables)) | 109 | self.log("Releasing %d tasks" % len(self.executables)) |
| 121 | released = lu.release_tasks() | 110 | released = lu.release_tasks() |
| @@ -145,10 +134,9 @@ class Experiment(object): | |||
| 145 | if not e.wait(): | 134 | if not e.wait(): |
| 146 | ret = False | 135 | ret = False |
| 147 | 136 | ||
| 148 | # And it must be stopped here for the same reason | 137 | # And these must be stopped here for the same reason |
| 149 | if self.overhead_trace: | 138 | self.log("Stopping exact tracers") |
| 150 | self.log("Stopping overhead trace") | 139 | map(methodcaller('stop_tracing'), self.exact_tracers) |
| 151 | self.overhead_trace.stop_tracing() | ||
| 152 | 140 | ||
| 153 | if not ret: | 141 | if not ret: |
| 154 | raise ExperimentFailed(self.name) | 142 | raise ExperimentFailed(self.name) |
| @@ -186,8 +174,8 @@ class Experiment(object): | |||
| 186 | self.log("Switching to %s" % self.scheduler) | 174 | self.log("Switching to %s" % self.scheduler) |
| 187 | lu.switch_scheduler(self.scheduler) | 175 | lu.switch_scheduler(self.scheduler) |
| 188 | 176 | ||
| 189 | self.log("Starting %d tracers" % len(self.tracers)) | 177 | self.log("Starting %d regular tracers" % len(self.regular_tracers)) |
| 190 | map(methodcaller('start_tracing'), self.tracers) | 178 | map(methodcaller('start_tracing'), self.regular_tracers) |
| 191 | 179 | ||
| 192 | self.exec_out = open('%s/exec-out.txt' % self.working_dir, 'w') | 180 | self.exec_out = open('%s/exec-out.txt' % self.working_dir, 'w') |
| 193 | self.exec_err = open('%s/exec-err.txt' % self.working_dir, 'w') | 181 | self.exec_err = open('%s/exec-err.txt' % self.working_dir, 'w') |
| @@ -200,6 +188,6 @@ class Experiment(object): | |||
| 200 | self.exec_out and self.exec_out.close() | 188 | self.exec_out and self.exec_out.close() |
| 201 | self.exec_err and self.exec_err.close() | 189 | self.exec_err and self.exec_err.close() |
| 202 | 190 | ||
| 203 | self.log("Stopping tracers") | 191 | self.log("Stopping regular tracers") |
| 204 | map(methodcaller('stop_tracing'), self.tracers) | 192 | map(methodcaller('stop_tracing'), self.regular_tracers) |
| 205 | 193 | ||
diff --git a/run/tracer.py b/run/tracer.py index 065797c..6e1d05c 100644 --- a/run/tracer.py +++ b/run/tracer.py | |||
| @@ -6,10 +6,17 @@ from operator import methodcaller | |||
| 6 | from run.executable.ftcat import FTcat,Executable | 6 | from run.executable.ftcat import FTcat,Executable |
| 7 | 7 | ||
| 8 | class Tracer(object): | 8 | class Tracer(object): |
| 9 | def __init__(self, name, output_dir): | 9 | def __init__(self, name, output_dir, exact=False): |
| 10 | self.name = name | 10 | self.name = name |
| 11 | self.output_dir = output_dir | 11 | self.output_dir = output_dir |
| 12 | self.bins = [] | 12 | self.bins = [] |
| 13 | self.exact=exact | ||
| 14 | |||
| 15 | def get_name(self): | ||
| 16 | return self.name | ||
| 17 | |||
| 18 | def is_exact(self): | ||
| 19 | return self.exact | ||
| 13 | 20 | ||
| 14 | def start_tracing(self): | 21 | def start_tracing(self): |
| 15 | map(methodcaller("execute"), self.bins) | 22 | map(methodcaller("execute"), self.bins) |
| @@ -23,7 +30,7 @@ class LinuxTracer(Tracer): | |||
| 23 | LITMUS_EVENTS = "%s/events/litmus" % EVENT_ROOT | 30 | LITMUS_EVENTS = "%s/events/litmus" % EVENT_ROOT |
| 24 | 31 | ||
| 25 | def __init__(self, output_dir): | 32 | def __init__(self, output_dir): |
| 26 | super(LinuxTracer, self).__init__("trace-cmd", output_dir) | 33 | super(LinuxTracer, self).__init__("Trace-cmd / Kernelshark", output_dir) |
| 27 | 34 | ||
| 28 | extra_args = ["record", # "-e", "sched:sched_switch", | 35 | extra_args = ["record", # "-e", "sched:sched_switch", |
| 29 | "-e", "litmus:*", | 36 | "-e", "litmus:*", |
| @@ -89,7 +96,7 @@ class OverheadTracer(Tracer): | |||
| 89 | DEVICE_STR = '/dev/litmus/ft_trace0' | 96 | DEVICE_STR = '/dev/litmus/ft_trace0' |
| 90 | 97 | ||
| 91 | def __init__(self, output_dir): | 98 | def __init__(self, output_dir): |
| 92 | super(OverheadTracer, self).__init__("Overhead Trace", output_dir) | 99 | super(OverheadTracer, self).__init__("Overhead Trace", output_dir, True) |
| 93 | 100 | ||
| 94 | stdout_f = open('{0}/{1}'.format(self.output_dir, conf.FILES['ft_data']), 'w') | 101 | stdout_f = open('{0}/{1}'.format(self.output_dir, conf.FILES['ft_data']), 'w') |
| 95 | stderr_f = open('{0}/{1}.stderr.txt'.format(self.output_dir, conf.FILES['ft_data']), 'w') | 102 | stderr_f = open('{0}/{1}.stderr.txt'.format(self.output_dir, conf.FILES['ft_data']), 'w') |
| @@ -109,3 +116,42 @@ class PerfTracer(Tracer): | |||
| 109 | @staticmethod | 116 | @staticmethod |
| 110 | def enabled(): | 117 | def enabled(): |
| 111 | return False | 118 | return False |
| 119 | |||
| 120 | |||
| 121 | tracers = {} | ||
| 122 | |||
| 123 | def register_tracer(tracer, name): | ||
| 124 | tracers[name] = tracer | ||
| 125 | |||
| 126 | def get_tracer_types(names): | ||
| 127 | error = True # Error if name is not present | ||
| 128 | errors = [] | ||
| 129 | |||
| 130 | if not names: | ||
| 131 | # Just return all enabled tracers if none specified | ||
| 132 | names = tracers.keys() | ||
| 133 | error = False | ||
| 134 | |||
| 135 | ret = [] | ||
| 136 | |||
| 137 | for name in names: | ||
| 138 | if name not in tracers: | ||
| 139 | raise ValueError("Invalid tracer '%s', valid names are: %s" % | ||
| 140 | (name, tracers.keys())) | ||
| 141 | |||
| 142 | if tracers[name].enabled(): | ||
| 143 | ret += [ tracers[name] ] | ||
| 144 | elif error: | ||
| 145 | errors += ["Tracer '%s' requested, but not enabled." % name] | ||
| 146 | |||
| 147 | if errors: | ||
| 148 | raise ValueError("Check your kernel compile configuration!\n" + | ||
| 149 | "\n".join(errors)) | ||
| 150 | |||
| 151 | return ret | ||
| 152 | |||
| 153 | register_tracer(LinuxTracer, "kernelshark") | ||
| 154 | register_tracer(LogTracer, "log") | ||
| 155 | register_tracer(SchedTracer, "sched") | ||
| 156 | register_tracer(OverheadTracer, "overhead") | ||
| 157 | |||
diff --git a/run_exps.py b/run_exps.py index c51f4c6..6043931 100755 --- a/run_exps.py +++ b/run_exps.py | |||
| @@ -7,6 +7,7 @@ import os | |||
| 7 | import re | 7 | import re |
| 8 | import shutil | 8 | import shutil |
| 9 | import sys | 9 | import sys |
| 10 | import run.tracer as trace | ||
| 10 | import traceback | 11 | import traceback |
| 11 | 12 | ||
| 12 | from collections import namedtuple | 13 | from collections import namedtuple |
| @@ -15,6 +16,12 @@ from run.executable.executable import Executable | |||
| 15 | from run.experiment import Experiment,ExperimentDone | 16 | from run.experiment import Experiment,ExperimentDone |
| 16 | from run.proc_entry import ProcEntry | 17 | from run.proc_entry import ProcEntry |
| 17 | 18 | ||
| 19 | '''Customizable experiment parameters''' | ||
| 20 | ExpParams = namedtuple('ExpParams', ['scheduler', 'duration', 'tracers', | ||
| 21 | 'kernel', 'config_options']) | ||
| 22 | '''Comparison of requested versus actual kernel compile parameter value''' | ||
| 23 | ConfigResult = namedtuple('ConfigResult', ['param', 'wanted', 'actual']) | ||
| 24 | |||
| 18 | class InvalidKernel(Exception): | 25 | class InvalidKernel(Exception): |
| 19 | def __init__(self, kernel): | 26 | def __init__(self, kernel): |
| 20 | self.kernel = kernel | 27 | self.kernel = kernel |
| @@ -22,7 +29,7 @@ class InvalidKernel(Exception): | |||
| 22 | def __str__(self): | 29 | def __str__(self): |
| 23 | return "Kernel name does not match '%s'." % self.kernel | 30 | return "Kernel name does not match '%s'." % self.kernel |
| 24 | 31 | ||
| 25 | ConfigResult = namedtuple('ConfigResult', ['param', 'wanted', 'actual']) | 32 | |
| 26 | class InvalidConfig(Exception): | 33 | class InvalidConfig(Exception): |
| 27 | def __init__(self, results): | 34 | def __init__(self, results): |
| 28 | self.results = results | 35 | self.results = results |
| @@ -38,6 +45,7 @@ class InvalidConfig(Exception): | |||
| 38 | return "Invalid kernel configuration " +\ | 45 | return "Invalid kernel configuration " +\ |
| 39 | "(ignore configuration with -i option).\n" + "\n".join(messages) | 46 | "(ignore configuration with -i option).\n" + "\n".join(messages) |
| 40 | 47 | ||
| 48 | |||
| 41 | def parse_args(): | 49 | def parse_args(): |
| 42 | parser = OptionParser("usage: %prog [options] [sched_file]... [exp_dir]...") | 50 | parser = OptionParser("usage: %prog [options] [sched_file]... [exp_dir]...") |
| 43 | 51 | ||
| @@ -92,6 +100,7 @@ def convert_data(data): | |||
| 92 | 100 | ||
| 93 | return {'proc' : procs, 'spin' : spins} | 101 | return {'proc' : procs, 'spin' : spins} |
| 94 | 102 | ||
| 103 | |||
| 95 | def fix_paths(schedule, exp_dir, sched_file): | 104 | def fix_paths(schedule, exp_dir, sched_file): |
| 96 | '''Replace relative paths of command line arguments with absolute ones.''' | 105 | '''Replace relative paths of command line arguments with absolute ones.''' |
| 97 | for (idx, (spin, args)) in enumerate(schedule['spin']): | 106 | for (idx, (spin, args)) in enumerate(schedule['spin']): |
| @@ -106,97 +115,24 @@ def fix_paths(schedule, exp_dir, sched_file): | |||
| 106 | 115 | ||
| 107 | schedule['spin'][idx] = (spin, args) | 116 | schedule['spin'][idx] = (spin, args) |
| 108 | 117 | ||
| 109 | def verify_environment(kernel, copts): | ||
| 110 | if kernel and not com.uname_matches(kernel): | ||
| 111 | raise InvalidKernel(kernel) | ||
| 112 | |||
| 113 | if copts: | ||
| 114 | results = [] | ||
| 115 | for param, wanted in copts.iteritems(): | ||
| 116 | try: | ||
| 117 | actual = com.get_config_option(param) | ||
| 118 | except IOError: | ||
| 119 | actual = None | ||
| 120 | if not str(wanted) == str(actual): | ||
| 121 | results += [ConfigResult(param, wanted, actual)] | ||
| 122 | |||
| 123 | if results: | ||
| 124 | raise InvalidConfig(results) | ||
| 125 | |||
| 126 | def load_experiment(sched_file, scheduler, duration, | ||
| 127 | param_file, out_dir, ignore, jabber): | ||
| 128 | if not os.path.isfile(sched_file): | ||
| 129 | raise IOError("Cannot find schedule file: %s" % sched_file) | ||
| 130 | |||
| 131 | dir_name, fname = os.path.split(sched_file) | ||
| 132 | exp_name = os.path.split(dir_name)[1] + "/" + fname | ||
| 133 | |||
| 134 | params = {} | ||
| 135 | kernel = copts = "" | ||
| 136 | |||
| 137 | param_file = param_file or \ | ||
| 138 | "%s/%s" % (dir_name, conf.DEFAULTS['params_file']) | ||
| 139 | |||
| 140 | if os.path.isfile(param_file): | ||
| 141 | params = com.load_params(param_file) | ||
| 142 | scheduler = scheduler or params[conf.PARAMS['sched']] | ||
| 143 | duration = duration or params[conf.PARAMS['dur']] | ||
| 144 | |||
| 145 | # Experiments can specify required kernel name | ||
| 146 | if conf.PARAMS['kernel'] in params: | ||
| 147 | kernel = params[conf.PARAMS['kernel']] | ||
| 148 | # Or required config options | ||
| 149 | if conf.PARAMS['copts'] in params: | ||
| 150 | copts = params[conf.PARAMS['copts']] | ||
| 151 | |||
| 152 | duration = duration or conf.DEFAULTS['duration'] | ||
| 153 | |||
| 154 | if not scheduler: | ||
| 155 | raise IOError("Parameter scheduler not specified in %s" % (param_file)) | ||
| 156 | |||
| 157 | # Parse schedule file's intentions | ||
| 158 | schedule = load_schedule(sched_file) | ||
| 159 | work_dir = "%s/tmp" % dir_name | ||
| 160 | |||
| 161 | fix_paths(schedule, os.path.split(sched_file)[0], sched_file) | ||
| 162 | |||
| 163 | if not ignore: | ||
| 164 | verify_environment(kernel, copts) | ||
| 165 | |||
| 166 | run_exp(exp_name, schedule, scheduler, kernel, duration, work_dir, out_dir) | ||
| 167 | |||
| 168 | if jabber: | ||
| 169 | jabber.send("Completed '%s'" % exp_name) | ||
| 170 | |||
| 171 | # Save parameters used to run experiment in out_dir | ||
| 172 | out_params = dict(params.items() + | ||
| 173 | [(conf.PARAMS['sched'], scheduler), | ||
| 174 | (conf.PARAMS['tasks'], len(schedule['spin'])), | ||
| 175 | (conf.PARAMS['dur'], duration)]) | ||
| 176 | 118 | ||
| 177 | # Feather-trace clock frequency saved for accurate overhead parsing | 119 | def load_schedule(name, fname, duration): |
| 178 | ft_freq = com.ft_freq() | 120 | '''Turn schedule file @fname into ProcEntry's and Executable's which execute |
| 179 | if ft_freq: | 121 | for @duration time.''' |
| 180 | out_params[conf.PARAMS['cycles']] = ft_freq | ||
| 181 | |||
| 182 | with open("%s/%s" % (out_dir, conf.DEFAULTS['params_file']), 'w') as f: | ||
| 183 | f.write(str(out_params)) | ||
| 184 | |||
| 185 | def load_schedule(fname): | ||
| 186 | with open(fname, 'r') as f: | 122 | with open(fname, 'r') as f: |
| 187 | data = f.read().strip() | 123 | data = f.read().strip() |
| 188 | try: | 124 | try: |
| 189 | schedule = eval(data) | 125 | schedule = eval(data) |
| 190 | except: | 126 | except: |
| 191 | schedule = convert_data(data) | 127 | schedule = convert_data(data) |
| 192 | return schedule | ||
| 193 | 128 | ||
| 129 | # Make paths relative to the file's directory | ||
| 130 | fix_paths(schedule, os.path.split(fname)[0], fname) | ||
| 194 | 131 | ||
| 195 | def run_exp(name, schedule, scheduler, kernel, duration, work_dir, out_dir): | ||
| 196 | proc_entries = [] | 132 | proc_entries = [] |
| 197 | executables = [] | 133 | executables = [] |
| 198 | 134 | ||
| 199 | # Parse values for proc entries | 135 | # Create proc entries |
| 200 | for entry_conf in schedule['proc']: | 136 | for entry_conf in schedule['proc']: |
| 201 | path = entry_conf[0] | 137 | path = entry_conf[0] |
| 202 | data = entry_conf[1] | 138 | data = entry_conf[1] |
| @@ -206,7 +142,7 @@ def run_exp(name, schedule, scheduler, kernel, duration, work_dir, out_dir): | |||
| 206 | 142 | ||
| 207 | proc_entries += [ProcEntry(path, data)] | 143 | proc_entries += [ProcEntry(path, data)] |
| 208 | 144 | ||
| 209 | # Parse spinners | 145 | # Create executables |
| 210 | for spin_conf in schedule['spin']: | 146 | for spin_conf in schedule['spin']: |
| 211 | if isinstance(spin_conf, str): | 147 | if isinstance(spin_conf, str): |
| 212 | # Just a string defaults to default spin | 148 | # Just a string defaults to default spin |
| @@ -217,9 +153,6 @@ def run_exp(name, schedule, scheduler, kernel, duration, work_dir, out_dir): | |||
| 217 | raise IOError("Invalid spin conf %s: %s" % (spin_conf, name)) | 153 | raise IOError("Invalid spin conf %s: %s" % (spin_conf, name)) |
| 218 | (spin, args) = (spin_conf[0], spin_conf[1]) | 154 | (spin, args) = (spin_conf[0], spin_conf[1]) |
| 219 | 155 | ||
| 220 | # if not conf.BINS[spin]: | ||
| 221 | # raise IndexError("No knowledge of program %s: %s" % (spin, name)) | ||
| 222 | |||
| 223 | real_spin = com.get_executable(spin, "") | 156 | real_spin = com.get_executable(spin, "") |
| 224 | real_args = args.split() | 157 | real_args = args.split() |
| 225 | if re.match(".*spin", real_spin): | 158 | if re.match(".*spin", real_spin): |
| @@ -230,21 +163,154 @@ def run_exp(name, schedule, scheduler, kernel, duration, work_dir, out_dir): | |||
| 230 | 163 | ||
| 231 | executables += [Executable(real_spin, real_args)] | 164 | executables += [Executable(real_spin, real_args)] |
| 232 | 165 | ||
| 233 | exp = Experiment(name, scheduler, work_dir, out_dir, | 166 | return proc_entries, executables |
| 234 | proc_entries, executables) | 167 | |
| 168 | |||
| 169 | def verify_environment(exp_params): | ||
| 170 | if exp_params.kernel and not com.uname_matches(exp_params.kernel): | ||
| 171 | raise InvalidKernel(exp_params.kernel) | ||
| 172 | |||
| 173 | if exp_params.config_options: | ||
| 174 | results = [] | ||
| 175 | for param, wanted in exp_params.config_options.iteritems(): | ||
| 176 | try: | ||
| 177 | actual = com.get_config_option(param) | ||
| 178 | except IOError: | ||
| 179 | actual = None | ||
| 180 | if not str(wanted) == str(actual): | ||
| 181 | results += [ConfigResult(param, wanted, actual)] | ||
| 182 | |||
| 183 | if results: | ||
| 184 | raise InvalidConfig(results) | ||
| 185 | |||
| 186 | |||
| 187 | def run_parameter(exp_dir, out_dir, params, param_name): | ||
| 188 | '''Run an executable (arguments optional) specified as a configurable | ||
| 189 | @param_name in @params.''' | ||
| 190 | if conf.PARAMS[param_name] not in params: | ||
| 191 | return | ||
| 192 | |||
| 193 | script_params = params[conf.PARAMS[param_name]] | ||
| 194 | |||
| 195 | # Split into arguments and program name | ||
| 196 | if type(script_params) != type([]): | ||
| 197 | script_params = [script_params] | ||
| 198 | script_name = script_params.pop(0) | ||
| 199 | |||
| 200 | cwd_name = "%s/%s" % (exp_dir, script_name) | ||
| 201 | if os.path.isfile(cwd_name): | ||
| 202 | script = cwd_name | ||
| 203 | else: | ||
| 204 | script = com.get_executable(script_name, optional=True) | ||
| 205 | |||
| 206 | if not script: | ||
| 207 | raise Exception("Cannot find executable %s-script: %s" % | ||
| 208 | (param_name, script_name)) | ||
| 209 | |||
| 210 | out = open('%s/%s-out.txt' % (out_dir, param_name), 'w') | ||
| 211 | prog = Executable(script, script_params, | ||
| 212 | stderr_file=out, stdout_file=out) | ||
| 213 | prog.cwd = out_dir | ||
| 214 | |||
| 215 | prog.execute() | ||
| 216 | prog.wait() | ||
| 217 | |||
| 218 | out.close() | ||
| 219 | |||
| 220 | |||
| 221 | def get_exp_params(cmd_scheduler, cmd_duration, file_params): | ||
| 222 | kernel = copts = "" | ||
| 223 | |||
| 224 | scheduler = cmd_scheduler or file_params[conf.PARAMS['sched']] | ||
| 225 | duration = cmd_duration or file_params[conf.PARAMS['dur']] or\ | ||
| 226 | conf.DEFAULTS['duration'] | ||
| 227 | |||
| 228 | # Experiments can specify required kernel name | ||
| 229 | if conf.PARAMS['kernel'] in file_params: | ||
| 230 | kernel = file_params[conf.PARAMS['kernel']] | ||
| 231 | |||
| 232 | # Or required config options | ||
| 233 | if conf.PARAMS['copts'] in file_params: | ||
| 234 | copts = file_params[conf.PARAMS['copts']] | ||
| 235 | |||
| 236 | # Or required tracers | ||
| 237 | requested = [] | ||
| 238 | if conf.PARAMS['trace'] in file_params: | ||
| 239 | requested = file_params[conf.PARAMS['trace']] | ||
| 240 | tracers = trace.get_tracer_types(requested) | ||
| 241 | |||
| 242 | # But only these two are mandatory | ||
| 243 | if not scheduler: | ||
| 244 | raise IOError("No scheduler found in param file!") | ||
| 245 | if not duration: | ||
| 246 | raise IOError("No duration found in param file!") | ||
| 247 | |||
| 248 | return ExpParams(scheduler=scheduler, kernel=kernel, duration=duration, | ||
| 249 | config_options=copts, tracers=tracers) | ||
| 250 | |||
| 251 | |||
| 252 | def load_experiment(sched_file, cmd_scheduler, cmd_duration, | ||
| 253 | param_file, out_dir, ignore, jabber): | ||
| 254 | '''Load and parse data from files and run result.''' | ||
| 255 | if not os.path.isfile(sched_file): | ||
| 256 | raise IOError("Cannot find schedule file: %s" % sched_file) | ||
| 257 | |||
| 258 | dir_name, fname = os.path.split(sched_file) | ||
| 259 | exp_name = os.path.split(dir_name)[1] + "/" + fname | ||
| 260 | work_dir = "%s/tmp" % dir_name | ||
| 261 | |||
| 262 | # Load parameter file | ||
| 263 | param_file = param_file or \ | ||
| 264 | "%s/%s" % (dir_name, conf.DEFAULTS['params_file']) | ||
| 265 | if os.path.isfile(param_file): | ||
| 266 | file_params = com.load_params(param_file) | ||
| 267 | else: | ||
| 268 | file_params = {} | ||
| 269 | |||
| 270 | exp_params = get_exp_params(cmd_scheduler, cmd_duration, file_params) | ||
| 271 | procs, execs = load_schedule(exp_name, sched_file, exp_params.duration) | ||
| 272 | |||
| 273 | exp = Experiment(exp_name, exp_params.scheduler, work_dir, out_dir, | ||
| 274 | procs, execs, exp_params.tracers) | ||
| 275 | |||
| 276 | if not ignore: | ||
| 277 | verify_environment(exp_params) | ||
| 278 | |||
| 279 | run_parameter(dir_name, work_dir, file_params, 'pre') | ||
| 235 | 280 | ||
| 236 | exp.run_exp() | 281 | exp.run_exp() |
| 237 | 282 | ||
| 283 | run_parameter(dir_name, out_dir, file_params, 'post') | ||
| 284 | |||
| 285 | if jabber: | ||
| 286 | jabber.send("Completed '%s'" % exp_name) | ||
| 287 | |||
| 288 | # Save parameters used to run experiment in out_dir | ||
| 289 | out_params = dict(file_params.items() + | ||
| 290 | [(conf.PARAMS['sched'], exp_params.scheduler), | ||
| 291 | (conf.PARAMS['tasks'], len(execs)), | ||
| 292 | (conf.PARAMS['dur'], exp_params.duration)]) | ||
| 293 | |||
| 294 | # Feather-trace clock frequency saved for accurate overhead parsing | ||
| 295 | ft_freq = com.ft_freq() | ||
| 296 | if ft_freq: | ||
| 297 | out_params[conf.PARAMS['cycles']] = ft_freq | ||
| 298 | |||
| 299 | with open("%s/%s" % (out_dir, conf.DEFAULTS['params_file']), 'w') as f: | ||
| 300 | f.write(str(out_params)) | ||
| 301 | |||
| 302 | |||
| 238 | def setup_jabber(target): | 303 | def setup_jabber(target): |
| 239 | try: | 304 | try: |
| 240 | from run.jabber import Jabber | 305 | from run.jabber import Jabber |
| 241 | 306 | ||
| 242 | return Jabber(target) | 307 | return Jabber(target) |
| 243 | except ImportError: | 308 | except ImportError: |
| 244 | sys.stderr.write("Failed to import jabber. Is python-xmpppy "+\ | 309 | sys.stderr.write("Failed to import jabber, disabling messages. " + |
| 245 | "installed?\nDisabling Jabber messaging.\n") | 310 | "Is python-xmpp installed?") |
| 246 | return None | 311 | return None |
| 247 | 312 | ||
| 313 | |||
| 248 | def main(): | 314 | def main(): |
| 249 | opts, args = parse_args() | 315 | opts, args = parse_args() |
| 250 | 316 | ||
