716617608c50879f98b6a9ea80146e7ed3a8f1e1
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
="", classpath
="", timeout
=None):
167 Run the command and return a tuple of a (CompletedProcess, stdout_path,
168 stderr_path). The subprocess is already executed and returned. The
169 callecaller is responsible for checking for errors.
171 args
= shlex
.split(command_line
)
175 env
['LD_PRELOAD'] = ld_preload
177 env
['CLASSPATH'] = classpath
180 tmp_id
= self
._run
_command
_count
181 self
._run
_command
_count
+= 1
183 cmd_map
= os
.path
.join(self
.__runtime
_log
, "cmd.map")
184 with
open(cmd_map
, 'a') as out
:
185 out
.write("{}: {}\n".format(tmp_id
, args
))
187 out_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".out")
188 err_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".err")
189 stdout
= open(out_path
, "w")
190 stderr
= open(err_path
, "w")
192 env_path
= os
.path
.join(self
.__runtime
_log
, str(tmp_id
) + ".env")
193 with
open(env_path
, 'w') as env_out
:
194 for key
, value
in env
.items():
195 env_out
.write('{}={}\n'.format(key
, value
))
197 cp
= subprocess
.run(args
, stdout
=stdout
, stderr
=stderr
, env
=env
,
198 cwd
=cwd
, timeout
=timeout
)
199 _logger
.debug("Command #{} args: {} stdout: {} stderr{}".format(tmp_id
, cp
.args
, out_path
, err_path
))
201 # Add to the global log file. This can help a little. Leave the other
202 # file available for per-run analysis
203 with
open(self
._runtime
_log
_aggregation
, "a") as log
:
204 with
open(out_path
, "r") as out
:
205 log
.write("Output for command #{} {}\n".format(tmp_id
, command_line
))
206 log
.write("Start >>>>>>>>>>>>>>>>\n")
207 log
.write(out
.read())
208 log
.write("End <<<<<<<<<<<<<<<<\n")
209 with
open(err_path
, "r") as out
:
210 log
.write("Error for command #{} {}\n".format(tmp_id
, command_line
))
211 log
.write("Start >>>>>>>>>>>>>>>>\n")
212 log
.write(out
.read())
213 log
.write("End <<<<<<<<<<<<<<<<\n")
216 cp
.check_returncode()
218 return (cp
, out_path
, err_path
)
220 def get_cppflags(self
):
222 for project
in self
.__projects
:
223 cppflags
.append(project
.get_cppflags())
224 return " ".join(cppflags
)
226 def get_ldflags(self
):
228 for project
in self
.__projects
:
229 ldflags
.append(project
.get_ldflags())
230 return " ".join(ldflags
)
232 def get_ld_library_path(self
):
234 for project
in self
.__projects
:
235 library_path
.append(project
.get_ld_library_path())
236 return ":".join(library_path
)
238 def get_bin_path(self
):
240 for project
in self
.__projects
:
241 path
.append(project
.get_bin_path())
242 return ":".join(path
)
245 env
= os
.environ
.copy()
247 env
["LTTNG_HOME"] = self
.lttng_home
249 env_fetch
= {"CPPFLAGS": (self
.get_cppflags(), " "),
250 "LDFLAGS": (self
.get_ldflags(), " "),
251 "LD_LIBRARY_PATH": (self
.get_ld_library_path(), ":"),
252 "PATH": (self
.get_bin_path(), ":"),
254 for key
, (value
, delimiter
) in env_fetch
.items():
258 env
[key
] = delimiter
.join([value
, tmp_var
])
260 for var
, value
in self
.special_env_variables
.items():
262 # Raise for now since no special cases is known
263 _logger
.warning("% Special var % is already defined",
265 raise Exception("Multiple definition of a special environment variable")
269 for project
in self
.__projects
:
270 for var
, value
in project
.special_env_variables
.items():
272 # Raise for now since no special cases is known
273 _logger
.warning("% Special var % is already defined",
275 raise Exception("Multiple definition of a special environment variable")
280 def load_test_module(self
):
281 # Base directory is provided by env
282 self
.run("modprobe lttng-test")
283 self
._is
_test
_modules
_loaded
= True
285 def unload_test_module(self
, check_return
=True):
286 # Base directory is provided by env
287 if self
._is
_test
_modules
_loaded
:
288 self
.run("modprobe -r lttng-test", check_return
=check_return
)
291 for key
, subp
in self
.__subprocess
.items():
292 self
.subprocess_kill(key
)
294 # Always try to remove test module but do not perform check on return
296 self
.unload_test_module(False)
298 # Hard linking would be nice here but it could be a problem when we use
299 # a tmpdir on another device. Let's consider we have unlimited space.
300 shutil
.copytree(self
.lttng_home
, self
.__post
_runtime
_lttng
_home
_path
,
301 ignore
=shutil
.ignore_patterns(".lttng"))
This page took 0.039469 seconds and 6 git commands to generate.