Fix: ld_library_path uses : as separator
[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
97d494b0 123 def run(self, command_line, cwd=None, check_return=True):
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
132 tmp_id = self._run_command_count
133 self._run_command_count += 1
134
97d494b0
JR
135 cmd_map = os.path.join(self.__runtime_log, "cmd.map")
136 with open(cmd_map, 'a') as out:
137 out.write("{}: {}\n".format(tmp_id, args))
138
18aedaf9
JR
139 out_path = os.path.join(self.__runtime_log, str(tmp_id) + ".out")
140 err_path = os.path.join(self.__runtime_log, str(tmp_id) + ".err")
141 stdout = open(out_path, "w")
142 stderr = open(err_path, "w")
143
97d494b0
JR
144 env_path = os.path.join(self.__runtime_log, str(tmp_id) + ".env")
145 with open(env_path, 'w') as env_out:
146 pprint.pprint(env, stream=env_out)
18aedaf9 147
97d494b0 148 cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
18aedaf9
JR
149 _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path))
150
18aedaf9
JR
151 # Add to the global log file. This can help a little. Leave the other
152 # file available for per-run analysis
153 with open(self._runtime_log_aggregation, "a") as log:
154 with open(out_path, "r") as out:
97d494b0
JR
155 log.write("Output for command #{} {}\n".format(tmp_id, command_line))
156 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 157 log.write(out.read())
97d494b0 158 log.write("End <<<<<<<<<<<<<<<<\n")
18aedaf9 159 with open(err_path, "r") as out:
97d494b0
JR
160 log.write("Error for command #{} {}\n".format(tmp_id, command_line))
161 log.write("Start >>>>>>>>>>>>>>>>\n")
18aedaf9 162 log.write(out.read())
97d494b0
JR
163 log.write("End <<<<<<<<<<<<<<<<\n")
164
165 if check_return:
166 cp.check_returncode()
18aedaf9
JR
167
168 return (cp, out_path, err_path)
169
170 def get_cppflags(self):
171 cppflags = []
172 for project in self.__projects:
173 cppflags.append(project.get_cppflags())
174 return " ".join(cppflags)
175
176 def get_ldflags(self):
177 ldflags = []
178 for project in self.__projects:
179 ldflags.append(project.get_ldflags())
180 return " ".join(ldflags)
181
182 def get_ld_library_path(self):
183 library_path = []
184 for project in self.__projects:
185 library_path.append(project.get_ld_library_path())
6aa98db5 186 return ":".join(library_path)
18aedaf9
JR
187
188 def get_bin_path(self):
189 path = []
190 for project in self.__projects:
191 path.append(project.get_bin_path())
192 return ":".join(path)
193
194 def get_env(self):
195 env = os.environ.copy()
196
197 env["LTTNG_HOME"] = self.lttng_home
198
199 env_fetch = {"CPPFLAGS": (self.get_cppflags(), " "),
200 "LDFLAGS": (self.get_ldflags(), " "),
97d494b0 201 "LD_LIBRARY_PATH": (self.get_ld_library_path(), ":"),
18aedaf9
JR
202 "PATH": (self.get_bin_path(), ":"),
203 }
204 for key, (value, delimiter) in env_fetch.items():
205 tmp_var = ""
206 if key in env:
207 tmp_var = env[key]
208 env[key] = delimiter.join([value, tmp_var])
209
97d494b0
JR
210 for var, value in self.special_env_variables.items():
211 if var in env:
212 # Raise for now since no special cases is known
213 _logger.warning("% Special var % is already defined",
214 self.label, var)
215 raise Exception("Multiple definition of a special environment variable")
216 else:
217 env[var] = value
218
18aedaf9
JR
219 for project in self.__projects:
220 for var, value in project.special_env_variables.items():
221 if var in env:
222 # Raise for now since no special cases is known
223 _logger.warning("% Special var % is already defined",
224 self.label, var)
225 raise Exception("Multiple definition of a special environment variable")
226 else:
227 env[var] = value
228 return env
229
230 def close(self):
231 for key, subp in self.__subprocess.items():
232 subp.terminate()
233 for key, subp in self.__subprocess.items():
97d494b0
JR
234 # TODO move timeout to settings
235 subp.wait(timeout=60)
18aedaf9
JR
236 for key, (stdout, stderr) in self.__stdout_stderr.items():
237 stdout.close()
238 stderr.close()
97d494b0
JR
239
240 # Copy the lttng_home used at runtime using hardlink to prevent useless
241 # data duplication
242 shutil.copytree(self.lttng_home, self.__post_runtime_lttng_home_path, copy_function=os.link)
243
This page took 0.032854 seconds and 5 git commands to generate.