blob: e04afa062100d3d957257773e3bc22369d389cca [file] [log] [blame]
Chris McDonaldc3e0f26b2020-06-04 02:15:141#!/usr/bin/env python
[email protected]0a88a652012-03-09 00:34:452# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Sets environment variables needed to run a chromium unit test."""
7
Chris McDonaldc3e0f26b2020-06-04 02:15:148from __future__ import print_function
Trent Aptedb1852432018-01-25 02:15:109import io
[email protected]0a88a652012-03-09 00:34:4510import os
Ben Pastenee5ece812018-06-20 22:39:3411import signal
[email protected]0a88a652012-03-09 00:34:4512import subprocess
13import sys
Trent Aptedb1852432018-01-25 02:15:1014import time
[email protected]0a88a652012-03-09 00:34:4515
Chris McDonaldc3e0f26b2020-06-04 02:15:1416
[email protected]0a88a652012-03-09 00:34:4517# This is hardcoded to be src/ relative to this script.
18ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
19
[email protected]76361be452012-08-30 22:48:1420CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
21CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
22
23
earthdok748e1352015-01-27 23:04:0824def get_sandbox_env(env):
25 """Returns the environment flags needed for the SUID sandbox to work."""
John Abd-El-Malek14ae0542014-10-15 17:52:3126 extra_env = {}
[email protected]76361be452012-08-30 22:48:1427 chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH)
earthdok748e1352015-01-27 23:04:0828 # The above would silently disable the SUID sandbox if the env value were
29 # an empty string. We don't want to allow that. http://crbug.com/245376
30 # TODO(jln): Remove this check once it's no longer possible to disable the
31 # sandbox that way.
32 if not chrome_sandbox_path:
33 chrome_sandbox_path = CHROME_SANDBOX_PATH
34 extra_env[CHROME_SANDBOX_ENV] = chrome_sandbox_path
John Abd-El-Malek14ae0542014-10-15 17:52:3135
36 return extra_env
37
38
39def trim_cmd(cmd):
40 """Removes internal flags from cmd since they're just used to communicate from
41 the host machine to this script running on the swarm slaves."""
earthdokeeb065302015-02-04 18:18:0442 sanitizers = ['asan', 'lsan', 'msan', 'tsan']
43 internal_flags = frozenset('--%s=%d' % (name, value)
44 for name in sanitizers
45 for value in [0, 1])
John Abd-El-Malek14ae0542014-10-15 17:52:3146 return [i for i in cmd if i not in internal_flags]
[email protected]76361be452012-08-30 22:48:1447
[email protected]0a88a652012-03-09 00:34:4548
[email protected]4bf4d632012-05-31 15:50:3049def fix_python_path(cmd):
50 """Returns the fixed command line to call the right python executable."""
51 out = cmd[:]
52 if out[0] == 'python':
53 out[0] = sys.executable
54 elif out[0].endswith('.py'):
55 out.insert(0, sys.executable)
56 return out
57
58
Nico Weber9417be62021-07-14 23:56:0759def get_sanitizer_env(asan, lsan, msan, tsan, cfi_diag):
60 """Returns the environment flags needed for sanitizer tools."""
John Abd-El-Malek14ae0542014-10-15 17:52:3161
62 extra_env = {}
63
earthdokeeb065302015-02-04 18:18:0464 # Instruct GTK to use malloc while running sanitizer-instrumented tests.
earthdoke6c54a42014-10-16 18:02:3665 extra_env['G_SLICE'] = 'always-malloc'
John Abd-El-Malek14ae0542014-10-15 17:52:3166
67 extra_env['NSS_DISABLE_ARENA_FREE_LIST'] = '1'
68 extra_env['NSS_DISABLE_UNLOAD'] = '1'
69
70 # TODO(glider): remove the symbolizer path once
71 # https://code.google.com/p/address-sanitizer/issues/detail?id=134 is fixed.
Nico Weber19097312016-01-29 18:13:1572 symbolizer_path = os.path.join(ROOT_DIR,
73 'third_party', 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer')
John Abd-El-Malek14ae0542014-10-15 17:52:3174
earthdokeeb065302015-02-04 18:18:0475 if lsan or tsan:
John Abd-El-Malek14ae0542014-10-15 17:52:3176 # LSan is not sandbox-compatible, so we can use online symbolization. In
77 # fact, it needs symbolization to be able to apply suppressions.
78 symbolization_options = ['symbolize=1',
79 'external_symbolizer_path=%s' % symbolizer_path]
pcc46233c22017-06-20 22:11:4180 elif (asan or msan or cfi_diag) and sys.platform not in ['win32', 'cygwin']:
timurrrr064f9522015-02-12 15:46:3281 # ASan uses a script for offline symbolization, except on Windows.
John Abd-El-Malek14ae0542014-10-15 17:52:3182 # Important note: when running ASan with leak detection enabled, we must use
83 # the LSan symbolization options above.
84 symbolization_options = ['symbolize=0']
vadimsh5cf3c442014-10-20 10:22:3885 # Set the path to llvm-symbolizer to be used by asan_symbolize.py
86 extra_env['LLVM_SYMBOLIZER_PATH'] = symbolizer_path
timurrrr7e67df92015-02-12 21:50:5287 else:
88 symbolization_options = []
John Abd-El-Malek14ae0542014-10-15 17:52:3189
Yves Gerey2e221522019-03-13 08:46:1990 # Leverage sanitizer to print stack trace on abort (e.g. assertion failure).
91 symbolization_options.append('handle_abort=1')
92
earthdokeeb065302015-02-04 18:18:0493 if asan:
94 asan_options = symbolization_options[:]
95 if lsan:
96 asan_options.append('detect_leaks=1')
Ben Pastene536f57c2021-05-17 23:21:5697 # LSan appears to have trouble with later versions of glibc.
98 # See https://github.com/google/sanitizers/issues/1322
99 if 'linux' in sys.platform:
100 asan_options.append('intercept_tls_get_addr=0')
John Abd-El-Malek14ae0542014-10-15 17:52:31101
timurrrr7e67df92015-02-12 21:50:52102 if asan_options:
103 extra_env['ASAN_OPTIONS'] = ' '.join(asan_options)
John Abd-El-Malek14ae0542014-10-15 17:52:31104
earthdokeeb065302015-02-04 18:18:04105 if lsan:
106 if asan or msan:
107 lsan_options = []
108 else:
109 lsan_options = symbolization_options[:]
110 if sys.platform == 'linux2':
111 # Use the debug version of libstdc++ under LSan. If we don't, there will
112 # be a lot of incomplete stack traces in the reports.
113 extra_env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:'
114
earthdokeeb065302015-02-04 18:18:04115 extra_env['LSAN_OPTIONS'] = ' '.join(lsan_options)
116
117 if msan:
118 msan_options = symbolization_options[:]
119 if lsan:
120 msan_options.append('detect_leaks=1')
121 extra_env['MSAN_OPTIONS'] = ' '.join(msan_options)
122
123 if tsan:
124 tsan_options = symbolization_options[:]
125 extra_env['TSAN_OPTIONS'] = ' '.join(tsan_options)
John Abd-El-Malek14ae0542014-10-15 17:52:31126
pcc46233c22017-06-20 22:11:41127 # CFI uses the UBSan runtime to provide diagnostics.
128 if cfi_diag:
129 ubsan_options = symbolization_options[:] + ['print_stacktrace=1']
130 extra_env['UBSAN_OPTIONS'] = ' '.join(ubsan_options)
131
John Abd-El-Malek14ae0542014-10-15 17:52:31132 return extra_env
133
134
glider9d919342015-02-06 17:42:27135def get_sanitizer_symbolize_command(json_path=None, executable_path=None):
earthdok9bd5d582015-01-29 21:19:36136 """Construct the command to invoke offline symbolization script."""
Nico Weber19097312016-01-29 18:13:15137 script_path = os.path.join(
138 ROOT_DIR, 'tools', 'valgrind', 'asan', 'asan_symbolize.py')
earthdok9bd5d582015-01-29 21:19:36139 cmd = [sys.executable, script_path]
140 if json_path is not None:
141 cmd.append('--test-summary-json-file=%s' % json_path)
glider9d919342015-02-06 17:42:27142 if executable_path is not None:
143 cmd.append('--executable-path=%s' % executable_path)
earthdok9bd5d582015-01-29 21:19:36144 return cmd
145
146
147def get_json_path(cmd):
148 """Extract the JSON test summary path from a command line."""
149 json_path_flag = '--test-launcher-summary-output='
150 for arg in cmd:
151 if arg.startswith(json_path_flag):
152 return arg.split(json_path_flag).pop()
153 return None
154
155
156def symbolize_snippets_in_json(cmd, env):
157 """Symbolize output snippets inside the JSON test summary."""
158 json_path = get_json_path(cmd)
159 if json_path is None:
160 return
161
162 try:
glider9d919342015-02-06 17:42:27163 symbolize_command = get_sanitizer_symbolize_command(
164 json_path=json_path, executable_path=cmd[0])
earthdok9bd5d582015-01-29 21:19:36165 p = subprocess.Popen(symbolize_command, stderr=subprocess.PIPE, env=env)
166 (_, stderr) = p.communicate()
167 except OSError as e:
Chris McDonaldc3e0f26b2020-06-04 02:15:14168 print('Exception while symbolizing snippets: %s' % e, file=sys.stderr)
thakis45643b42016-01-29 21:53:42169 raise
earthdok9bd5d582015-01-29 21:19:36170
171 if p.returncode != 0:
Chris McDonaldc3e0f26b2020-06-04 02:15:14172 print("Error: failed to symbolize snippets in JSON:\n", file=sys.stderr)
173 print(stderr, file=sys.stderr)
thakis45643b42016-01-29 21:53:42174 raise subprocess.CalledProcessError(p.returncode, symbolize_command)
earthdok9bd5d582015-01-29 21:19:36175
176
Trent Aptedb1852432018-01-25 02:15:10177def run_command_with_output(argv, stdoutfile, env=None, cwd=None):
Caleb Rouleaufd511292019-02-22 18:22:00178 """Run command and stream its stdout/stderr to the console & |stdoutfile|.
179
180 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32181 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleaufd511292019-02-22 18:22:00182
183 Returns:
184 integer returncode of the subprocess.
Trent Aptedb1852432018-01-25 02:15:10185 """
Sorin Jianue11c9852019-09-12 20:43:55186 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Trent Aptedb1852432018-01-25 02:15:10187 assert stdoutfile
Ned Nguyen7f43ff62018-10-04 04:58:46188 with io.open(stdoutfile, 'wb') as writer, \
189 io.open(stdoutfile, 'rb', 1) as reader:
Caleb Rouleau6844df152019-09-11 01:11:59190 process = _popen(argv, env=env, cwd=cwd, stdout=writer,
191 stderr=subprocess.STDOUT)
Ben Pastenee5ece812018-06-20 22:39:34192 forward_signals([process])
Trent Aptedb1852432018-01-25 02:15:10193 while process.poll() is None:
194 sys.stdout.write(reader.read())
Caleb Rouleaufd511292019-02-22 18:22:00195 # This sleep is needed for signal propagation. See the
196 # wait_with_signals() docstring.
Trent Aptedb1852432018-01-25 02:15:10197 time.sleep(0.1)
198 # Read the remaining.
199 sys.stdout.write(reader.read())
Sorin Jianue11c9852019-09-12 20:43:55200 print('Command %r returned exit code %d' % (argv, process.returncode))
Trent Aptedb1852432018-01-25 02:15:10201 return process.returncode
202
203
Caleb Rouleaufd511292019-02-22 18:22:00204def run_command(argv, env=None, cwd=None, log=True):
205 """Run command and stream its stdout/stderr both to stdout.
206
207 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32208 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleaufd511292019-02-22 18:22:00209
210 Returns:
211 integer returncode of the subprocess.
212 """
213 if log:
Sorin Jianue11c9852019-09-12 20:43:55214 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Caleb Rouleau6844df152019-09-11 01:11:59215 process = _popen(argv, env=env, cwd=cwd, stderr=subprocess.STDOUT)
Caleb Rouleaufd511292019-02-22 18:22:00216 forward_signals([process])
217 return wait_with_signals(process)
218
219
Caleb Rouleau84e3e812019-05-30 23:34:50220def run_command_output_to_handle(argv, file_handle, env=None, cwd=None):
221 """Run command and stream its stdout/stderr both to |file_handle|.
222
223 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32224 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleau84e3e812019-05-30 23:34:50225
226 Returns:
227 integer returncode of the subprocess.
228 """
Sorin Jianue11c9852019-09-12 20:43:55229 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Caleb Rouleau6844df152019-09-11 01:11:59230 process = _popen(
Caleb Rouleau84e3e812019-05-30 23:34:50231 argv, env=env, cwd=cwd, stderr=file_handle, stdout=file_handle)
232 forward_signals([process])
233 exit_code = wait_with_signals(process)
Sorin Jianue11c9852019-09-12 20:43:55234 print('Command returned exit code %d' % exit_code)
Caleb Rouleau84e3e812019-05-30 23:34:50235 return exit_code
236
237
Caleb Rouleaufd511292019-02-22 18:22:00238def wait_with_signals(process):
239 """A version of process.wait() that works cross-platform.
240
241 This version properly surfaces the SIGBREAK signal.
242
243 From reading the subprocess.py source code, it seems we need to explicitly
244 call time.sleep(). The reason is that subprocess.Popen.wait() on Windows
245 directly calls WaitForSingleObject(), but only time.sleep() properly surface
246 the SIGBREAK signal.
247
248 Refs:
249 https://github.com/python/cpython/blob/v2.7.15/Lib/subprocess.py#L692
250 https://github.com/python/cpython/blob/v2.7.15/Modules/timemodule.c#L1084
251
252 Returns:
253 returncode of the process.
254 """
255 while process.poll() is None:
256 time.sleep(0.1)
257 return process.returncode
258
259
Ben Pastenee5ece812018-06-20 22:39:34260def forward_signals(procs):
261 """Forwards unix's SIGTERM or win's CTRL_BREAK_EVENT to the given processes.
262
263 This plays nicely with swarming's timeout handling. See also
John Palmera8515fca2021-05-20 03:35:32264 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Ben Pastenee5ece812018-06-20 22:39:34265
266 Args:
267 procs: A list of subprocess.Popen objects representing child processes.
268 """
269 assert all(isinstance(p, subprocess.Popen) for p in procs)
270 def _sig_handler(sig, _):
271 for p in procs:
Ilia Samsonova00835302019-04-19 17:37:59272 if p.poll() is not None:
273 continue
Andrew Grieve95a2d332018-06-28 02:22:59274 # SIGBREAK is defined only for win32.
275 if sys.platform == 'win32' and sig == signal.SIGBREAK:
Ben Pastenee5ece812018-06-20 22:39:34276 p.send_signal(signal.CTRL_BREAK_EVENT)
277 else:
278 p.send_signal(sig)
279 if sys.platform == 'win32':
280 signal.signal(signal.SIGBREAK, _sig_handler)
281 else:
282 signal.signal(signal.SIGTERM, _sig_handler)
Ilia Samsonova00835302019-04-19 17:37:59283 signal.signal(signal.SIGINT, _sig_handler)
Ben Pastenee5ece812018-06-20 22:39:34284
Haiyang Pan94c26592019-08-13 17:48:11285
Trent Aptedb1852432018-01-25 02:15:10286def run_executable(cmd, env, stdoutfile=None):
[email protected]0a88a652012-03-09 00:34:45287 """Runs an executable with:
Dirk Pranke50e557b2017-12-01 23:48:09288 - CHROME_HEADLESS set to indicate that the test is running on a
289 bot and shouldn't do anything interactive like show modal dialogs.
[email protected]3766ed1c2012-07-26 20:53:56290 - environment variable CR_SOURCE_ROOT set to the root directory.
[email protected]0a88a652012-03-09 00:34:45291 - environment variable LANGUAGE to en_US.UTF-8.
earthdok748e1352015-01-27 23:04:08292 - environment variable CHROME_DEVEL_SANDBOX set
[email protected]0a88a652012-03-09 00:34:45293 - Reuses sys.executable automatically.
294 """
Dirk Pranke50e557b2017-12-01 23:48:09295 extra_env = {
296 # Set to indicate that the executable is running non-interactively on
297 # a bot.
298 'CHROME_HEADLESS': '1',
299
300 # Many tests assume a English interface...
301 'LANG': 'en_US.UTF-8',
302 }
303
[email protected]3766ed1c2012-07-26 20:53:56304 # Used by base/base_paths_linux.cc as an override. Just make sure the default
305 # logic is used.
306 env.pop('CR_SOURCE_ROOT', None)
earthdok748e1352015-01-27 23:04:08307 extra_env.update(get_sandbox_env(env))
John Abd-El-Malek4569c422014-10-09 05:10:53308
309 # Copy logic from tools/build/scripts/slave/runtest.py.
310 asan = '--asan=1' in cmd
311 lsan = '--lsan=1' in cmd
earthdokeeb065302015-02-04 18:18:04312 msan = '--msan=1' in cmd
313 tsan = '--tsan=1' in cmd
pcc46233c22017-06-20 22:11:41314 cfi_diag = '--cfi-diag=1' in cmd
Trent Aptedb1852432018-01-25 02:15:10315 if stdoutfile or sys.platform in ['win32', 'cygwin']:
timurrrr064f9522015-02-12 15:46:32316 # Symbolization works in-process on Windows even when sandboxed.
317 use_symbolization_script = False
318 else:
Ilia Samsonov4ea016762019-12-02 22:11:13319 # If any sanitizer is enabled, we print unsymbolized stack trace
320 # that is required to run through symbolization script.
321 use_symbolization_script = (asan or msan or cfi_diag or lsan or tsan)
John Abd-El-Malek4569c422014-10-09 05:10:53322
pcc46233c22017-06-20 22:11:41323 if asan or lsan or msan or tsan or cfi_diag:
Nico Weber9417be62021-07-14 23:56:07324 extra_env.update(get_sanitizer_env(asan, lsan, msan, tsan, cfi_diag))
earthdokeeb065302015-02-04 18:18:04325
Timur Iskhodzhanovfdbfd4e12015-02-05 13:50:23326 if lsan or tsan:
327 # LSan and TSan are not sandbox-friendly.
328 cmd.append('--no-sandbox')
John Abd-El-Malek14ae0542014-10-15 17:52:31329
330 cmd = trim_cmd(cmd)
John Abd-El-Malek4569c422014-10-09 05:10:53331
[email protected]8ba98352012-05-23 20:43:59332 # Ensure paths are correctly separated on windows.
333 cmd[0] = cmd[0].replace('/', os.path.sep)
[email protected]4bf4d632012-05-31 15:50:30334 cmd = fix_python_path(cmd)
John Abd-El-Malek14ae0542014-10-15 17:52:31335
Dirk Pranke2cf1a152019-04-03 01:34:38336 # We also want to print the GTEST env vars that were set by the caller,
337 # because you need them to reproduce the task properly.
338 env_to_print = extra_env.copy()
339 for env_var_name in ('GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'):
Chris McDonaldc3e0f26b2020-06-04 02:15:14340 if env_var_name in env:
341 env_to_print[env_var_name] = env[env_var_name]
Dirk Pranke2cf1a152019-04-03 01:34:38342
Sorin Jianue11c9852019-09-12 20:43:55343 print('Additional test environment:\n%s\n'
John Abd-El-Malek14ae0542014-10-15 17:52:31344 'Command: %s\n' % (
Dirk Pranke2b9bec72020-11-18 04:33:38345 '\n'.join(' %s=%s' % (k, v)
346 for k, v in sorted(env_to_print.items())),
Sorin Jianue11c9852019-09-12 20:43:55347 ' '.join(cmd)))
maruelf00125f82016-11-19 00:01:14348 sys.stdout.flush()
John Abd-El-Malek14ae0542014-10-15 17:52:31349 env.update(extra_env or {})
[email protected]50ec9f232012-03-16 04:18:23350 try:
Trent Aptedb1852432018-01-25 02:15:10351 if stdoutfile:
352 # Write to stdoutfile and poll to produce terminal output.
353 return run_command_with_output(cmd, env=env, stdoutfile=stdoutfile)
354 elif use_symbolization_script:
355 # See above comment regarding offline symbolization.
John Abd-El-Malek4569c422014-10-09 05:10:53356 # Need to pipe to the symbolizer script.
Caleb Rouleau6844df152019-09-11 01:11:59357 p1 = _popen(cmd, env=env, stdout=subprocess.PIPE,
358 stderr=sys.stdout)
359 p2 = _popen(
glider9d919342015-02-06 17:42:27360 get_sanitizer_symbolize_command(executable_path=cmd[0]),
361 env=env, stdin=p1.stdout)
John Abd-El-Malek4569c422014-10-09 05:10:53362 p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
Ben Pastenee5ece812018-06-20 22:39:34363 forward_signals([p1, p2])
Caleb Rouleaufd511292019-02-22 18:22:00364 wait_with_signals(p1)
365 wait_with_signals(p2)
earthdok9bd5d582015-01-29 21:19:36366 # Also feed the out-of-band JSON output to the symbolizer script.
367 symbolize_snippets_in_json(cmd, env)
vadimsh1eaeb2982014-10-20 12:28:45368 return p1.returncode
John Abd-El-Malek4569c422014-10-09 05:10:53369 else:
Caleb Rouleaufd511292019-02-22 18:22:00370 return run_command(cmd, env=env, log=False)
[email protected]50ec9f232012-03-16 04:18:23371 except OSError:
Chris McDonaldc3e0f26b2020-06-04 02:15:14372 print('Failed to start %s' % cmd, file=sys.stderr)
[email protected]50ec9f232012-03-16 04:18:23373 raise
[email protected]0a88a652012-03-09 00:34:45374
375
Caleb Rouleau6844df152019-09-11 01:11:59376def _popen(*args, **kwargs):
377 assert 'creationflags' not in kwargs
378 if sys.platform == 'win32':
379 # Necessary for signal handling. See crbug.com/733612#c6.
380 kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
381 return subprocess.Popen(*args, **kwargs)
382
383
[email protected]0a88a652012-03-09 00:34:45384def main():
gliderb73f1872014-10-09 16:24:56385 return run_executable(sys.argv[1:], os.environ.copy())
[email protected]0a88a652012-03-09 00:34:45386
387
[email protected]ed763a72012-08-29 03:51:22388if __name__ == '__main__':
Will Harris9e4cf312021-04-19 23:55:15389 if sys.platform == 'win32':
Greg Thompson0c98a682021-06-05 03:30:44390 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
Will Harris9e4cf312021-04-19 23:55:15391 'scripts'))
392 import common
393 common.set_lpac_acls(ROOT_DIR)
[email protected]0a88a652012-03-09 00:34:45394 sys.exit(main())