diff options
Diffstat (limited to 'run_exps.py')
-rwxr-xr-x | run_exps.py | 218 |
1 files changed, 128 insertions, 90 deletions
diff --git a/run_exps.py b/run_exps.py index 6531415..a15018d 100755 --- a/run_exps.py +++ b/run_exps.py | |||
@@ -2,22 +2,23 @@ | |||
2 | from __future__ import print_function | 2 | from __future__ import print_function |
3 | 3 | ||
4 | import common as com | 4 | import common as com |
5 | import config.config as conf | ||
6 | import os | 5 | import os |
7 | import re | 6 | import re |
8 | import shutil | 7 | import shutil |
9 | import sys | 8 | import sys |
10 | import run.tracer as trace | 9 | import run.tracer as trace |
11 | 10 | ||
11 | from config.config import PARAMS,DEFAULTS | ||
12 | from collections import namedtuple | 12 | from collections import namedtuple |
13 | from optparse import OptionParser | 13 | from optparse import OptionParser |
14 | from run.executable.executable import Executable | 14 | from run.executable.executable import Executable |
15 | from run.experiment import Experiment,ExperimentDone,ExperimentFailed,SystemCorrupted | 15 | from run.experiment import Experiment,ExperimentDone,SystemCorrupted |
16 | from run.proc_entry import ProcEntry | 16 | from run.proc_entry import ProcEntry |
17 | 17 | ||
18 | '''Customizable experiment parameters''' | 18 | '''Customizable experiment parameters''' |
19 | ExpParams = namedtuple('ExpParams', ['scheduler', 'duration', 'tracers', | 19 | ExpParams = namedtuple('ExpParams', ['scheduler', 'duration', 'tracers', |
20 | 'kernel', 'config_options']) | 20 | 'kernel', 'config_options', 'file_params', |
21 | 'pre_script', 'post_script']) | ||
21 | '''Comparison of requested versus actual kernel compile parameter value''' | 22 | '''Comparison of requested versus actual kernel compile parameter value''' |
22 | ConfigResult = namedtuple('ConfigResult', ['param', 'wanted', 'actual']) | 23 | ConfigResult = namedtuple('ConfigResult', ['param', 'wanted', 'actual']) |
23 | 24 | ||
@@ -56,12 +57,13 @@ def parse_args(): | |||
56 | parser.add_option('-d', '--duration', dest='duration', type='int', | 57 | parser.add_option('-d', '--duration', dest='duration', type='int', |
57 | help='duration (seconds) of tasks') | 58 | help='duration (seconds) of tasks') |
58 | parser.add_option('-o', '--out-dir', dest='out_dir', | 59 | parser.add_option('-o', '--out-dir', dest='out_dir', |
59 | help='directory for data output', default=("%s/run-data"%os.getcwd())) | 60 | help='directory for data output', |
61 | default=DEFAULTS['out-run']) | ||
60 | parser.add_option('-p', '--params', dest='param_file', | 62 | parser.add_option('-p', '--params', dest='param_file', |
61 | help='file with experiment parameters') | 63 | help='file with experiment parameters') |
62 | parser.add_option('-c', '--schedule-file', dest='sched_file', | 64 | parser.add_option('-c', '--schedule-file', dest='sched_file', |
63 | help='name of schedule files within directories', | 65 | help='name of schedule files within directories', |
64 | default=conf.DEFAULTS['sched_file']) | 66 | default=DEFAULTS['sched_file']) |
65 | parser.add_option('-f', '--force', action='store_true', default=False, | 67 | parser.add_option('-f', '--force', action='store_true', default=False, |
66 | dest='force', help='overwrite existing data') | 68 | dest='force', help='overwrite existing data') |
67 | parser.add_option('-j', '--jabber', metavar='username@domain', | 69 | parser.add_option('-j', '--jabber', metavar='username@domain', |
@@ -96,7 +98,7 @@ def convert_data(data): | |||
96 | proc = (loc, match.group("CONTENT")) | 98 | proc = (loc, match.group("CONTENT")) |
97 | procs.append(proc) | 99 | procs.append(proc) |
98 | else: | 100 | else: |
99 | prog = match.group("PROG") or conf.DEFAULTS['prog'] | 101 | prog = match.group("PROG") or DEFAULTS['prog'] |
100 | spin = (prog, match.group("ARGS")) | 102 | spin = (prog, match.group("ARGS")) |
101 | tasks.append(spin) | 103 | tasks.append(spin) |
102 | 104 | ||
@@ -112,8 +114,8 @@ def fix_paths(schedule, exp_dir, sched_file): | |||
112 | args = args.replace(arg, abspath) | 114 | args = args.replace(arg, abspath) |
113 | break | 115 | break |
114 | elif re.match(r'.*\w+\.[a-zA-Z]\w*', arg): | 116 | elif re.match(r'.*\w+\.[a-zA-Z]\w*', arg): |
115 | print("WARNING: non-existent file '%s' may be referenced:\n\t%s" | 117 | sys.stderr.write("WARNING: non-existent file '%s' " % arg + |
116 | % (arg, sched_file)) | 118 | "may be referenced:\n\t%s" % sched_file) |
117 | 119 | ||
118 | schedule['task'][idx] = (task, args) | 120 | schedule['task'][idx] = (task, args) |
119 | 121 | ||
@@ -181,25 +183,23 @@ def verify_environment(exp_params): | |||
181 | raise InvalidConfig(results) | 183 | raise InvalidConfig(results) |
182 | 184 | ||
183 | 185 | ||
184 | def run_parameter(exp_dir, out_dir, params, param_name): | 186 | def run_script(script_params, exp, exp_dir, out_dir): |
185 | '''Run an executable (arguments optional) specified as a configurable | 187 | '''Run an executable (arguments optional)''' |
186 | @param_name in @params.''' | 188 | if not script_params: |
187 | if conf.PARAMS[param_name] not in params: | ||
188 | return | 189 | return |
189 | 190 | ||
190 | script_params = params[conf.PARAMS[param_name]] | ||
191 | |||
192 | # Split into arguments and program name | 191 | # Split into arguments and program name |
193 | if type(script_params) != type([]): | 192 | if type(script_params) != type([]): |
194 | script_params = [script_params] | 193 | script_params = [script_params] |
195 | script_name = script_params.pop(0) | ||
196 | 194 | ||
195 | exp.log("Running %s" % script_params.join(" ")) | ||
196 | |||
197 | script_name = script_params.pop(0) | ||
197 | script = com.get_executable(script_name, cwd=exp_dir) | 198 | script = com.get_executable(script_name, cwd=exp_dir) |
198 | 199 | ||
199 | out = open('%s/%s-out.txt' % (out_dir, param_name), 'w') | 200 | out = open('%s/%s-out.txt' % (out_dir, script_name), 'w') |
200 | prog = Executable(script, script_params, | 201 | prog = Executable(script, script_params, cwd=out_dir, |
201 | stderr_file=out, stdout_file=out) | 202 | stderr_file=out, stdout_file=out) |
202 | prog.cwd = out_dir | ||
203 | 203 | ||
204 | prog.execute() | 204 | prog.execute() |
205 | prog.wait() | 205 | prog.wait() |
@@ -207,28 +207,41 @@ def run_parameter(exp_dir, out_dir, params, param_name): | |||
207 | out.close() | 207 | out.close() |
208 | 208 | ||
209 | 209 | ||
210 | def get_exp_params(cmd_scheduler, cmd_duration, file_params): | 210 | def make_exp_params(cmd_scheduler, cmd_duration, sched_dir, param_file): |
211 | '''Return ExpParam with configured values of all hardcoded params.''' | 211 | '''Return ExpParam with configured values of all hardcoded params.''' |
212 | kernel = copts = "" | 212 | kernel = copts = "" |
213 | 213 | ||
214 | scheduler = cmd_scheduler or file_params[conf.PARAMS['sched']] | 214 | # Load parameter file |
215 | duration = cmd_duration or file_params[conf.PARAMS['dur']] or\ | 215 | param_file = param_file or "%s/%s" % (sched_dir, DEFAULTS['params_file']) |
216 | conf.DEFAULTS['duration'] | 216 | if os.path.isfile(param_file): |
217 | fparams = com.load_params(param_file) | ||
218 | else: | ||
219 | fparams = {} | ||
220 | |||
221 | scheduler = cmd_scheduler or fparams[PARAMS['sched']] | ||
222 | duration = cmd_duration or fparams[PARAMS['dur']] or\ | ||
223 | DEFAULTS['duration'] | ||
217 | 224 | ||
218 | # Experiments can specify required kernel name | 225 | # Experiments can specify required kernel name |
219 | if conf.PARAMS['kernel'] in file_params: | 226 | if PARAMS['kernel'] in fparams: |
220 | kernel = file_params[conf.PARAMS['kernel']] | 227 | kernel = fparams[PARAMS['kernel']] |
221 | 228 | ||
222 | # Or required config options | 229 | # Or required config options |
223 | if conf.PARAMS['copts'] in file_params: | 230 | if PARAMS['copts'] in fparams: |
224 | copts = file_params[conf.PARAMS['copts']] | 231 | copts = fparams[PARAMS['copts']] |
225 | 232 | ||
226 | # Or required tracers | 233 | # Or required tracers |
227 | requested = [] | 234 | requested = [] |
228 | if conf.PARAMS['trace'] in file_params: | 235 | if PARAMS['trace'] in fparams: |
229 | requested = file_params[conf.PARAMS['trace']] | 236 | requested = fparams[PARAMS['trace']] |
230 | tracers = trace.get_tracer_types(requested) | 237 | tracers = trace.get_tracer_types(requested) |
231 | 238 | ||
239 | # Or scripts to run before and after experiments | ||
240 | def get_script(name): | ||
241 | return fparams[name] if name in fparams else None | ||
242 | pre_script = get_script('pre') | ||
243 | post_script = get_script('post') | ||
244 | |||
232 | # But only these two are mandatory | 245 | # But only these two are mandatory |
233 | if not scheduler: | 246 | if not scheduler: |
234 | raise IOError("No scheduler found in param file!") | 247 | raise IOError("No scheduler found in param file!") |
@@ -236,61 +249,70 @@ def get_exp_params(cmd_scheduler, cmd_duration, file_params): | |||
236 | raise IOError("No duration found in param file!") | 249 | raise IOError("No duration found in param file!") |
237 | 250 | ||
238 | return ExpParams(scheduler=scheduler, kernel=kernel, duration=duration, | 251 | return ExpParams(scheduler=scheduler, kernel=kernel, duration=duration, |
239 | config_options=copts, tracers=tracers) | 252 | config_options=copts, tracers=tracers, file_params=fparams, |
253 | pre_script=pre_script, post_script=post_script) | ||
240 | 254 | ||
241 | 255 | def run_experiment(name, sched_file, exp_params, out_dir, | |
242 | def load_experiment(sched_file, cmd_scheduler, cmd_duration, | 256 | start_message, ignore, jabber): |
243 | param_file, out_dir, ignore, jabber): | ||
244 | '''Load and parse data from files and run result.''' | 257 | '''Load and parse data from files and run result.''' |
245 | if not os.path.isfile(sched_file): | 258 | if not os.path.isfile(sched_file): |
246 | raise IOError("Cannot find schedule file: %s" % sched_file) | 259 | raise IOError("Cannot find schedule file: %s" % sched_file) |
247 | 260 | ||
248 | dir_name, fname = os.path.split(sched_file) | 261 | dir_name, fname = os.path.split(sched_file) |
249 | exp_name = os.path.split(dir_name)[1] + "/" + fname | ||
250 | work_dir = "%s/tmp" % dir_name | 262 | work_dir = "%s/tmp" % dir_name |
251 | 263 | ||
252 | # Load parameter file | 264 | procs, execs = load_schedule(name, sched_file, exp_params.duration) |
253 | param_file = param_file or \ | ||
254 | "%s/%s" % (dir_name, conf.DEFAULTS['params_file']) | ||
255 | if os.path.isfile(param_file): | ||
256 | file_params = com.load_params(param_file) | ||
257 | else: | ||
258 | file_params = {} | ||
259 | |||
260 | # Create input needed by Experiment | ||
261 | exp_params = get_exp_params(cmd_scheduler, cmd_duration, file_params) | ||
262 | procs, execs = load_schedule(exp_name, sched_file, exp_params.duration) | ||
263 | 265 | ||
264 | exp = Experiment(exp_name, exp_params.scheduler, work_dir, out_dir, | 266 | exp = Experiment(name, exp_params.scheduler, work_dir, out_dir, |
265 | procs, execs, exp_params.tracers) | 267 | procs, execs, exp_params.tracers) |
266 | 268 | ||
269 | exp.log(start_message) | ||
270 | |||
267 | if not ignore: | 271 | if not ignore: |
268 | verify_environment(exp_params) | 272 | verify_environment(exp_params) |
269 | 273 | ||
270 | run_parameter(dir_name, work_dir, file_params, 'pre') | 274 | run_script(exp_params.pre_script, exp, dir_name, work_dir) |
271 | 275 | ||
272 | exp.run_exp() | 276 | exp.run_exp() |
273 | 277 | ||
274 | run_parameter(dir_name, out_dir, file_params, 'post') | 278 | run_script(exp_params.post_script, exp, dir_name, out_dir) |
275 | 279 | ||
276 | if jabber: | 280 | if jabber: |
277 | jabber.send("Completed '%s'" % exp_name) | 281 | jabber.send("Completed '%s'" % name) |
278 | 282 | ||
279 | # Save parameters used to run experiment in out_dir | 283 | # Save parameters used to run experiment in out_dir |
280 | out_params = dict(file_params.items() + | 284 | out_params = dict(exp_params.file_params.items() + |
281 | [(conf.PARAMS['sched'], exp_params.scheduler), | 285 | [(PARAMS['sched'], exp_params.scheduler), |
282 | (conf.PARAMS['tasks'], len(execs)), | 286 | (PARAMS['tasks'], len(execs)), |
283 | (conf.PARAMS['dur'], exp_params.duration)]) | 287 | (PARAMS['dur'], exp_params.duration)]) |
284 | 288 | ||
285 | # Feather-trace clock frequency saved for accurate overhead parsing | 289 | # Feather-trace clock frequency saved for accurate overhead parsing |
286 | ft_freq = com.ft_freq() | 290 | ft_freq = com.ft_freq() |
287 | if ft_freq: | 291 | if ft_freq: |
288 | out_params[conf.PARAMS['cycles']] = ft_freq | 292 | out_params[PARAMS['cycles']] = ft_freq |
289 | 293 | ||
290 | with open("%s/%s" % (out_dir, conf.DEFAULTS['params_file']), 'w') as f: | 294 | with open("%s/%s" % (out_dir, DEFAULTS['params_file']), 'w') as f: |
291 | f.write(str(out_params)) | 295 | f.write(str(out_params)) |
292 | 296 | ||
293 | 297 | ||
298 | def get_exps(opts, args): | ||
299 | '''Return list of experiment files or directories''' | ||
300 | if args: | ||
301 | return args | ||
302 | |||
303 | # Default to sched_file > generated dirs | ||
304 | if os.path.exists(opts.sched_file): | ||
305 | sys.stderr.write("Reading schedule from %s.\n" % opts.sched_file) | ||
306 | return [opts.sched_file] | ||
307 | elif os.path.exists(DEFAULTS['out-gen']): | ||
308 | sys.stderr.write("Reading schedules from %s/*.\n" % DEFAULTS['out-gen']) | ||
309 | sched_dirs = os.listdir(DEFAULTS['out-gen']) | ||
310 | return ['%s/%s' % (DEFAULTS['out-gen'], d) for d in sched_dirs] | ||
311 | else: | ||
312 | sys.stderr.write("Run with -h to view options.\n"); | ||
313 | sys.exit(1) | ||
314 | |||
315 | |||
294 | def setup_jabber(target): | 316 | def setup_jabber(target): |
295 | try: | 317 | try: |
296 | from run.jabber import Jabber | 318 | from run.jabber import Jabber |
@@ -301,6 +323,7 @@ def setup_jabber(target): | |||
301 | "Disabling instant messages.\n") | 323 | "Disabling instant messages.\n") |
302 | return None | 324 | return None |
303 | 325 | ||
326 | |||
304 | def setup_email(target): | 327 | def setup_email(target): |
305 | try: | 328 | try: |
306 | from run.emailer import Emailer | 329 | from run.emailer import Emailer |
@@ -314,71 +337,82 @@ def setup_email(target): | |||
314 | sys.stderr.write(message + " Disabling email message.\n") | 337 | sys.stderr.write(message + " Disabling email message.\n") |
315 | return None | 338 | return None |
316 | 339 | ||
340 | |||
341 | def make_paths(exp, out_base_dir, opts): | ||
342 | '''Translate experiment name to (schedule file, output directory) paths''' | ||
343 | path = "%s/%s" % (os.getcwd(), exp) | ||
344 | out_dir = "%s/%s" % (out_base_dir, os.path.split(exp.strip('/'))[1]) | ||
345 | |||
346 | if not os.path.exists(path): | ||
347 | raise IOError("Invalid experiment: %s" % path) | ||
348 | |||
349 | if opts.force and os.path.exists(out_dir): | ||
350 | shutil.rmtree(out_dir) | ||
351 | |||
352 | if os.path.isdir(path): | ||
353 | sched_file = "%s/%s" % (path, opts.sched_file) | ||
354 | else: | ||
355 | sched_file = path | ||
356 | |||
357 | return sched_file, out_dir | ||
358 | |||
359 | |||
317 | def main(): | 360 | def main(): |
318 | opts, args = parse_args() | 361 | opts, args = parse_args() |
319 | 362 | ||
320 | scheduler = opts.scheduler | 363 | exps = get_exps(opts, args) |
321 | duration = opts.duration | ||
322 | param_file = opts.param_file | ||
323 | out_base = os.path.abspath(opts.out_dir) | ||
324 | 364 | ||
325 | args = args or [opts.sched_file] | 365 | jabber = setup_jabber(opts.jabber) if opts.jabber else None |
366 | email = setup_email(opts.email) if opts.email else None | ||
326 | 367 | ||
327 | created = False | 368 | out_base = os.path.abspath(opts.out_dir) |
369 | created = False | ||
328 | if not os.path.exists(out_base): | 370 | if not os.path.exists(out_base): |
329 | created = True | 371 | created = True |
330 | os.mkdir(out_base) | 372 | os.mkdir(out_base) |
331 | 373 | ||
332 | ran = 0 | 374 | ran = done = succ = failed = invalid = 0 |
333 | done = 0 | ||
334 | succ = 0 | ||
335 | failed = 0 | ||
336 | invalid = 0 | ||
337 | |||
338 | jabber = setup_jabber(opts.jabber) if opts.jabber else None | ||
339 | email = setup_email(opts.email) if opts.email else None | ||
340 | |||
341 | for exp in args: | ||
342 | path = "%s/%s" % (os.getcwd(), exp) | ||
343 | out_dir = "%s/%s" % (out_base, os.path.split(exp.strip('/'))[1]) | ||
344 | 375 | ||
345 | if not os.path.exists(path): | 376 | for i, exp in enumerate(exps): |
346 | raise IOError("Invalid experiment: %s" % path) | 377 | sched_file, out_dir = make_paths(exp, out_base, opts) |
378 | sched_dir = os.path.split(sched_file)[0] | ||
347 | 379 | ||
348 | if opts.force and os.path.exists(out_dir): | 380 | try: |
349 | shutil.rmtree(out_dir) | 381 | start_message = "Loading experiment %d of %d." % (i+1, len(exps)) |
382 | exp_params = make_exp_params(opts.scheduler, opts.duration, | ||
383 | sched_dir, opts.param_file) | ||
350 | 384 | ||
351 | if os.path.isdir(exp): | 385 | run_experiment(exp, sched_file, exp_params, out_dir, |
352 | path = "%s/%s" % (path, opts.sched_file) | 386 | start_message, opts.ignore, jabber) |
353 | 387 | ||
354 | try: | ||
355 | load_experiment(path, scheduler, duration, param_file, | ||
356 | out_dir, opts.ignore, jabber) | ||
357 | succ += 1 | 388 | succ += 1 |
358 | except ExperimentDone: | 389 | except ExperimentDone: |
390 | sys.stderr.write("Experiment '%s' already completed " % exp + | ||
391 | "at '%s'\n" % out_base) | ||
359 | done += 1 | 392 | done += 1 |
360 | print("Experiment '%s' already completed at '%s'" % (exp, out_base)) | ||
361 | except (InvalidKernel, InvalidConfig) as e: | 393 | except (InvalidKernel, InvalidConfig) as e: |
394 | sys.stderr.write("Invalid environment for experiment '%s'\n" % exp) | ||
395 | sys.stderr.write("%s\n" % e) | ||
362 | invalid += 1 | 396 | invalid += 1 |
363 | print("Invalid environment for experiment '%s'" % exp) | ||
364 | print(e) | ||
365 | except KeyboardInterrupt: | 397 | except KeyboardInterrupt: |
366 | print("Keyboard interrupt, quitting") | 398 | sys.stderr.write("Keyboard interrupt, quitting\n") |
367 | break | 399 | break |
368 | except SystemCorrupted as e: | 400 | except SystemCorrupted as e: |
369 | print("System is corrupted! Fix state before continuing.") | 401 | sys.stderr.write("System is corrupted! Fix state before continuing.\n") |
370 | print(e) | 402 | sys.stderr.write("%s\n" % e) |
371 | break | 403 | break |
372 | except ExperimentFailed: | 404 | except Exception as e: |
373 | print("Failed experiment %s" % exp) | 405 | sys.stderr.write("Failed experiment %s\n" % exp) |
406 | sys.stderr.write("%s\n" % e) | ||
374 | failed += 1 | 407 | failed += 1 |
375 | 408 | ||
376 | ran += 1 | 409 | ran += 1 |
377 | 410 | ||
411 | # Clean out directory if it failed immediately | ||
378 | if not os.listdir(out_base) and created and not succ: | 412 | if not os.listdir(out_base) and created and not succ: |
379 | os.rmdir(out_base) | 413 | os.rmdir(out_base) |
380 | 414 | ||
381 | message = "Experiments ran:\t%d of %d" % (ran, len(args)) +\ | 415 | message = "Experiments ran:\t%d of %d" % (ran, len(exps)) +\ |
382 | "\n Successful:\t\t%d" % succ +\ | 416 | "\n Successful:\t\t%d" % succ +\ |
383 | "\n Failed:\t\t%d" % failed +\ | 417 | "\n Failed:\t\t%d" % failed +\ |
384 | "\n Already Done:\t\t%d" % done +\ | 418 | "\n Already Done:\t\t%d" % done +\ |
@@ -386,6 +420,10 @@ def main(): | |||
386 | 420 | ||
387 | print(message) | 421 | print(message) |
388 | 422 | ||
423 | if succ: | ||
424 | sys.stderr.write("Successful experiment data saved in %s.\n" % | ||
425 | opts.out_dir) | ||
426 | |||
389 | if email: | 427 | if email: |
390 | email.send(message) | 428 | email.send(message) |
391 | email.close() | 429 | email.close() |