aboutsummaryrefslogtreecommitdiffstats
path: root/run
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2012-11-26 17:06:27 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2012-11-26 17:06:27 -0500
commitb43b83beead92ff7cf28a5fe5a2710537268aae1 (patch)
treed9c29b14cd18a9df520f36d7e85eb460c30fa7a9 /run
parentcb8db5d30ee769304c2c2b00f2a7d9bcb3c4098f (diff)
Read locations of binary files from path instead of config.py.
Diffstat (limited to 'run')
-rw-r--r--run/__init__.py0
-rw-r--r--run/executable/__init__.py0
-rw-r--r--run/executable/executable.py77
-rw-r--r--run/executable/ftcat.py33
-rw-r--r--run/experiment.py209
-rw-r--r--run/litmus_util.py79
-rw-r--r--run/proc_entry.py12
-rw-r--r--run/tracer.py112
8 files changed, 522 insertions, 0 deletions
diff --git a/run/__init__.py b/run/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/run/__init__.py
diff --git a/run/executable/__init__.py b/run/executable/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/run/executable/__init__.py
diff --git a/run/executable/executable.py b/run/executable/executable.py
new file mode 100644
index 0000000..628f711
--- /dev/null
+++ b/run/executable/executable.py
@@ -0,0 +1,77 @@
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 __str__(self):
47 return " ".join(self.__get_full_command())
48
49 def execute(self):
50 '''Execute the binary.'''
51 full_command = self.__get_full_command()
52 self.sp = subprocess.Popen(full_command, stdout=self.stdout_file,
53 stderr=self.stderr_file, cwd=self.cwd)
54
55 def kill(self):
56 self.sp.kill()
57
58 def interrupt(self):
59 self.sp.send_signal(signal.SIGINT)
60
61 def terminate(self):
62 '''Send the terminate signal to the binary.'''
63 self.sp.terminate()
64
65 def wait(self):
66 '''Wait until the executable is finished, checking return code.
67
68 If the exit status is non-zero, raise an exception.
69
70 '''
71
72 self.sp.wait()
73 if self.sp.returncode != 0:
74 print >>sys.stderr, "Non-zero return: %s %s" % (self.exec_file, " ".join(self.extra_args))
75 return 0
76 else:
77 return 1
diff --git a/run/executable/ftcat.py b/run/executable/ftcat.py
new file mode 100644
index 0000000..5da8fa7
--- /dev/null
+++ b/run/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/run/experiment.py b/run/experiment.py
new file mode 100644
index 0000000..4bd47c6
--- /dev/null
+++ b/run/experiment.py
@@ -0,0 +1,209 @@
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 INTERRUPTED_DIR = ".interrupted"
33
34 def __init__(self, name, scheduler, working_dir, finished_dir, proc_entries, executables):
35 '''Run an experiment, optionally wrapped in tracing.'''
36
37 self.name = name
38 self.scheduler = scheduler
39 self.working_dir = working_dir
40 self.finished_dir = finished_dir
41 self.proc_entries = proc_entries
42 self.executables = executables
43 self.exec_out = None
44 self.exec_err = None
45
46 self.__make_dirs()
47 self.__assign_executable_cwds()
48
49 self.tracers = []
50 if SchedTracer.enabled():
51 self.log("Enabling sched_trace")
52 self.tracers.append( SchedTracer(working_dir) )
53 if LinuxTracer.enabled():
54 self.log("Enabling trace-cmd / ftrace / kernelshark")
55 self.tracers.append( LinuxTracer(working_dir) )
56 if LogTracer.enabled():
57 self.log("Enabling logging")
58 self.tracers.append( LogTracer(working_dir) )
59 if PerfTracer.enabled():
60 self.log("Tracking CPU performance counters")
61 self.tracers.append( PerfTracer(working_dir) )
62
63 # Overhead trace must be handled seperately, see __run_tasks
64 if OverheadTracer.enabled():
65 self.log("Enabling overhead tracing")
66 self.overhead_trace = OverheadTracer(working_dir)
67 else:
68 self.overhead_trace = None
69
70 def __make_dirs(self):
71 interrupted = None
72
73 if os.path.exists(self.finished_dir):
74 raise ExperimentDone(self.name)
75
76 if os.path.exists(self.working_dir):
77 self.log("Found interrupted experiment, saving in %s" %
78 Experiment.INTERRUPTED_DIR)
79 interrupted = "%s/%s" % (os.path.split(self.working_dir)[0],
80 Experiment.INTERRUPTED_DIR)
81 os.rename(self.working_dir, interrupted)
82
83 os.mkdir(self.working_dir)
84
85 if interrupted:
86 os.rename(interrupted, "%s/%s" % (self.working_dir,
87 os.path.split(interrupted)[1]))
88
89 def __assign_executable_cwds(self):
90 def assign_cwd(executable):
91 executable.cwd = self.working_dir
92 map(assign_cwd, self.executables)
93
94 def __run_tasks(self):
95 exec_pause = 0.3
96 self.log("Starting the programs over ({0} seconds)".format(
97 len(self.executables) * exec_pause))
98 for e in self.executables:
99 try:
100 e.execute()
101 except:
102 raise Exception("Executable failed: %s" % e)
103 time.sleep(exec_pause)
104
105 sleep_time = len(self.executables) / litmus_util.num_cpus()
106 self.log("Sleeping for %d seconds before release" % sleep_time)
107 time.sleep(sleep_time)
108
109 # Overhead tracer must be started right after release or overhead
110 # measurements will be full of irrelevant records
111 if self.overhead_trace:
112 self.log("Starting overhead trace")
113 self.overhead_trace.start_tracing()
114
115 self.log("Releasing %d tasks" % len(self.executables))
116 released = litmus_util.release_tasks()
117
118 ret = True
119 if released != len(self.executables):
120 # Some tasks failed to release, kill all tasks and fail
121 # Need to re-release non-released tasks before we can kill them though
122 self.log("Failed to release {} tasks! Re-releasing and killing".format(
123 len(self.executables) - released, len(self.executables)))
124
125 time.sleep(5)
126
127 released = litmus_util.release_tasks()
128
129 self.log("Re-released %d tasks" % released)
130
131 time.sleep(5)
132
133 self.log("Killing all tasks")
134 map(methodcaller('kill'), self.executables)
135
136 ret = False
137
138 self.log("Waiting for program to finish...")
139 for e in self.executables:
140 if not e.wait():
141 ret = False
142
143 # And it must be stopped here for the same reason
144 if self.overhead_trace:
145 self.log("Stopping overhead trace")
146 self.overhead_trace.stop_tracing()
147
148 if not ret:
149 raise ExperimentFailed(self.name)
150
151 def __save_results(self):
152 os.rename(self.working_dir, self.finished_dir)
153
154 def log(self, msg):
155 print "[Exp %s]: %s" % (self.name, msg)
156
157 def run_exp(self):
158 succ = False
159 try:
160 self.setup()
161
162 try:
163 self.__run_tasks()
164 self.log("Saving results in %s" % self.finished_dir)
165 succ = True
166 finally:
167 self.teardown()
168 finally:
169 self.log("Switching to Linux scheduler")
170 litmus_util.switch_scheduler("Linux")
171
172 if succ:
173 self.__save_results()
174 self.log("Experiment done!")
175
176
177 def setup(self):
178 self.log("Writing %d proc entries" % len(self.proc_entries))
179 map(methodcaller('write_proc'), self.proc_entries)
180
181 if len(self.proc_entries):
182 time.sleep(2)
183
184 self.log("Switching to %s" % self.scheduler)
185 litmus_util.switch_scheduler(self.scheduler)
186
187 self.log("Starting %d tracers" % len(self.tracers))
188 map(methodcaller('start_tracing'), self.tracers)
189
190 self.exec_out = open('%s/exec-out.txt' % self.working_dir, 'w')
191 self.exec_err = open('%s/exec-err.txt' % self.working_dir, 'w')
192 def set_out(executable):
193 executable.stdout_file = self.exec_out
194 executable.stderr_file = self.exec_err
195 map(set_out, self.executables)
196
197 time.sleep(4)
198
199 def teardown(self):
200 self.exec_out and self.exec_out.close()
201 self.exec_err and self.exec_err.close()
202
203 sleep_time = 10
204 self.log("Sleeping %d seconds to allow buffer flushing" % sleep_time)
205 time.sleep(sleep_time)
206
207 self.log("Stopping tracers")
208 map(methodcaller('stop_tracing'), self.tracers)
209
diff --git a/run/litmus_util.py b/run/litmus_util.py
new file mode 100644
index 0000000..fb2b341
--- /dev/null
+++ b/run/litmus_util.py
@@ -0,0 +1,79 @@
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 cpu_freq():
21 '''
22 The frequency (in MHz) of the CPU.
23 '''
24 reg = re.compile(r'^cpu MHz\s*:\s*(\d+)', re.M)
25 with open('/proc/cpuinfo', 'r') as f:
26 data = f.read()
27
28 match = re.search(reg, data)
29 if not match:
30 raise Exception("Cannot parse CPU frequency!")
31 return int(match.group(1))
32
33def switch_scheduler(switch_to_in):
34 '''Switch the scheduler to whatever is passed in.
35
36 This methods sleeps for two seconds to give Linux the chance to execute
37 schedule switching code. Raises an exception if the switch does not work.
38
39 '''
40
41 switch_to = str(switch_to_in).strip()
42
43 with open('/proc/litmus/active_plugin', 'w') as active_plugin:
44 subprocess.Popen(["echo", switch_to], stdout=active_plugin)
45
46 # it takes a bit to do the switch, sleep an arbitrary amount of time
47 time.sleep(2)
48
49 with open('/proc/litmus/active_plugin', 'r') as active_plugin:
50 cur_plugin = active_plugin.read().strip()
51
52 if switch_to != cur_plugin:
53 raise Exception("Could not switch to plugin: %s" % switch_to)
54
55def uname_matches(reg):
56 data = subprocess.check_output(["uname", "-r"])
57 return bool( re.match(reg, data) )
58
59def is_executable(fname):
60 '''Return whether the file passed in is executable'''
61 mode = os.stat(fname)[stat.ST_MODE]
62 return mode & stat.S_IXUSR and mode & stat.S_IRUSR
63
64def is_device(dev):
65 if not os.path.exists(dev):
66 return False
67 mode = os.stat(dev)[stat.ST_MODE]
68 return not (not mode & stat.S_IFCHR)
69
70def release_tasks():
71
72 try:
73 data = subprocess.check_output([conf.BINS['release']])
74 except subprocess.CalledProcessError:
75 raise Exception('Something went wrong in release_ts')
76
77 released = re.findall(r"([0-9]+) real-time", data)[0]
78
79 return int(released)
diff --git a/run/proc_entry.py b/run/proc_entry.py
new file mode 100644
index 0000000..0b7f9ce
--- /dev/null
+++ b/run/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/run/tracer.py b/run/tracer.py
new file mode 100644
index 0000000..5d00e86
--- /dev/null
+++ b/run/tracer.py
@@ -0,0 +1,112 @@
1import litmus_util
2import os
3import config.config as conf
4
5from operator import methodcaller
6from executable.ftcat import FTcat,Executable
7
8
9class Tracer(object):
10 def __init__(self, name, output_dir):
11 self.name = name
12 self.output_dir = output_dir
13 self.bins = []
14
15 def start_tracing(self):
16 map(methodcaller("execute"), self.bins)
17
18 def stop_tracing(self):
19 map(methodcaller('terminate'), self.bins)
20 map(methodcaller('wait'), self.bins)
21
22
23class LinuxTracer(Tracer):
24 EVENT_ROOT = "/sys/kernel/debug/tracing"
25 LITMUS_EVENTS = "%s/events/litmus" % EVENT_ROOT
26
27 def __init__(self, output_dir):
28 super(LinuxTracer, self).__init__("trace-cmd", output_dir)
29
30 extra_args = ["record", # "-e", "sched:sched_switch",
31 "-e", "litmus:*",
32 "-o", "%s/%s" % (output_dir, conf.FILES['linux_data'])]
33 stdout = open('%s/trace-cmd-stdout.txt' % self.output_dir, 'w')
34 stderr = open('%s/trace-cmd-stderr.txt' % self.output_dir, 'w')
35
36 execute = Executable(conf.BINS['trace-cmd'], extra_args, stdout, stderr)
37 execute.cwd = output_dir
38 self.bins.append(execute)
39
40 @staticmethod
41 def enabled():
42 return os.path.exists(LinuxTracer.LITMUS_EVENTS)
43
44 def stop_tracing(self):
45 map(methodcaller('interrupt'), self.bins)
46 map(methodcaller('wait'), self.bins)
47
48
49class LogTracer(Tracer):
50 DEVICE_STR = '/dev/litmus/log'
51
52 def __init__(self, output_dir):
53 super(LogTracer, self).__init__("Logger", output_dir)
54
55 out_file = open("%s/%s" % (self.output_dir, conf.FILES['log_data']), 'w')
56
57 cat = (Executable("/bin/cat", [LogTracer.DEVICE_STR]))
58 cat.stdout_file = out_file
59
60 self.bins.append(cat)
61
62 @staticmethod
63 def enabled():
64 return litmus_util.is_device(LogTracer.DEVICE_STR)
65
66
67class SchedTracer(Tracer):
68 DEVICE_STR = '/dev/litmus/sched_trace'
69
70 def __init__(self, output_dir):
71 super(SchedTracer, self).__init__("Sched Trace", output_dir)
72
73 if SchedTracer.enabled():
74 for cpu in range(litmus_util.num_cpus()):
75 # Executable will close the stdout/stderr files
76 stdout_f = open('%s/st-%d.bin' % (self.output_dir, cpu), 'w')
77 stderr_f = open('%s/st-%d-stderr.txt' % (self.output_dir, cpu), 'w')
78 dev = '{0}{1}'.format(SchedTracer.DEVICE_STR, cpu)
79 ftc = FTcat(conf.BINS['ftcat'], stdout_f, stderr_f, dev, conf.SCHED_EVENTS, cpu=cpu)
80
81 self.bins.append(ftc)
82
83 @staticmethod
84 def enabled():
85 return litmus_util.is_device("%s%d" % (SchedTracer.DEVICE_STR, 0))
86
87
88class OverheadTracer(Tracer):
89 DEVICE_STR = '/dev/litmus/ft_trace0'
90
91 def __init__(self, output_dir):
92 super(OverheadTracer, self).__init__("Overhead Trace", output_dir)
93
94 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')
96 ftc = FTcat(conf.BINS['ftcat'], stdout_f, stderr_f,
97 OverheadTracer.DEVICE_STR, conf.OVH_ALL_EVENTS)
98
99 self.bins.append(ftc)
100
101 @staticmethod
102 def enabled():
103 return litmus_util.is_device(OverheadTracer.DEVICE_STR)
104
105
106class PerfTracer(Tracer):
107 def __init__(self, output_dir):
108 super(PerfTracer, self).__init__("CPU perf counters", output_dir)
109
110 @staticmethod
111 def enabled():
112 return False