X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=lttng_ivc%2Futils%2Fruntime.py;fp=lttng_ivc%2Futils%2Fruntime.py;h=c2d0b8397299b5f0453bcb2550873e108aee28ea;hb=97d494b06731f7e39d113e0696af1066bc159363;hp=1bd8cbf6dbe1abb1c790d8b62ff83b1f0036bbb9;hpb=18aedaf96b66a383f46487b7203bc35fc2280900;p=deliverable%2Flttng-ivc.git diff --git a/lttng_ivc/utils/runtime.py b/lttng_ivc/utils/runtime.py index 1bd8cbf..c2d0b83 100644 --- a/lttng_ivc/utils/runtime.py +++ b/lttng_ivc/utils/runtime.py @@ -1,12 +1,29 @@ import os +import sys import shlex import subprocess import uuid import logging +import shutil +import contextlib +import pprint +import traceback +from tempfile import TemporaryDirectory + +import lttng_ivc.settings as Settings _logger = logging.getLogger("Runtime") +@contextlib.contextmanager +def get_runtime(runtime_dir): + runtime = Runtime(runtime_dir) + try: + yield runtime + finally: + runtime.close() + + class Runtime(object): def __init__(self, runtime_dir): """ @@ -20,16 +37,32 @@ class Runtime(object): self.__runtime_log = os.path.join(runtime_dir, "log") self.__runtime_log_sub = os.path.join(self.__runtime_log, "subprocess") + """ + Path of the copy of lttng_home folder after Runtime.close() is issued. This is + to be used for post runtime analysis and mostly debugging on error. + """ + self.__post_runtime_lttng_home_path = os.path.join(runtime_dir, + "lttng_home") + self._runtime_log_aggregation = os.path.join(self.__runtime_log, "runtime.log") self._run_command_count = 0 - self.lttng_home = os.path.join(runtime_dir, "lttng_home") + self.special_env_variables = {"LTTNG_UST_DEBUG": "1", + #"LTTNG_APP_SOCKET_TIMEOUT": "-1", + #"LTTNG_UST_REGISTER_TIMEOUT": "-1", + "LTTNG_NETWORK_SOCKET_TIMEOUT": "-1"} - # TODO move exist_ok to false !!!! ONLY for testing - os.makedirs(self.__runtime_log, exist_ok=True) - os.makedirs(self.__runtime_log_sub, exist_ok=True) - os.makedirs(self.lttng_home, exist_ok=True) + # Keep a reference on the object to keep it alive. It will close/clean on + # exit. + self.__lttng_home_dir = TemporaryDirectory(prefix=Settings.tmp_object_prefix) + self.lttng_home = self.__lttng_home_dir.name + + if len(self.lttng_home) > 88: + raise Exception("TemporaryDirectory for lttng_home is to long. Use a short TMPDIR") + + os.makedirs(self.__runtime_log) + os.makedirs(self.__runtime_log_sub) def add_project(self, project): self.__projects.append(project) @@ -44,6 +77,7 @@ class Runtime(object): stdout, stderr = self.__stdout_stderr[subprocess_uuid] stdout.close() stderr.close() + return process def subprocess_kill(self, subprocess_uuid): process = self.__subprocess[subprocess_uuid] @@ -52,6 +86,7 @@ class Runtime(object): stdout, stderr = self.__stdout_stderr[subprocess_uuid] stdout.close() stderr.close() + return process def get_subprocess_stdout_path(self, subprocess_uuid): stdout, stderr = self.__stdout_stderr[subprocess_uuid] @@ -61,22 +96,31 @@ class Runtime(object): stdout, stderr = self.__stdout_stderr[subprocess_uuid] return stderr.name - def spawn_subprocess(self, command_line): + def spawn_subprocess(self, command_line, cwd=None): args = shlex.split(command_line) env = self.get_env() + if not os.path.isdir(self.lttng_home): + raise Exception("lttng home does not exist") + tmp_id = uuid.uuid1() out_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".out") err_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".err") - stdout = open(out_path, "w") - stderr = open(err_path, "w") + + stdout = open(out_path, 'w') + stderr = open(err_path, 'w') + + env_path = os.path.join(self.__runtime_log_sub, str(tmp_id) + ".env") + with open(env_path, 'w') as env_out: + pprint.pprint(env, stream=env_out) p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env) self.__subprocess[tmp_id] = p self.__stdout_stderr[tmp_id] = (stdout, stderr) _logger.debug("Spawned sub pid: {} args: {} stdout: {} stderr{}".format(p.pid, p.args, out_path, err_path)) + return tmp_id - def run(self, command_line): + def run(self, command_line, cwd=None, check_return=True): """ Run the command and return a tuple of a (CompletedProcess, stdout_path, stderr_path). The subprocess is already executed and returned. The @@ -88,35 +132,38 @@ class Runtime(object): tmp_id = self._run_command_count self._run_command_count += 1 + cmd_map = os.path.join(self.__runtime_log, "cmd.map") + with open(cmd_map, 'a') as out: + out.write("{}: {}\n".format(tmp_id, args)) + out_path = os.path.join(self.__runtime_log, str(tmp_id) + ".out") err_path = os.path.join(self.__runtime_log, str(tmp_id) + ".err") stdout = open(out_path, "w") stderr = open(err_path, "w") - stdout.write("Output for command #{} {}\n".format(tmp_id, command_line)) - stdout.write("Start >>>>>>>>>>>>>>>>\n") - stdout.flush() - - stderr.write("Output for command #{} {}\n".format(tmp_id, command_line)) - stderr.write("Start >>>>>>>>>>>>>>>>\n") - stderr.flush() + env_path = os.path.join(self.__runtime_log, str(tmp_id) + ".env") + with open(env_path, 'w') as env_out: + pprint.pprint(env, stream=env_out) - cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env) + cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd) _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path)) - stdout.write("End <<<<<<<<<<<<<<<<\n") - stdout.close() - - stderr.write("End <<<<<<<<<<<<<<<<\n") - stderr.close() - # Add to the global log file. This can help a little. Leave the other # file available for per-run analysis with open(self._runtime_log_aggregation, "a") as log: with open(out_path, "r") as out: + log.write("Output for command #{} {}\n".format(tmp_id, command_line)) + log.write("Start >>>>>>>>>>>>>>>>\n") log.write(out.read()) + log.write("End <<<<<<<<<<<<<<<<\n") with open(err_path, "r") as out: + log.write("Error for command #{} {}\n".format(tmp_id, command_line)) + log.write("Start >>>>>>>>>>>>>>>>\n") log.write(out.read()) + log.write("End <<<<<<<<<<<<<<<<\n") + + if check_return: + cp.check_returncode() return (cp, out_path, err_path) @@ -151,7 +198,7 @@ class Runtime(object): env_fetch = {"CPPFLAGS": (self.get_cppflags(), " "), "LDFLAGS": (self.get_ldflags(), " "), - "LD_LIRABRY_PATH": (self.get_ld_library_path(), ":"), + "LD_LIBRARY_PATH": (self.get_ld_library_path(), ":"), "PATH": (self.get_bin_path(), ":"), } for key, (value, delimiter) in env_fetch.items(): @@ -160,6 +207,15 @@ class Runtime(object): tmp_var = env[key] env[key] = delimiter.join([value, tmp_var]) + for var, value in self.special_env_variables.items(): + if var in env: + # Raise for now since no special cases is known + _logger.warning("% Special var % is already defined", + self.label, var) + raise Exception("Multiple definition of a special environment variable") + else: + env[var] = value + for project in self.__projects: for var, value in project.special_env_variables.items(): if var in env: @@ -175,13 +231,13 @@ class Runtime(object): for key, subp in self.__subprocess.items(): subp.terminate() for key, subp in self.__subprocess.items(): - try: - # TODO move timeout to settings - subp.wait(timeout=60) - except subprocess.TimeoutExpired as e: - # Force a little bit - subp.kill() - subp.wait() + # TODO move timeout to settings + subp.wait(timeout=60) for key, (stdout, stderr) in self.__stdout_stderr.items(): stdout.close() stderr.close() + + # Copy the lttng_home used at runtime using hardlink to prevent useless + # data duplication + shutil.copytree(self.lttng_home, self.__post_runtime_lttng_home_path, copy_function=os.link) +