diff options
Diffstat (limited to 'run_exps.py')
-rwxr-xr-x | run_exps.py | 242 |
1 files changed, 154 insertions, 88 deletions
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 | ||