1 # Copyright (c) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice shall be included in all
11 # copies or substantial portions of the Software.
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 from tempfile
import TemporaryDirectory
34 import lttng_ivc
.settings
as Settings
35 _logger
= logging
.getLogger("Runtime")
38 @contextlib.contextmanager
39 def get_runtime(runtime_dir
):
40 runtime
= Runtime(runtime_dir
)
47 class Runtime(object):
48 def __init__(self
, runtime_dir
):
50 A dictionary of popen object eg. lttng-sessiond, relayd,
51 anything really. Key is a uuid.
53 self
.__subprocess
= {}
54 self
.__stdout
_stderr
= {}
57 self
.__runtime
_log
= os
.path
.join(runtime_dir
, "log")
58 self
.__runtime
_log
_sub
= os
.path
.join(self
.__runtime
_log
, "subprocess")
61 Path of the copy of lttng_home folder after Runtime.close() is issued. This is
62 to be used for post runtime analysis and mostly debugging on error.
64 self
.__post
_runtime
_lttng
_home
_path
= os
.path
.join(runtime_dir
,
67 self
._runtime
_log
_aggregation
= os
.path
.join(self
.__runtime
_log
, "runtime.log")
69 self
._run
_command
_count
= 0
70 self
._is
_test
_modules
_loaded
= False
72 self
.special_env_variables
= {"LTTNG_UST_DEBUG": "1",
73 "LTTNG_APP_SOCKET_TIMEOUT": "-1",
74 #"LTTNG_UST_REGISTER_TIMEOUT": "-1",
75 "LTTNG_NETWORK_SOCKET_TIMEOUT": "-1"}
77 # Keep a reference on the object to keep it alive. It will close/clean on
79 self
.__lttng
_home
_dir
= TemporaryDirectory(prefix
=Settings
.tmp_object_prefix
)
80 self
.lttng_home
= self
.__lttng
_home
_dir
.name
82 if len(self
.lttng_home
) > 88:
83 raise Exception("TemporaryDirectory for lttng_home is to long. Use a short TMPDIR")
85 os
.makedirs(self
.__runtime
_log
)
86 os
.makedirs(self
.__runtime
_log
_sub
)
88 def add_project(self
, project
):
89 self
.__projects
.append(project
)
91 def remove_project(self
, project
):
92 self
.__projects
.remove(project
)
94 def subprocess_signal(self
, subprocess_uuid
, signal
):
95 self
.__subproces
[subprocess_uuid
].send_signal(signal
)
97 def subprocess_terminate(self
, subprocess_uuid
, timeout
=60, check_return
=True):
98 process
= self
.__subprocess
[subprocess_uuid
]
101 process
.wait(timeout
)
102 except subprocess
.TimeoutExpired
:
104 return self
.subprocess_kill(subprocess_uuid
)
105 stdout
, stderr
= self
.__stdout
_stderr
[subprocess_uuid
]
109 if process
.returncode
!= 0:
110 raise subprocess
.CalledProcessError(process
.returncode
, process
.args
)
113 def subprocess_kill(self
, subprocess_uuid
):
114 process
= self
.__subprocess
[subprocess_uuid
]
117 stdout
, stderr
= self
.__stdout
_stderr
[subprocess_uuid
]
122 def subprocess_wait(self
, subprocess_uuid
, check_return
=True):
123 process
= self
.__subprocess
[subprocess_uuid
]
125 stdout
, stderr
= self
.__stdout
_stderr
[subprocess_uuid
]
129 if process
.returncode
!= 0:
130 raise subprocess
.CalledProcessError(process
.returncode
, process
.args
)
133 def get_subprocess_stdout_path(self
, subprocess_uuid
):
134 stdout
, stderr
= self
.__stdout
_stderr
[subprocess_uuid
]
137 def get_subprocess_stderr_path(self
, subprocess_uuid
):
138 stdout
, stderr
= self
.__stdout
_stderr
[subprocess_uuid
]
141 def spawn_subprocess(self
, command_line
, cwd
=None):
142 args
= shlex
.split(command_line
)
145 if not os
.path
.isdir(self
.lttng_home
):
146 raise Exception("lttng home does not exist")
148 tmp_id
= uuid
.uuid1()
149 out_path
= os
.path
.join(self
.__runtime
_log
_sub
, str(tmp_id
) + ".out")
150 err_path
= os
.path
.join(self
.__runtime
_log
_sub
, str(tmp_id
) + ".err")
152 stdout
= open(out_path
, 'w')
153 stderr
= open(err_path
, 'w')
155 env_path
= os
.path
.join(self
.__runtime
_log
_sub
, str(tmp_id
) + ".env")
156 with
open(env_path
, 'w') as env_out
:
157 pprint
.pprint(env
, stream
=env_out
)
159 p
= subprocess
.Popen(args
, stdout
=stdout
, stderr
=stderr
, env
=env
, cwd
=cwd
)
160 self
.__subprocess
[tmp_id
] = p
161 self
.__stdout
_stderr
[tmp_id
] = (stdout
, stderr
)
162 _logger
.debug("Spawned sub pid: {} args: {} stdout: {} stderr{}".format(p
.pid
, p
.args
, out_path
, err_path
))
165 def run(self
, command_line
, cwd
=None, check_return
=True, ld_preload
="",
166 classpath
="", timeout
=None, ld_debug
=False):
168 Run the command and return a tuple of a (CompletedProcess, stdout_path,
169 stderr_path). The subprocess is already executed and returned. The
170 callecaller is responsible for checking for errors.
172 args
= shlex
.split(command_line
)
176 env
['LD_PRELOAD'] = ld_preload
178 env
['CLASSPATH'] = classpath
180 # ld debugging switch
181 env
["LD_DEBUG"] = "all"
183 tmp_id
= self
._run
_command
_count
184 self
._run
_command
_count
+= 1
186 cmd_map
= os
.path
.join(self
.__runtime
_log
, "cmd.map")
187 with
open(cmd_map
, 'a') as out
:
188 out
.write("{}: {}\n".format(tmp_id
, args
))
190 out_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".out")
191 err_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".err")
192 stdout
= open(out_path
, "w")
193 stderr
= open(err_path
, "w")
195 env_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".env")
196 with
open(env_path
, 'w') as env_out
:
197 for key
, value
in env
.items():
198 env_out
.write('{}={}\n'.format(key
, value
))
200 cp
= subprocess
.run(args
, stdout
=stdout
, stderr
=stderr
, env
=env
,
201 cwd
=cwd
, timeout
=timeout
)
202 _logger
.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id
, cp
.args
, out_path
, err_path
))
204 # Add to the global log file. This can help a little. Leave the other
205 # file available for per-run analysis
206 with
open(self
._runtime
_log
_aggregation
, "a") as log
:
207 with
open(out_path
, "r") as out
:
208 log
.write("Output for command #{} {}\n".format(tmp_id
, command_line
))
209 log
.write("Start >>>>>>>>>>>>>>>>\n")
210 log
.write(out
.read())
211 log
.write("End <<<<<<<<<<<<<<<<\n")
212 with
open(err_path
, "r") as out
:
213 log
.write("Error for command #{} {}\n".format(tmp_id
, command_line
))
214 log
.write("Start >>>>>>>>>>>>>>>>\n")
215 log
.write(out
.read())
216 log
.write("End <<<<<<<<<<<<<<<<\n")
219 cp
.check_returncode()
221 return (cp
, out_path
, err_path
)
223 def get_cppflags(self
):
225 for project
in self
.__projects
:
226 cppflags
.append(project
.get_cppflags())
227 return " ".join(cppflags
)
229 def get_ldflags(self
):
231 for project
in self
.__projects
:
232 ldflags
.append(project
.get_ldflags())
233 return " ".join(ldflags
)
235 def get_ld_library_path(self
):
237 for project
in self
.__projects
:
238 library_path
.append(project
.get_ld_library_path())
239 return ":".join(library_path
)
241 def get_bin_path(self
):
243 for project
in self
.__projects
:
244 path
.append(project
.get_bin_path())
245 return ":".join(path
)
248 env
= os
.environ
.copy()
250 env
["LTTNG_HOME"] = self
.lttng_home
251 env
["LD_BIND_NOW"] = "enabled"
253 env_fetch
= {"CPPFLAGS": (self
.get_cppflags(), " "),
254 "LDFLAGS": (self
.get_ldflags(), " "),
255 "LD_LIBRARY_PATH": (self
.get_ld_library_path(), ":"),
256 "PATH": (self
.get_bin_path(), ":"),
258 for key
, (value
, delimiter
) in env_fetch
.items():
262 env
[key
] = delimiter
.join([value
, tmp_var
])
264 for var
, value
in self
.special_env_variables
.items():
266 # Raise for now since no special cases is known
267 _logger
.warning("% Special var % is already defined",
269 raise Exception("Multiple definition of a special environment variable")
273 for project
in self
.__projects
:
274 for var
, value
in project
.special_env_variables
.items():
276 # Raise for now since no special cases is known
277 _logger
.warning("% Special var % is already defined",
279 raise Exception("Multiple definition of a special environment variable")
284 def load_test_module(self
):
285 # Base directory is provided by env
286 self
.run("modprobe lttng-test")
287 self
._is
_test
_modules
_loaded
= True
289 def unload_test_module(self
, check_return
=True):
290 # Base directory is provided by env
291 if self
._is
_test
_modules
_loaded
:
292 self
.run("modprobe -r lttng-test", check_return
=check_return
)
295 for key
, subp
in self
.__subprocess
.items():
296 self
.subprocess_kill(key
)
298 # Always try to remove test module but do not perform check on return
300 self
.unload_test_module(False)
302 # Hard linking would be nice here but it could be a problem when we use
303 # a tmpdir on another device. Let's consider we have unlimited space.
304 shutil
.copytree(self
.lttng_home
, self
.__post
_runtime
_lttng
_home
_path
,
305 ignore
=shutil
.ignore_patterns(".lttng"))
This page took 0.046946 seconds and 5 git commands to generate.