Do not use for now since a regression in userspace master deadlock java agent
[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 51 self.special_env_variables = {"LTTNG_UST_DEBUG": "1",
d96b684b 52 "LTTNG_APP_SOCKET_TIMEOUT": "-1",
97d494b0
JR
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 116
bde0c540 117 p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
18aedaf9
JR
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
74eb7096 123 def run(self, command_line, cwd=None, check_return=True, ld_preload="", classpath=""):
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
74eb7096
JR
134 if classpath:
135 env['CLASSPATH'] = classpath
9f2ed047
JR
136
137
18aedaf9
JR
138 tmp_id = self._run_command_count
139 self._run_command_count += 1
140
97d494b0
JR
141 cmd_map = os.path.join(self.__runtime_log, "cmd.map")
142 with open(cmd_map, 'a') as out:
143 out.write("{}: {}\n".format(tmp_id, args))
144
18aedaf9
JR
145 out_path = os.path.join(self.__runtime_log, str(tmp_id) + ".out")
146 err_path = os.path.join(self.__runtime_log, str(tmp_id) + ".err")
147 stdout = open(out_path, "w")
148 stderr = open(err_path, "w")
149
97d494b0
JR
150 env_path = os.path.join(self.__runtime_log, str(tmp_id) + ".env")
151 with open(env_path, 'w') as env_out:
0a7e963a
JR
152 for key, value in env.items():
153 env_out.write('{}={}\n'.format(key, value))
18aedaf9 154
97d494b0 155 cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
18aedaf9
JR
156 _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path))
157
18aedaf9
JR
158 # Add to the global log file. This can help a little. Leave the other
159 # file available for per-run analysis
160 with open(self._runtime_log_aggregation, "a") as log:
161 with open(out_path, "r") as out:
97d494b0
JR
162 log.write("Output for command #{} {}\n".format(tmp_id, command_line))
163 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 164 log.write(out.read())
97d494b0 165 log.write("End <<<<<<<<<<<<<<<<\n")
18aedaf9 166 with open(err_path, "r") as out:
97d494b0
JR
167 log.write("Error for command #{} {}\n".format(tmp_id, command_line))
168 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 169 log.write(out.read())
97d494b0
JR
170 log.write("End <<<<<<<<<<<<<<<<\n")
171
172 if check_return:
173 cp.check_returncode()
18aedaf9
JR
174
175 return (cp, out_path, err_path)
176
177 def get_cppflags(self):
178 cppflags = []
179 for project in self.__projects:
180 cppflags.append(project.get_cppflags())
181 return " ".join(cppflags)
182
183 def get_ldflags(self):
184 ldflags = []
185 for project in self.__projects:
186 ldflags.append(project.get_ldflags())
187 return " ".join(ldflags)
188
189 def get_ld_library_path(self):
190 library_path = []
191 for project in self.__projects:
192 library_path.append(project.get_ld_library_path())
6aa98db5 193 return ":".join(library_path)
18aedaf9
JR
194
195 def get_bin_path(self):
196 path = []
197 for project in self.__projects:
198 path.append(project.get_bin_path())
199 return ":".join(path)
200
201 def get_env(self):
202 env = os.environ.copy()
203
204 env["LTTNG_HOME"] = self.lttng_home
205
206 env_fetch = {"CPPFLAGS": (self.get_cppflags(), " "),
207 "LDFLAGS": (self.get_ldflags(), " "),
97d494b0 208 "LD_LIBRARY_PATH": (self.get_ld_library_path(), ":"),
18aedaf9
JR
209 "PATH": (self.get_bin_path(), ":"),
210 }
211 for key, (value, delimiter) in env_fetch.items():
212 tmp_var = ""
213 if key in env:
214 tmp_var = env[key]
215 env[key] = delimiter.join([value, tmp_var])
216
97d494b0
JR
217 for var, value in self.special_env_variables.items():
218 if var in env:
219 # Raise for now since no special cases is known
220 _logger.warning("% Special var % is already defined",
221 self.label, var)
222 raise Exception("Multiple definition of a special environment variable")
223 else:
224 env[var] = value
225
18aedaf9
JR
226 for project in self.__projects:
227 for var, value in project.special_env_variables.items():
228 if var in env:
229 # Raise for now since no special cases is known
230 _logger.warning("% Special var % is already defined",
231 self.label, var)
232 raise Exception("Multiple definition of a special environment variable")
233 else:
234 env[var] = value
235 return env
236
ab63b97e
JR
237 def load_test_module(self):
238 # Base directory is provided by env
239 self.run("modprobe lttng-test")
240
241 def unload_test_module(self, check_return=True):
242 # Base directory is provided by env
243 self.run("modprobe -r lttng-test", check_return=check_return)
244
18aedaf9
JR
245 def close(self):
246 for key, subp in self.__subprocess.items():
247 subp.terminate()
248 for key, subp in self.__subprocess.items():
97d494b0
JR
249 # TODO move timeout to settings
250 subp.wait(timeout=60)
18aedaf9
JR
251 for key, (stdout, stderr) in self.__stdout_stderr.items():
252 stdout.close()
253 stderr.close()
97d494b0 254
ab63b97e
JR
255 # Always try to remove test module but do not perform check on return
256 # value.
257 self.unload_test_module(False)
258
97d494b0
JR
259 # Copy the lttng_home used at runtime using hardlink to prevent useless
260 # data duplication
261 shutil.copytree(self.lttng_home, self.__post_runtime_lttng_home_path, copy_function=os.link)
262
This page took 0.033823 seconds and 5 git commands to generate.