aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2012-09-16 20:46:19 -0400
committerJonathan Herman <hermanjl@cs.unc.edu>2012-09-16 20:46:19 -0400
commitbdb33621ac67b2cd9fadf3f3b006419ebb16a713 (patch)
tree8918b5dbc6db8a73c275e445153c7ea42857210b
parentfd92ecb5a642eeae6c54d3cca1508fc4c4cb6a87 (diff)
Created run_exps.py script.
Currently poorly documented.
-rw-r--r--.gitignore4
-rw-r--r--__init__.py0
-rw-r--r--config/__init__.py0
-rw-r--r--config/config.example.py46
-rw-r--r--config/config.py46
-rw-r--r--experiment/__init__.py0
-rw-r--r--experiment/executable/__init__.py0
-rw-r--r--experiment/executable/executable.py71
-rw-r--r--experiment/executable/ftcat.py33
-rw-r--r--experiment/experiment.py153
-rw-r--r--experiment/litmus_util.py63
-rw-r--r--experiment/proc_entry.py12
-rw-r--r--experiment/tracer.py118
-rwxr-xr-x[-rw-r--r--]run_exps.py192
14 files changed, 738 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c62cf4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
1*~
2\#*#
3*.pyc
4.ropeproject* \ No newline at end of file
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/__init__.py
diff --git a/config/__init__.py b/config/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/__init__.py
diff --git a/config/config.example.py b/config/config.example.py
new file mode 100644
index 0000000..5176460
--- /dev/null
+++ b/config/config.example.py
@@ -0,0 +1,46 @@
1from __future__ import print_function
2import os
3import sys
4
5REPOS = {'liblitmus' : '/home/hermanjl/git/liblitmus',
6 'sched_trace' : '/home/hermanjl/git/sched_trace',
7 'analysis' : '/home/hermanjl/git/overhead-analysis-cjk',
8 'ft_tools' : '/home/hermanjl/git/ft_tools/ftcat',
9 'trace-cmd' : '/home/hermanjl/git/trace-cmd'}
10
11BINS = {'bespin' : '{}/bespin'.format(REPOS['liblitmus']),
12 'colorspin' : '{}/colorspin'.format(REPOS['liblitmus']),
13 'rtspin' : '{}/rtspin'.format(REPOS['liblitmus']),
14 'release' : '{}/release_ts'.format(REPOS['liblitmus']),
15 'ftcat' : '{}/ftcat'.format(REPOS['ft_tools']),
16 'st_trace' : '{}/st_trace'.format(REPOS['ft_tools']),
17 'split' : '{}/split'.format(REPOS['analysis']),
18 'sort' : '{}/sort-all'.format(REPOS['analysis']),
19 'analyze' : '{}/analyze'.format(REPOS['analysis']),
20 'trace-cmd' : '{}/trace-cmd'.format(REPOS['trace-cmd'])}
21
22DEFAULTS = {'params_file' : 'params.py',
23 'sched_file' : 'sched.py',
24 'exps_file' : 'exps.py',
25 'duration' : '10',
26 'spin' : 'rtspin'}
27
28FILES = {'ft_data' : 'ft.bin',
29 'linux_data' : 'trace.dat',
30 'sched_data' : 'st-{}.bin',
31 'log_data' : 'trace.slog'}
32
33PARAMS = {'sched' : 'scheduler',
34 'dur' : 'duration'}
35
36valid = True
37for repo, loc in REPOS.items():
38 if not os.path.isdir(loc):
39 valid = False
40 print("Cannot access repo '%s' at '%s'" % (repo, loc), file=sys.stderr)
41for prog, loc in BINS.items():
42 if not os.path.isfile(loc):
43 valid = False
44 print("Cannot access program '%s' at '%s'" % (prog, loc), file=sys.stderr)
45if not valid:
46 print("Errors in config file", file=sys.stderr)
diff --git a/config/config.py b/config/config.py
new file mode 100644
index 0000000..5176460
--- /dev/null
+++ b/config/config.py
@@ -0,0 +1,46 @@
1from __future__ import print_function
2import os
3import sys
4
5REPOS = {'liblitmus' : '/home/hermanjl/git/liblitmus',
6 'sched_trace' : '/home/hermanjl/git/sched_trace',
7 'analysis' : '/home/hermanjl/git/overhead-analysis-cjk',
8 'ft_tools' : '/home/hermanjl/git/ft_tools/ftcat',
9 'trace-cmd' : '/home/hermanjl/git/trace-cmd'}
10
11BINS = {'bespin' : '{}/bespin'.format(REPOS['liblitmus']),
12 'colorspin' : '{}/colorspin'.format(REPOS['liblitmus']),
13 'rtspin' : '{}/rtspin'.format(REPOS['liblitmus']),
14 'release' : '{}/release_ts'.format(REPOS['liblitmus']),
15 'ftcat' : '{}/ftcat'.format(REPOS['ft_tools']),
16 'st_trace' : '{}/st_trace'.format(REPOS['ft_tools']),
17 'split' : '{}/split'.format(REPOS['analysis']),
18 'sort' : '{}/sort-all'.format(REPOS['analysis']),
19 'analyze' : '{}/analyze'.format(REPOS['analysis']),
20 'trace-cmd' : '{}/trace-cmd'.format(REPOS['trace-cmd'])}
21
22DEFAULTS = {'params_file' : 'params.py',
23 'sched_file' : 'sched.py',
24 'exps_file' : 'exps.py',
25 'duration' : '10',
26 'spin' : 'rtspin'}
27
28FILES = {'ft_data' : 'ft.bin',
29 'linux_data' : 'trace.dat',
30 'sched_data' : 'st-{}.bin',
31 'log_data' : 'trace.slog'}
32
33PARAMS = {'sched' : 'scheduler',
34 'dur' : 'duration'}
35
36valid = True
37for repo, loc in REPOS.items():
38 if not os.path.isdir(loc):
39 valid = False
40 print("Cannot access repo '%s' at '%s'" % (repo, loc), file=sys.stderr)
41for prog, loc in BINS.items():
42 if not os.path.isfile(loc):
43 valid = False
44 print("Cannot access program '%s' at '%s'" % (prog, loc), file=sys.stderr)
45if not valid:
46 print("Errors in config file", file=sys.stderr)
diff --git a/experiment/__init__.py b/experiment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/experiment/__init__.py
diff --git a/experiment/executable/__init__.py b/experiment/executable/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/experiment/executable/__init__.py
diff --git a/experiment/executable/executable.py b/experiment/executable/executable.py
new file mode 100644
index 0000000..6697a8d
--- /dev/null
+++ b/experiment/executable/executable.py
@@ -0,0 +1,71 @@
1import sys
2import subprocess
3import signal
4from ..litmus_util import is_executable
5
6class Executable(object):
7 """Parent object that represents an executable for use in task-sets."""
8
9 def __init__(self, exec_file, extra_args=None, stdout_file = None, stderr_file = None):
10 self.exec_file = exec_file
11 self.cwd = None
12 self.stdout_file = stdout_file
13 self.stderr_file = stderr_file
14 self.sp = None
15
16 if extra_args is None:
17 self.extra_args = None
18 else:
19 self.extra_args = list(extra_args) # make a duplicate
20
21 if not is_executable(self.exec_file):
22 raise Exception("Not executable ? : %s" % self.exec_file)
23
24 def __del__(self):
25 # Try and clean up
26 if self.stdout_file is not None:
27 self.stdout_file.close()
28 if self.stderr_file is not None:
29 self.stderr_file.close()
30
31 if self.sp is not None:
32 try:
33 self.sp.terminate()
34 except OSError as e:
35 if e.errno == 3:
36 pass # no such process (already killed), okay
37 else:
38 raise e
39
40 def __get_full_command(self):
41 full_command = [self.exec_file]
42 if self.extra_args is not None:
43 full_command += self.extra_args
44 return full_command
45
46 def execute(self):
47 """Execute the binary."""
48 full_command = self.__get_full_command()
49 self.sp = subprocess.Popen(full_command, stdout=self.stdout_file,
50 stderr=self.stderr_file, cwd=self.cwd)
51
52 def kill(self):
53 self.sp.kill()
54
55 def interrupt(self):
56 self.sp.send_signal(signal.SIGINT)
57
58 def terminate(self):
59 """Send the terminate signal to the binary."""
60 self.sp.terminate()
61
62 def wait(self):
63 """Wait until the executable is finished, checking return code.
64
65 If the exit status is non-zero, raise an exception.
66
67 """
68
69 self.sp.wait()
70 if self.sp.returncode != 0:
71 print >>sys.stderr, "Non-zero return: %s %s" % (self.exec_file, self.extra_args)
diff --git a/experiment/executable/ftcat.py b/experiment/executable/ftcat.py
new file mode 100644
index 0000000..9966312
--- /dev/null
+++ b/experiment/executable/ftcat.py
@@ -0,0 +1,33 @@
1import os
2import stat
3
4from executable import Executable
5
6class FTcat(Executable):
7 """Used to wrap the ftcat binary in the Experiment object."""
8
9 def __init__(self, ft_cat_bin, stdout_file, stderr_file, dev, events, cpu=None):
10 """Extends the Executable initializer method with ftcat attributes."""
11
12 # hack to run FTCat at higher priority
13 chrt_bin = '/usr/bin/chrt'
14
15 super(FTcat, self).__init__(chrt_bin)
16 self.stdout_file = stdout_file
17 self.stderr_file = stderr_file
18
19 mode = os.stat(dev)[stat.ST_MODE]
20 if not mode & stat.S_IFCHR:
21 raise Exception("%s is not a character device" % dev)
22
23 if events is None:
24 raise Exception('No events!')
25
26 # hack to run FTCat at higher priority
27 self.extra_args = ['-f', '40']
28 if cpu is not None:
29 # and bind it to a CPU
30 self.extra_args.extend(['/usr/bin/taskset', '-c', str(cpu)])
31 events_str_arr = map(str, events)
32 self.extra_args.extend([ft_cat_bin, dev] + events_str_arr)
33
diff --git a/experiment/experiment.py b/experiment/experiment.py
new file mode 100644
index 0000000..29e6bd7
--- /dev/null
+++ b/experiment/experiment.py
@@ -0,0 +1,153 @@
1import os
2import time
3import litmus_util
4from operator import methodcaller
5from tracer import SchedTracer, LogTracer, PerfTracer, LinuxTracer, OverheadTracer
6
7class ExperimentException(Exception):
8 """Used to indicate when there are problems with an experiment."""
9 def __init__(self, name):
10 self.name = name
11
12
13class ExperimentDone(ExperimentException):
14 """Raised when an experiment looks like it's been run already."""
15 def __str__(self):
16 return "Experiment finished already: %d" % self.name
17
18
19class ExperimentInterrupted(ExperimentException):
20 """Raised when an experiment appears to be interrupted (partial results)."""
21 def __str__(self):
22 return "Experiment was interrupted in progress: %d" % self.name
23
24
25class ExperimentFailed(ExperimentException):
26 def __str__(self):
27 return "Experiment failed during execution: %d" % self.name
28
29
30class Experiment(object):
31 """Execute one task-set and save the results. Experiments have unique IDs."""
32
33 def __init__(self, name, scheduler, working_dir, finished_dir, proc_entries, executables):
34 """Run an experiment, optionally wrapped in tracing."""
35
36 self.name = name
37 self.scheduler = scheduler
38 self.working_dir = working_dir
39 self.finished_dir = finished_dir
40 self.proc_entries = proc_entries
41 self.executables = executables
42
43 self.__make_dirs()
44 self.__assign_executable_cwds()
45
46 self.tracers = []
47 if SchedTracer.enabled():
48 self.tracers.append( SchedTracer(working_dir) )
49 if LinuxTracer.enabled():
50 self.tracers.append( LinuxTracer(working_dir) )
51 if LogTracer.enabled():
52 self.tracers.append( LogTracer(working_dir) )
53 if PerfTracer.enabled():
54 self.tracers.append( PerfTracer(working_dir) )
55
56 # Overhead trace must be handled seperately, see __run_tasks
57 if OverheadTracer.enabled():
58 self.overhead_trace = OverheadTracer(working_dir)
59 else:
60 self.overhead_trace = None
61
62 def __make_dirs(self):
63 if os.path.exists(self.finished_dir):
64 raise ExperimentDone(self.name)
65 if os.path.exists(self.working_dir):
66 raise ExperimentInterrupted(self.name)
67
68 os.mkdir(self.working_dir)
69
70 def __assign_executable_cwds(self):
71 def assign_cwd(executable):
72 executable.cwd = self.working_dir
73 map(assign_cwd, self.executables)
74
75 def __run_tasks(self):
76 exec_pause = 0.3
77 self.log("Starting the program in ({0} seconds)".format(
78 len(self.executables) * exec_pause))
79 for e in self.executables:
80 e.execute()
81 time.sleep(exec_pause)
82
83 sleep_time = 2
84 self.log("Sleeping for %d seconds before release" % sleep_time)
85 time.sleep(sleep_time)
86
87 # Overhead tracer must be started right after release or overhead
88 # measurements will be full of irrelevant records
89 if self.overhead_trace:
90 self.log("Starting overhead trace")
91 self.overhead_trace.start_tracing()
92
93 released = litmus_util.release_tasks()
94
95 ret = True
96 if released != len(self.executables):
97 self.log("Failed to release %d tasks! Re-releasing and killing".format(
98 len(self.experiments) - released))
99
100 time.sleep(10)
101 litmus_util.release_tasks()
102
103 time.sleep(20)
104 map(methodcaller('kill'), self.executables)
105
106 ret = False
107
108 self.log("Waiting for program to finish...")
109 map(methodcaller('wait'), self.executables)
110
111 # And it must be stopped here for the same reason
112 if self.overhead_trace:
113 self.log("Stopping overhead trace")
114 self.overhead_trace.stop_tracing()
115
116 if not ret:
117 raise ExperimentFailed(self.name)
118
119 def __save_results(self):
120 os.rename(self.working_dir, self.finished_dir)
121
122 def log(self, msg):
123 print "[Exp %s]: %s" % (self.name, msg)
124
125 def run_exp(self):
126 self.setup()
127 self.__run_tasks()
128 self.teardown()
129
130 def setup(self):
131 self.log("Switching to %s" % self.scheduler)
132 litmus_util.switch_scheduler(self.scheduler)
133
134 self.log("Writing %d proc entries" % len(self.proc_entries))
135 map(methodcaller('write_proc'), self.proc_entries)
136
137 self.log("Starting %d tracers" % len(self.tracers))
138 map(methodcaller('start_tracing'), self.tracers)
139
140 def teardown(self):
141 sleep_time = 5
142 self.log("Sleeping %d seconds to allow buffer flushing" % sleep_time)
143 time.sleep(sleep_time)
144
145 self.log("Stopping tracers")
146 map(methodcaller('stop_tracing'), self.tracers)
147
148 self.log("Switching to Linux scheduler")
149 litmus_util.switch_scheduler("Linux")
150
151 self.log("Saving results in %s" % self.finished_dir)
152 self.__save_results()
153 self.log("Experiment done!")
diff --git a/experiment/litmus_util.py b/experiment/litmus_util.py
new file mode 100644
index 0000000..114f4c9
--- /dev/null
+++ b/experiment/litmus_util.py
@@ -0,0 +1,63 @@
1import re
2import time
3import subprocess
4import os
5import stat
6import config.config as conf
7
8def num_cpus():
9 """Return the number of CPUs in the system."""
10
11 lnx_re = re.compile(r'^(processor|online)')
12 cpus = 0
13
14 with open('/proc/cpuinfo', 'r') as f:
15 for line in f:
16 if lnx_re.match(line):
17 cpus += 1
18 return cpus
19
20def switch_scheduler(switch_to_in):
21 """Switch the scheduler to whatever is passed in.
22
23 This methods sleeps for two seconds to give Linux the chance to execute
24 schedule switching code. Raises an exception if the switch does not work.
25
26 """
27
28 switch_to = str(switch_to_in).strip()
29
30 with open('/proc/litmus/active_plugin', 'w') as active_plugin:
31 subprocess.Popen(["echo", switch_to], stdout=active_plugin)
32
33 # it takes a bit to do the switch, sleep an arbitrary amount of time
34 time.sleep(2)
35
36 with open('/proc/litmus/active_plugin', 'r') as active_plugin:
37 cur_plugin = active_plugin.read().strip()
38
39 if switch_to != cur_plugin:
40 raise Exception("Could not switch to plugin: %s" % switch_to)
41
42
43def is_executable(fname):
44 """Return whether the file passed in is executable"""
45 mode = os.stat(fname)[stat.ST_MODE]
46 return mode & stat.S_IXUSR and mode & stat.S_IRUSR
47
48def is_device(dev):
49 if not os.path.exists(dev):
50 return False
51 mode = os.stat(dev)[stat.ST_MODE]
52 return not (not mode & stat.S_IFCHR)
53
54def release_tasks():
55
56 try:
57 data = subprocess.check_output([conf.BINS['release']])
58 except subprocess.CalledProcessError:
59 raise Exception('Something went wrong in release_ts')
60
61 released = re.findall(r"([0-9]+) real-time", data)[0]
62
63 return int(released)
diff --git a/experiment/proc_entry.py b/experiment/proc_entry.py
new file mode 100644
index 0000000..0b7f9ce
--- /dev/null
+++ b/experiment/proc_entry.py
@@ -0,0 +1,12 @@
1import os
2
3class ProcEntry(object):
4 def __init__(self, proc, data):
5 self.proc = proc
6 self.data = data
7
8 def write_proc(self):
9 if not os.path.exists(self.proc):
10 raise Exception("Invalid proc entry %s" % self.proc)
11 with open(self.proc, 'w') as entry:
12 entry.write(self.data)
diff --git a/experiment/tracer.py b/experiment/tracer.py
new file mode 100644
index 0000000..d7743ad
--- /dev/null
+++ b/experiment/tracer.py
@@ -0,0 +1,118 @@
1import litmus_util
2import os
3from operator import methodcaller
4from executable.ftcat import FTcat,Executable
5from config.config import FILES,BINS
6
7class Tracer(object):
8 def __init__(self, name, output_dir):
9 self.name = name
10 self.output_dir = output_dir
11 self.bins = []
12
13 def start_tracing(self):
14 map(methodcaller("execute"), self.bins)
15
16 def stop_tracing(self):
17 map(methodcaller('terminate'), self.bins)
18 map(methodcaller('wait'), self.bins)
19
20
21class LinuxTracer(Tracer):
22 EVENT_ROOT = "/sys/kernel/debug/tracing"
23 LITMUS_EVENTS = "%s/events/litmus" % EVENT_ROOT
24
25 def __init__(self, output_dir):
26 super(LinuxTracer, self).__init__("trace-cmd", output_dir)
27
28 extra_args = ["record", "-e", "sched:sched_switch",
29 "-e", "litmus:*",
30 "-o", "%s/%s" % (output_dir, FILES['linux_data'])]
31 stdout = open('%s/trace-cmd-stdout.txt' % self.output_dir, 'w')
32 stderr = open('%s/trace-cmd-stderr.txt' % self.output_dir, 'w')
33
34 execute = Executable(BINS['trace-cmd'], extra_args, stdout, stderr)
35 self.bins.append(execute)
36
37 @staticmethod
38 def enabled():
39 return os.path.exists(LinuxTracer.LITMUS_EVENTS)
40
41 def stop_tracing(self):
42 map(methodcaller('interrupt'), self.bins)
43 map(methodcaller('wait'), self.bins)
44
45
46class LogTracer(Tracer):
47 DEVICE_STR = '/dev/litmus/log'
48
49 def __init__(self, output_dir):
50 super(LogTracer, self).__init__("Logger", output_dir)
51
52 out_file = open("%s/%s" % (self.output_dir, FILES['log_data']), 'w')
53
54 cat = (Executable("/bin/cat", [LogTracer.DEVICE_STR]))
55 cat.stdout_file = out_file
56
57 self.bins.append(cat)
58
59 @staticmethod
60 def enabled():
61 return litmus_util.is_device(LogTracer.DEVICE_STR)
62
63
64class SchedTracer(Tracer):
65 EVENTS = range(501, 510) # not including 511
66 DEVICE_STR = '/dev/litmus/sched_trace'
67
68 def __init__(self, output_dir):
69 super(SchedTracer, self).__init__("Sched Trace", output_dir)
70
71 if SchedTracer.enabled():
72 for cpu in range(litmus_util.num_cpus()):
73 # Executable will close the stdout/stderr files
74 stdout_f = open('%s/st-%d.bin' % (self.output_dir, cpu), 'w')
75 stderr_f = open('%s/st-%d-stderr.txt' % (self.output_dir, cpu), 'w')
76 dev = '{0}{1}'.format(SchedTracer.DEVICE_STR, cpu)
77 ftc = FTcat(BINS['ftcat'], stdout_f, stderr_f, dev, SchedTracer.EVENTS, cpu=cpu)
78
79 self.bins.append(ftc)
80
81 @staticmethod
82 def enabled():
83 return litmus_util.is_device("%s%d" % (SchedTracer.DEVICE_STR, 0))
84
85
86class OverheadTracer(Tracer):
87 DEVICE_STR = '/dev/litmus/ft_trace0'
88 EVENTS = [# 'SCHED_START', 'SCHED_END', 'SCHED2_START', 'SCHED2_END',
89 'RELEASE_START', 'RELEASE_END',
90 'LVLA_RELEASE_START', 'LVLA_RELEASE_END',
91 'LVLA_SCHED_START', 'LVLA_SCHED_END',
92 'LVLB_RELEASE_START', 'LVLB_RELEASE_END',
93 'LVLB_SCHED_START', 'LVLB_SCHED_END',
94 'LVLC_RELEASE_START', 'LVLC_RELEASE_END',
95 'LVLC_SCHED_START', 'LVLC_SCHED_END']
96
97 def __init__(self, output_dir):
98 super(OverheadTracer, self).__init__("Overhead Trace", output_dir)
99
100 stdout_f = open('{0}/{1}'.format(self.output_dir, FILES['ft_data']), 'w')
101 stderr_f = open('{0}/{1}.stderr.txt'.format(self.output_dir, FILES['ft_data']), 'w')
102 ftc = FTcat(BINS['ftcat'], stdout_f, stderr_f,
103 OverheadTracer.DEVICE_STR, OverheadTracer.EVENTS)
104
105 self.bins.append(ftc)
106
107 @staticmethod
108 def enabled():
109 return litmus_util.is_device(OverheadTracer.DEVICE_STR)
110
111
112class PerfTracer(Tracer):
113 def __init__(self, output_dir):
114 super(PerfTracer, self).__init__("CPU perf counters", output_dir)
115
116 @staticmethod
117 def enabled():
118 return False
diff --git a/run_exps.py b/run_exps.py
index e69de29..c589f51 100644..100755
--- a/run_exps.py
+++ b/run_exps.py
@@ -0,0 +1,192 @@
1#!/usr/bin/env python
2from __future__ import print_function
3
4import config.config as conf
5import experiment.litmus_util as lu
6import os
7import re
8
9from collections import defaultdict
10from optparse import OptionParser
11from experiment.executable.executable import Executable
12from experiment.experiment import Experiment
13from experiment.proc_entry import ProcEntry
14
15
16def parse_args():
17 parser = OptionParser();
18
19 parser.add_option('-s', '--scheduler', dest='scheduler',
20 help='scheduler for all experiments')
21 parser.add_option('-d', '--duration', dest='duration', type='int',
22 help='duration (seconds) of tasks')
23 parser.add_option('-o', '--out-dir', dest='out_dir',
24 help='directory for data output', default=os.getcwd())
25 parser.add_option('-p', '--params', dest='param_file',
26 help='file with experiment parameters')
27 parser.add_option('-f', '--schedule-file', dest='sched_file',
28 help='name of schedule files',
29 default=conf.DEFAULTS['sched_file'])
30
31 return parser.parse_args()
32
33
34def convert_data(data):
35 """Convert a non-python schedule file into the python format"""
36 regex = re.compile(
37
38 r"(?P<PROC>^"
39 r"(?P<HEADER>/proc/\w+?/)?"
40 r"(?P<ENTRY>[\w\/]+)"
41 r"\s*{\s*(?P<CONTENT>.*?)\s*?}$)|"
42 r"(?P<SPIN>^(?P<TYPE>\w+?spin)?\s*?"
43 r"(?P<ARGS>\w[\s\w]*?)?\s*?$)",
44 re.S|re.I|re.M)
45
46 procs = []
47 spins = []
48
49 for match in regex.finditer(data):
50 if match.group("PROC"):
51 header = match.group("HEADER") or "/proc/litmus/"
52 loc = "{}{}".format(header, match.group("ENTRY"))
53 proc = (loc, match.group("CONTENT"))
54 procs.append(proc)
55 else:
56 prog = match.group("TYPE") or "rtspin"
57 spin = (prog, match.group("ARGS"))
58 spins.append(spin)
59
60 return {'proc' : procs, 'spin' : spins}
61
62
63def get_dirs(sched_file, out_base_dir):
64 sched_leaf_dir = re.findall(r".*/([\w_-]+)/.*?$", sched_file)[0]
65 sched_full_dir = os.path.split(sched_file)[0]
66
67 work_dir = "%s/tmp" % sched_full_dir
68
69 if sched_full_dir == out_base_dir:
70 out_dir = "%s/data" % sched_full_dir
71 else:
72 # Put it under the base output dir with the same directory name
73 out_dir = "%s/%s" % (out_base_dir, sched_leaf_dir)
74
75 return (work_dir, out_dir)
76
77
78def load_experiment(sched_file, scheduler, duration, param_file, out_base):
79 if not os.path.isfile(sched_file):
80 raise IOError("Cannot find schedule file: %s" % sched_file)
81
82 dirname = os.path.split(sched_file)[0]
83
84 if not scheduler or not duration:
85 param_file = param_file or \
86 "%s/%s" % (dirname, conf.DEFAULTS['params_file'])
87
88 if os.path.isfile(param_file):
89 params = load_params(param_file)
90 scheduler = scheduler or params[conf.PARAMS['sched']]
91 duration = duration or params[conf.PARAMS['dur']]
92
93 duration = duration or conf.DEFAULTS['duration']
94
95 if not scheduler:
96 raise IOError("Parameter scheduler not specified")
97
98 schedule = load_schedule(sched_file)
99 (work_dir, out_dir) = get_dirs(sched_file, out_base)
100
101 run_exp(sched_file, schedule, scheduler, duration, work_dir, out_dir)
102
103
104def load_params(fname):
105 params = defaultdict(int)
106 with open(fname, 'r') as f:
107 data = f.read()
108 try:
109 parsed = eval(data)
110 for k in parsed:
111 params[k] = parsed[k]
112 except Exception as e:
113 raise IOError("Invalid param file: %s\n%s" % (fname, e))
114
115 return params
116
117
118def load_schedule(fname):
119 with open(fname, 'r') as f:
120 data = f.read()
121 try:
122 schedule = eval(data)
123 except:
124 schedule = convert_data(data)
125 return schedule
126
127
128def run_exp(name, schedule, scheduler, duration, work_dir, out_dir):
129 proc_entries = []
130 executables = []
131
132 # Parse values for proc entries
133 for entry_conf in schedule['proc']:
134 path = entry_conf[0]
135 data = entry_conf[1]
136
137 if not os.path.exists(path):
138 raise IOError("Invalid proc path %s: %s" % (path, name))
139
140 proc_entries += [ProcEntry(path, data)]
141
142 # Parse spinners
143 for spin_conf in schedule['spin']:
144 if isinstance(spin_conf, str):
145 # Just a string defaults to default spin
146 (spin, args) = (conf.DEFAULTS['spin'], spin_conf)
147 else:
148 # Otherwise its a pair, the type and the args
149 if len(spin_conf) != 2:
150 raise IOError("Invalid spin conf %s: %s" % (spin_conf, name))
151 (spin, args) = (spin_conf[0], spin_conf[1])
152
153 if not conf.BINS[spin]:
154 raise IndexError("No knowledge of program %s: %s" % (spin, name))
155
156 real_spin = conf.BINS[spin]
157 real_args = ['-w'] + args.split() + [duration]
158
159 if not lu.is_executable(real_spin):
160 raise OSError("Cannot run spin %s: %s" % (real_spin, name))
161
162 executables += [Executable(real_spin, real_args)]
163
164 exp = Experiment(name, scheduler, work_dir, out_dir,
165 proc_entries, executables)
166 exp.run_exp()
167
168
169def main():
170 opts, args = parse_args()
171
172 scheduler = opts.scheduler
173 duration = opts.duration
174 param_file = opts.param_file
175 out_base = os.path.abspath(opts.out_dir)
176
177 args = args or [opts.sched_file]
178
179 for exp in args:
180 path = "%s/%s" % (os.getcwd(), exp)
181
182 if not os.path.exists(path):
183 raise IOError("Invalid experiment: %s" % path)
184
185 if os.path.isdir(exp):
186 path = "%s%s" % (path, opts.sched_file)
187
188 load_experiment(path, scheduler, duration, param_file, out_base)
189
190
191if __name__ == '__main__':
192 main()