diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-22 15:32:12 -0400 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-04-22 15:32:12 -0400 |
| commit | 25ccdb0cbc6b959b1f96c89b8bce91963cb67b4c (patch) | |
| tree | dcaeb7d85f3dcc0f2afbb53d11c512c71fb712ab | |
| parent | fbd1df6f63eb551b99f71330d2370c570ff323f5 (diff) | |
Improved robustness of run_exps.py execution.
Thanks to bcw and gelliott for debugging and ideas.
* Print out experiment number and total experiments when starting experiments.
* Only sleep and re-release tasks if tasks are waiting to release.
* Fail experiment with verbose messages if any tasks fail before becoming ready
to release.
* When waiting for tasks to become ready for release, reset the waiting time
whenever a new task (or task(s)) become ready.
* Start regular tracers BEFORE the plugin switch to log data from the switch.
* Check the number of running tasks AFTER trying to switch the linux scheduler.
This gives plugin deactivate code the opportunity to kill these tasks.
* If an invalid executable is specified in the schedule file, fail before
attempting to run the experiment and print out the problem.
* Propogate exceptions up from experiment failures instead of creating
ExperimentFailed exceptions.
This commit also made clock-frequency automatically ignored by parse_exps.py.
The value of this would change by +- a Mhz between experiments, ruining graphs.
| -rw-r--r-- | config/config.py | 2 | ||||
| -rwxr-xr-x | parse_exps.py | 4 | ||||
| -rw-r--r-- | run/executable/executable.py | 3 | ||||
| -rw-r--r-- | run/experiment.py | 185 | ||||
| -rw-r--r-- | run/litmus_util.py | 13 | ||||
| -rwxr-xr-x | run_exps.py | 170 |
6 files changed, 216 insertions, 161 deletions
diff --git a/config/config.py b/config/config.py index 1ac468b..b631aa2 100644 --- a/config/config.py +++ b/config/config.py | |||
| @@ -9,7 +9,7 @@ BINS = {'rtspin' : get_executable_hint('rtspin', 'liblitmus'), | |||
| 9 | 'ftsplit' : get_executable_hint('ft2csv', 'feather-trace-tools'), | 9 | 'ftsplit' : get_executable_hint('ft2csv', 'feather-trace-tools'), |
| 10 | 'ftsort' : get_executable_hint('ftsort', 'feather-trace-tools'), | 10 | 'ftsort' : get_executable_hint('ftsort', 'feather-trace-tools'), |
| 11 | 'st_trace' : get_executable_hint('st_trace', 'feather-trace-tools'), | 11 | 'st_trace' : get_executable_hint('st_trace', 'feather-trace-tools'), |
| 12 | # Option, as not everyone uses kernelshark yet | 12 | # Optional, as not everyone uses kernelshark yet |
| 13 | 'trace-cmd' : get_executable_hint('trace-cmd', 'rt-kernelshark', True), | 13 | 'trace-cmd' : get_executable_hint('trace-cmd', 'rt-kernelshark', True), |
| 14 | # Optional, as sched_trace is not a publically supported repository | 14 | # Optional, as sched_trace is not a publically supported repository |
| 15 | 'st_show' : get_executable_hint('st_show', 'sched_trace', True)} | 15 | 'st_show' : get_executable_hint('st_show', 'sched_trace', True)} |
diff --git a/parse_exps.py b/parse_exps.py index d07378c..c2cbedb 100755 --- a/parse_exps.py +++ b/parse_exps.py | |||
| @@ -140,8 +140,8 @@ def main(): | |||
| 140 | if opts.ignore: | 140 | if opts.ignore: |
| 141 | for param in opts.ignore.split(","): | 141 | for param in opts.ignore.split(","): |
| 142 | builder.try_remove(param) | 142 | builder.try_remove(param) |
| 143 | # Always average multiple trials | 143 | builder.try_remove(PARAMS['trial']) # Always average multiple trials |
| 144 | builder.try_remove(PARAMS['trial']) | 144 | builder.try_remove(PARAMS['cycles']) # Only need for feather-trace parsing |
| 145 | 145 | ||
| 146 | col_map = builder.build() | 146 | col_map = builder.build() |
| 147 | result_table = TupleTable(col_map) | 147 | result_table = TupleTable(col_map) |
diff --git a/run/executable/executable.py b/run/executable/executable.py index e6f2003..a2426f1 100644 --- a/run/executable/executable.py +++ b/run/executable/executable.py | |||
| @@ -59,6 +59,9 @@ class Executable(object): | |||
| 59 | def interrupt(self): | 59 | def interrupt(self): |
| 60 | self.sp.send_signal(signal.SIGINT) | 60 | self.sp.send_signal(signal.SIGINT) |
| 61 | 61 | ||
| 62 | def poll(self): | ||
| 63 | return self.sp.poll() | ||
| 64 | |||
| 62 | def terminate(self): | 65 | def terminate(self): |
| 63 | '''Send the terminate signal to the binary.''' | 66 | '''Send the terminate signal to the binary.''' |
| 64 | self.sp.terminate() | 67 | self.sp.terminate() |
diff --git a/run/experiment.py b/run/experiment.py index ff0e9f3..b0e46b6 100644 --- a/run/experiment.py +++ b/run/experiment.py | |||
| @@ -1,9 +1,7 @@ | |||
| 1 | import common as com | ||
| 2 | import os | 1 | import os |
| 3 | import time | 2 | import time |
| 4 | import run.litmus_util as lu | 3 | import run.litmus_util as lu |
| 5 | import shutil as sh | 4 | import shutil as sh |
| 6 | import traceback | ||
| 7 | from operator import methodcaller | 5 | from operator import methodcaller |
| 8 | 6 | ||
| 9 | class ExperimentException(Exception): | 7 | class ExperimentException(Exception): |
| @@ -11,16 +9,11 @@ class ExperimentException(Exception): | |||
| 11 | def __init__(self, name): | 9 | def __init__(self, name): |
| 12 | self.name = name | 10 | self.name = name |
| 13 | 11 | ||
| 14 | |||
| 15 | class ExperimentDone(ExperimentException): | 12 | class ExperimentDone(ExperimentException): |
| 16 | '''Raised when an experiment looks like it's been run already.''' | 13 | '''Raised when an experiment looks like it's been run already.''' |
| 17 | def __str__(self): | 14 | def __str__(self): |
| 18 | return "Experiment finished already: %d" % self.name | 15 | return "Experiment finished already: %d" % self.name |
| 19 | 16 | ||
| 20 | class ExperimentFailed(ExperimentException): | ||
| 21 | def __str__(self): | ||
| 22 | return "Experiment failed during execution: %d" % self.name | ||
| 23 | |||
| 24 | class SystemCorrupted(Exception): | 17 | class SystemCorrupted(Exception): |
| 25 | pass | 18 | pass |
| 26 | 19 | ||
| @@ -31,7 +24,6 @@ class Experiment(object): | |||
| 31 | def __init__(self, name, scheduler, working_dir, finished_dir, | 24 | def __init__(self, name, scheduler, working_dir, finished_dir, |
| 32 | proc_entries, executables, tracer_types): | 25 | proc_entries, executables, tracer_types): |
| 33 | '''Run an experiment, optionally wrapped in tracing.''' | 26 | '''Run an experiment, optionally wrapped in tracing.''' |
| 34 | |||
| 35 | self.name = name | 27 | self.name = name |
| 36 | self.scheduler = scheduler | 28 | self.scheduler = scheduler |
| 37 | self.working_dir = working_dir | 29 | self.working_dir = working_dir |
| @@ -40,16 +32,10 @@ class Experiment(object): | |||
| 40 | self.executables = executables | 32 | self.executables = executables |
| 41 | self.exec_out = None | 33 | self.exec_out = None |
| 42 | self.exec_err = None | 34 | self.exec_err = None |
| 35 | self.tracer_types = tracer_types | ||
| 43 | 36 | ||
| 44 | self.task_batch = com.num_cpus() | 37 | def __setup_tracers(self): |
| 45 | 38 | tracers = [ t(self.working_dir) for t in self.tracer_types ] | |
| 46 | self.__make_dirs() | ||
| 47 | self.__assign_executable_cwds() | ||
| 48 | self.__setup_tracers(tracer_types) | ||
| 49 | |||
| 50 | |||
| 51 | def __setup_tracers(self, tracer_types): | ||
| 52 | tracers = [ t(self.working_dir) for t in tracer_types ] | ||
| 53 | 39 | ||
| 54 | self.regular_tracers = [t for t in tracers if not t.is_exact()] | 40 | self.regular_tracers = [t for t in tracers if not t.is_exact()] |
| 55 | self.exact_tracers = [t for t in tracers if t.is_exact()] | 41 | self.exact_tracers = [t for t in tracers if t.is_exact()] |
| @@ -84,13 +70,11 @@ class Experiment(object): | |||
| 84 | map(assign_cwd, self.executables) | 70 | map(assign_cwd, self.executables) |
| 85 | 71 | ||
| 86 | def __kill_all(self): | 72 | def __kill_all(self): |
| 87 | # Give time for whatever failed to finish failing | 73 | if lu.waiting_tasks(): |
| 88 | time.sleep(2) | 74 | released = lu.release_tasks() |
| 89 | 75 | self.log("Re-released %d tasks" % released) | |
| 90 | released = lu.release_tasks() | ||
| 91 | self.log("Re-released %d tasks" % released) | ||
| 92 | 76 | ||
| 93 | time.sleep(5) | 77 | time.sleep(1) |
| 94 | 78 | ||
| 95 | self.log("Killing all tasks") | 79 | self.log("Killing all tasks") |
| 96 | for e in self.executables: | 80 | for e in self.executables: |
| @@ -99,7 +83,62 @@ class Experiment(object): | |||
| 99 | except: | 83 | except: |
| 100 | pass | 84 | pass |
| 101 | 85 | ||
| 102 | time.sleep(2) | 86 | time.sleep(1) |
| 87 | |||
| 88 | def __strip_path(self, path): | ||
| 89 | '''Shorten path to something more readable.''' | ||
| 90 | file_dir = os.path.split(self.working_dir)[0] | ||
| 91 | if path.index(file_dir) == 0: | ||
| 92 | path = path[len(file_dir):] | ||
| 93 | |||
| 94 | return path.strip("/") | ||
| 95 | |||
| 96 | def __check_tasks_status(self): | ||
| 97 | '''Raises an exception if any tasks have failed.''' | ||
| 98 | msgs = [] | ||
| 99 | |||
| 100 | for e in self.executables: | ||
| 101 | status = e.poll() | ||
| 102 | if status != None and status: | ||
| 103 | err_msg = "Task %s failed with status: %s" % (e.wait(), status) | ||
| 104 | msgs += [err_msg] | ||
| 105 | |||
| 106 | if msgs: | ||
| 107 | # Show at most 3 messages so that every task failing doesn't blow | ||
| 108 | # up the terminal | ||
| 109 | if len(msgs) > 3: | ||
| 110 | num_errs = len(msgs) - 3 | ||
| 111 | msgs = msgs[0:4] + ["...%d more task errors..." % num_errs] | ||
| 112 | |||
| 113 | out_name = self.__strip_path(self.exec_out.name) | ||
| 114 | err_name = self.__strip_path(self.exec_err.name) | ||
| 115 | help = "Check dmesg, %s, and %s" % (out_name, err_name) | ||
| 116 | |||
| 117 | raise Exception("\n".join(msgs + [help])) | ||
| 118 | |||
| 119 | def __wait_for_ready(self): | ||
| 120 | self.log("Sleeping until tasks are ready for release...") | ||
| 121 | |||
| 122 | wait_start = time.time() | ||
| 123 | num_ready = lu.waiting_tasks() | ||
| 124 | |||
| 125 | while num_ready < len(self.executables): | ||
| 126 | # Quit if too much time passes without a task becoming ready | ||
| 127 | if time.time() - wait_start > 180.0: | ||
| 128 | s = "waiting: %d, submitted: %d" %\ | ||
| 129 | (lu.waiting_tasks(), len(self.executables)) | ||
| 130 | raise Exception("Too much time spent waiting for tasks! %s" % s) | ||
| 131 | |||
| 132 | time.sleep(1) | ||
| 133 | |||
| 134 | # Quit if any tasks fail | ||
| 135 | self.__check_tasks_status() | ||
| 136 | |||
| 137 | # Reset the waiting time whenever more tasks become ready | ||
| 138 | now_ready = lu.waiting_tasks() | ||
| 139 | if now_ready != num_ready: | ||
| 140 | wait_start = time.time() | ||
| 141 | num_ready = lu.now_ready | ||
| 103 | 142 | ||
| 104 | def __run_tasks(self): | 143 | def __run_tasks(self): |
| 105 | self.log("Starting %d tasks" % len(self.executables)) | 144 | self.log("Starting %d tasks" % len(self.executables)) |
| @@ -108,16 +147,9 @@ class Experiment(object): | |||
| 108 | try: | 147 | try: |
| 109 | e.execute() | 148 | e.execute() |
| 110 | except: | ||
