Licensing information
[deliverable/lttng-ivc.git] / lttng_ivc / utils / runtime.py
index c2d0b8397299b5f0453bcb2550873e108aee28ea..6c9f04c04e0deb77c3a8df035113b6d9fbf8b7b3 100644 (file)
@@ -1,3 +1,23 @@
+# Copyright (c) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
 import os
 import sys
 import shlex
@@ -7,7 +27,7 @@ import logging
 import shutil
 import contextlib
 import pprint
-import traceback
+import signal
 
 from tempfile import TemporaryDirectory
 
@@ -47,9 +67,10 @@ class Runtime(object):
         self._runtime_log_aggregation = os.path.join(self.__runtime_log, "runtime.log")
 
         self._run_command_count = 0
+        self._is_test_modules_loaded = False
 
         self.special_env_variables = {"LTTNG_UST_DEBUG": "1",
-                                      #"LTTNG_APP_SOCKET_TIMEOUT": "-1",
+                                      "LTTNG_APP_SOCKET_TIMEOUT": "-1",
                                       #"LTTNG_UST_REGISTER_TIMEOUT": "-1",
                                       "LTTNG_NETWORK_SOCKET_TIMEOUT": "-1"}
 
@@ -67,16 +88,26 @@ class Runtime(object):
     def add_project(self, project):
         self.__projects.append(project)
 
+    def remove_project(self, project):
+        self.__projects.remove(project)
+
     def subprocess_signal(self, subprocess_uuid, signal):
         self.__subproces[subprocess_uuid].send_signal(signal)
 
-    def subprocess_terminate(self, subprocess_uuid, timeout=60):
+    def subprocess_terminate(self, subprocess_uuid, timeout=60, check_return=True):
         process = self.__subprocess[subprocess_uuid]
         process.terminate()
-        process.wait(timeout)
+        try:
+            process.wait(timeout)
+        except subprocess.TimeoutExpired:
+            # Force kill
+            return self.subprocess_kill(subprocess_uuid)
         stdout, stderr = self.__stdout_stderr[subprocess_uuid]
         stdout.close()
         stderr.close()
+        if check_return:
+            if process.returncode != 0:
+                raise subprocess.CalledProcessError(process.returncode, process.args)
         return process
 
     def subprocess_kill(self, subprocess_uuid):
@@ -88,6 +119,17 @@ class Runtime(object):
         stderr.close()
         return process
 
+    def subprocess_wait(self, subprocess_uuid, check_return=True):
+        process = self.__subprocess[subprocess_uuid]
+        process.wait()
+        stdout, stderr = self.__stdout_stderr[subprocess_uuid]
+        stdout.close()
+        stderr.close()
+        if check_return:
+            if process.returncode != 0:
+                raise subprocess.CalledProcessError(process.returncode, process.args)
+        return process
+
     def get_subprocess_stdout_path(self, subprocess_uuid):
         stdout, stderr = self.__stdout_stderr[subprocess_uuid]
         return stdout.name
@@ -114,13 +156,13 @@ class Runtime(object):
         with open(env_path, 'w') as env_out:
             pprint.pprint(env, stream=env_out)
 
-        p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env)
+        p = subprocess.Popen(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
         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, cwd=None, check_return=True):
+    def run(self, command_line, cwd=None, check_return=True, ld_preload="", classpath="", timeout=None):
         """
         Run the command and return a tuple of a (CompletedProcess, stdout_path,
         stderr_path). The subprocess is already executed and returned. The
@@ -129,6 +171,12 @@ class Runtime(object):
         args = shlex.split(command_line)
         env = self.get_env()
 
+        if ld_preload:
+            env['LD_PRELOAD'] = ld_preload
+        if classpath:
+            env['CLASSPATH'] = classpath
+
+
         tmp_id = self._run_command_count
         self._run_command_count += 1
 
@@ -143,9 +191,11 @@ class Runtime(object):
 
         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)
+            for key, value in env.items():
+                env_out.write('{}={}\n'.format(key, value))
 
-        cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env, cwd=cwd)
+        cp = subprocess.run(args, stdout=stdout, stderr=stderr, env=env,
+                            cwd=cwd, timeout=timeout)
         _logger.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id, cp.args, out_path, err_path))
 
         # Add to the global log file. This can help a little. Leave the other
@@ -183,7 +233,7 @@ class Runtime(object):
         library_path = []
         for project in self.__projects:
             library_path.append(project.get_ld_library_path())
-        return " ".join(library_path)
+        return ":".join(library_path)
 
     def get_bin_path(self):
         path = []
@@ -227,17 +277,24 @@ class Runtime(object):
                     env[var] = value
         return env
 
+    def load_test_module(self):
+        # Base directory is provided by env
+        self.run("modprobe lttng-test")
+        self._is_test_modules_loaded = True
+
+    def unload_test_module(self, check_return=True):
+        # Base directory is provided by env
+        if self._is_test_modules_loaded:
+            self.run("modprobe -r lttng-test", check_return=check_return)
+
     def close(self):
         for key, subp in self.__subprocess.items():
-            subp.terminate()
-        for key, subp in self.__subprocess.items():
-            # TODO move timeout to settings
-            subp.wait(timeout=60)
-        for key, (stdout, stderr) in self.__stdout_stderr.items():
-            stdout.close()
-            stderr.close()
+            self.subprocess_kill(key)
+
+        # Always try to remove test module but do not perform check on return
+        # value.
+        self.unload_test_module(False)
 
         # 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)
-
This page took 0.026546 seconds and 5 git commands to generate.