Implement part of test_modules_abi_vs_tools
[deliverable/lttng-ivc.git] / lttng_ivc / utils / runtime.py
CommitLineData
18aedaf9 1import os
97d494b0 2import sys
18aedaf9
JR
3import shlex
4import subprocess
5import uuid
6import logging
97d494b0
JR
7import shutil
8import contextlib
9import pprint
10import traceback
18aedaf9 11
97d494b0
JR
12from tempfile import TemporaryDirectory
13
14import lttng_ivc.settings as Settings
18aedaf9
JR
15_logger = logging.getLogger("Runtime")
16
17
97d494b0
JR
18@contextlib.contextmanager
19def get_runtime(runtime_dir):
20 runtime = Runtime(runtime_dir)
21 try:
22 yield runtime
23 finally:
24 runtime.close()
25
26
18aedaf9
JR
27class Runtime(object):
28 def __init__(self, runtime_dir):
29 """
30 A dictionary of popen object eg. lttng-sessiond, relayd,
31 anything really. Key is a uuid.
32 """
33 self.__subprocess = {}
34 self.__stdout_stderr = {}
35 self.__projects = []
36
37 self.__runtime_log = os.path.join(runtime_dir, "log")
38 self.__runtime_log_sub = os.path.join(self.__runtime_log, "subprocess")
39
97d494b0
JR
40 """
41 Path of the copy of lttng_home folder after Runtime.close() is issued. This is
42 to be used for post runtime analysis and mostly debugging on error.
43 """
44 self.__post_runtime_lttng_home_path = os.path.join(runtime_dir,
45 "lttng_home")
46
18aedaf9
JR
47 self._runtime_log_aggregation = os.path.join(self.__runtime_log, "runtime.log")
48
49 self._run_command_count = 0
50
97d494b0
JR
51 self.special_env_variables = {"LTTNG_UST_DEBUG": "1",
52 #"LTTNG_APP_SOCKET_TIMEOUT": "-1",
53 #"LTTNG_UST_REGISTER_TIMEOUT": "-1",
54 "LTTNG_NETWORK_SOCKET_TIMEOUT": "-1"}
18aedaf9 55
97d494b0
JR
56 # Keep a reference on the object to keep it alive. It will close/clean on
57 # exit.
58 self.__lttng_home_dir = TemporaryDirectory(prefix=Settings.tmp_object_prefix)
59 self.lttng_home = self.__lttng_home_dir.name
60
61 if len(self.lttng_home) > 88:
62 raise Exception("TemporaryDirectory for lttng_home is to long. Use a short TMPDIR")
63
64 os.makedirs(self.__runtime_log)
65 os.makedirs(self.__runtime_log_sub)
18aedaf9
JR
66
67 def add_project(self, project):
68 self.__projects.append(project)
69
70 def subprocess_signal(self, subprocess_uuid, signal):
71 self.__subproces[subprocess_uuid].send_signal(signal)
72
73 def subprocess_terminate(self, subprocess_uuid, timeout=60):
74 process = self.__subprocess[subprocess_uuid]
75 process.terminate()
76 process.wait(timeout)
77 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
78 stdout.close()
79 stderr.close()
97d494b0 80 return process
18aedaf9
JR
81
82 def subprocess_kill(self, subprocess_uuid):
83 process = self.__subprocess[subprocess_uuid]
84 process.kill()
85 process.wait()
86 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
87 stdout.close()
88 stderr.close()
97d494b0 89 return process
18aedaf9
JR
90
91 def get_subprocess_stdout_path(self, subprocess_uuid):
92 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
93 return stdout.name
94
95 def get_subprocess_stderr_path(self, subprocess_uuid):
96 stdout, stderr = self.__stdout_stderr[subprocess_uuid]
97 return stderr.name
98
97d494b0 99 def spawn_subprocess(self, command_line, cwd=None):
18aedaf9
JR
100 args = shlex.split(command_line)
101 env = self.get_env()
102
97d494b0
JR
103 if not os.path.isdir(self.lttng_home):
104 raise Exception("lttng home does not exist")
105
18aedaf9
JR
106 tmp_id = uuid.uuid1()
107 out_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".out")
108 err_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".err")
97d494b0
JR
109
110 stdout = open(out_path, 'w')
111 stderr = open(err_path, 'w')
112
113 env_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".env")
114 with open(env_path, 'w') as env_out:
115 pprint.pprint(env, stream=env_out)
18aedaf9
JR
116
117 p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env)
118 self.__subprocess[tmp_id] = p
119 self.__stdout_stderr[tmp_id] = (stdout, stderr)
120 _logger.debug("Spawned sub pid: {} args: {} stdout: {} stderr{}".format(p.pid, p.args, out_path, err_path))
97d494b0 121 return tmp_id
18aedaf9 122
9f2ed047 123 def run(self, command_line, cwd=None, check_return=True, ld_preload=""):
18aedaf9
JR
124 """
125 Run the command and return a tuple of a (CompletedProcess, stdout_path,
126 stderr_path). The subprocess is already executed and returned. The
127 callecaller is responsible for checking for errors.
128 """
129 args = shlex.split(command_line)
130 env = self.get_env()
131
9f2ed047
JR
132 if ld_preload:
133 env['LD_PRELOAD'] = ld_preload
134
135
18aedaf9
JR
136 tmp_id = self._run_command_count
137 self._run_command_count += 1
138
97d494b0
JR
139 cmd_map = os.path.join(self.__runtime_log, "cmd.map")
140 with open(cmd_map, 'a') as out:
141 out.write("{}: {}\n".format(tmp_id, args))
142
18aedaf9
JR
143 out_path = os.path.join(self.__runtime_log, str(tmp_id) + ".out")
144 err_path = os.path.join(self.__runtime_log, str(tmp_id) + ".err")
145 stdout = open(out_path, "w")
146 stderr = open(err_path, "w")
147
97d494b0
JR
148 env_path = os.path.join(self.__runtime_log, str(tmp_id) + ".env")
149 with open(env_path, 'w') as env_out:
0a7e963a
JR
150 for key, value in env.items():
151 env_out.write('{}={}\n'.format(key, value))
18aedaf9 152
97d494b0 153 cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
18aedaf9
JR
154 _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path))
155
18aedaf9
JR
156 # Add to the global log file. This can help a little. Leave the other
157 # file available for per-run analysis
158 with open(self._runtime_log_aggregation, "a") as log:
159 with open(out_path, "r") as out:
97d494b0
JR
160 log.write("Output for command #{} {}\n".format(tmp_id, command_line))
161 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 162 log.write(out.read())
97d494b0 163 log.write("End <<<<<<<<<<<<<<<<\n")
18aedaf9 164 with open(err_path, "r") as out:
97d494b0
JR
165 log.write("Error for command #{} {}\n".format(tmp_id, command_line))
166 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 167 log.write(out.read())
97d494b0
JR
168 log.write("End <<<<<<<<<<<<<<<<\n")
169
170 if check_return:
171 cp.check_returncode()
18aedaf9
JR
172
173 return (cp, out_path, err_path)
174
175 def get_cppflags(self):
176 cppflags = []
177 for project in self.__projects:
178 cppflags.append(project.get_cppflags())
179 return " ".join(cppflags)
180
181 def get_ldflags(self):
182 ldflags = []
183 for project in self.__projects:
184 ldflags.append(project.get_ldflags())
185 return " ".join(ldflags)
186
187 def get_ld_library_path(self):
188 library_path = []
189 for project in self.__projects:
190 library_path.append(project.get_ld_library_path())
6aa98db5 191 return ":".join(library_path)
18aedaf9
JR
192
193 def get_bin_path(self):
194 path = []
195 for project in self.__projects:
196 path.append(project.get_bin_path())
197 return ":".join(path)
198
199 def get_env(self):
200 env = os.environ.copy()
201
202 env["LTTNG_HOME"] = self.lttng_home
203
204 env_fetch = {"CPPFLAGS": (self.get_cppflags(), " "),
205 "LDFLAGS": (self.get_ldflags(), " "),
97d494b0 206 "LD_LIBRARY_PATH": (self.get_ld_library_path(), ":"),
18aedaf9
JR
207 "PATH": (self.get_bin_path(), ":"),
208 }
209 for key, (value, delimiter) in env_fetch.items():
210 tmp_var = ""
211 if key in env:
212 tmp_var = env[key]
213 env[key] = delimiter.join([value, tmp_var])
214
97d494b0
JR
215 for var, value in self.special_env_variables.items():
216 if var in env:
217 # Raise for now since no special cases is known
218 _logger.warning("% Special var % is already defined",
219 self.label, var)
220 raise Exception("Multiple definition of a special environment variable")
221 else:
222 env[var] = value
223
18aedaf9
JR
224 for project in self.__projects:
225 for var, value in project.special_env_variables.items():
226 if var in env:
227 # Raise for now since no special cases is known
228 _logger.warning("% Special var % is already defined",
229 self.label, var)
230 raise Exception("Multiple definition of a special environment variable")
231 else:
232 env[var] = value
233 return env
234
ab63b97e
JR
235 def load_test_module(self):
236 # Base directory is provided by env
237 self.run("modprobe lttng-test")
238
239 def unload_test_module(self, check_return=True):
240 # Base directory is provided by env
241 self.run("modprobe -r lttng-test", check_return=check_return)
242
18aedaf9
JR
243 def close(self):
244 for key, subp in self.__subprocess.items():
245 subp.terminate()
246 for key, subp in self.__subprocess.items():
97d494b0
JR
247 # TODO move timeout to settings
248 subp.wait(timeout=60)
18aedaf9
JR
249 for key, (stdout, stderr) in self.__stdout_stderr.items():
250 stdout.close()
251 stderr.close()
97d494b0 252
ab63b97e
JR
253 # Always try to remove test module but do not perform check on return
254 # value.
255 self.unload_test_module(False)
256
97d494b0
JR
257 # Copy the lttng_home used at runtime using hardlink to prevent useless
258 # data duplication
259 shutil.copytree(self.lttng_home, self.__post_runtime_lttng_home_path, copy_function=os.link)
260
This page took 0.034696 seconds and 5 git commands to generate.