Index: testing/test_env.py |
diff --git a/testing/test_env.py b/testing/test_env.py |
index 2c39508c438d2e9074c10aad29f4c845c380464b..0539309fd59c48c43a2a3baa55d863c768ed01b2 100755 |
--- a/testing/test_env.py |
+++ b/testing/test_env.py |
@@ -5,7 +5,6 @@ |
"""Sets environment variables needed to run a chromium unit test.""" |
-import collections |
import os |
import stat |
import subprocess |
@@ -39,20 +38,31 @@ def should_enable_sandbox(cmd, sandbox_path): |
return False |
-def enable_sandbox_if_required(cmd, env, verbose=False): |
- """Checks enables the sandbox if it is required, otherwise it disables it.""" |
+def get_sandbox_env(cmd, env, verbose=False): |
+ """Checks enables the sandbox if it is required, otherwise it disables it. |
+ Returns the environment flags to set.""" |
+ extra_env = {} |
chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH) |
if should_enable_sandbox(cmd, chrome_sandbox_path): |
if verbose: |
print 'Enabling sandbox. Setting environment variable:' |
print ' %s="%s"' % (CHROME_SANDBOX_ENV, chrome_sandbox_path) |
- env[CHROME_SANDBOX_ENV] = chrome_sandbox_path |
+ extra_env[CHROME_SANDBOX_ENV] = chrome_sandbox_path |
else: |
if verbose: |
print 'Disabling sandbox. Setting environment variable:' |
print ' CHROME_DEVEL_SANDBOX=""' |
- env['CHROME_DEVEL_SANDBOX'] = '' |
+ extra_env['CHROME_DEVEL_SANDBOX'] = '' |
+ |
+ return extra_env |
+ |
+ |
+def trim_cmd(cmd): |
+ """Removes internal flags from cmd since they're just used to communicate from |
+ the host machine to this script running on the swarm slaves.""" |
+ internal_flags = frozenset(['--asan=0', '--asan=1', '--lsan=0', '--lsan=1']) |
+ return [i for i in cmd if i not in internal_flags] |
def fix_python_path(cmd): |
@@ -65,6 +75,63 @@ def fix_python_path(cmd): |
return out |
+def get_asan_env(cmd, lsan): |
+ """Returns the envirnoment flags needed for ASan and LSan.""" |
+ |
+ extra_env = {} |
+ |
+ # Instruct GTK to use malloc while running ASan or LSan tests. |
+ # TODO(earthdok): enabling G_SLICE gives these leaks, locally and on swarming |
+ #0 0x62c01b in __interceptor_malloc (/tmp/run_tha_testXukBDT/out/Release/browser_tests+0x62c01b) |
+ #1 0x7fb64ab64a38 in g_malloc /build/buildd/glib2.0-2.32.4/./glib/gmem.c:159 |
+ #extra_env['G_SLICE'] = 'always-malloc' |
+ |
+ extra_env['NSS_DISABLE_ARENA_FREE_LIST'] = '1' |
+ extra_env['NSS_DISABLE_UNLOAD'] = '1' |
+ |
+ # TODO(glider): remove the symbolizer path once |
+ # https://code.google.com/p/address-sanitizer/issues/detail?id=134 is fixed. |
+ symbolizer_path = os.path.abspath(os.path.join(ROOT_DIR, 'third_party', |
+ 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer')) |
+ |
+ asan_options = [] |
+ if lsan: |
+ asan_options.append('detect_leaks=1') |
+ if sys.platform == 'linux2': |
+ # Use the debug version of libstdc++ under LSan. If we don't, there will |
+ # be a lot of incomplete stack traces in the reports. |
+ extra_env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:' |
+ |
+ # LSan is not sandbox-compatible, so we can use online symbolization. In |
+ # fact, it needs symbolization to be able to apply suppressions. |
+ symbolization_options = ['symbolize=1', |
+ 'external_symbolizer_path=%s' % symbolizer_path] |
+ |
+ suppressions_file = os.path.join(ROOT_DIR, 'tools', 'lsan', |
+ 'suppressions.txt') |
+ lsan_options = ['suppressions=%s' % suppressions_file, |
+ 'print_suppressions=1'] |
+ extra_env['LSAN_OPTIONS'] = ' '.join(lsan_options) |
+ else: |
+ # ASan uses a script for offline symbolization. |
+ # Important note: when running ASan with leak detection enabled, we must use |
+ # the LSan symbolization options above. |
+ symbolization_options = ['symbolize=0'] |
+ |
+ asan_options.extend(symbolization_options) |
+ |
+ extra_env['ASAN_OPTIONS'] = ' '.join(asan_options) |
+ |
+ if sys.platform == 'darwin': |
+ isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0])) |
+ # This is needed because the test binary has @executable_path embedded in it |
+ # it that the OS tries to resolve to the cache directory and not the mapped |
+ # directory. |
+ extra_env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir) |
+ |
+ return extra_env |
+ |
+ |
def run_executable(cmd, env): |
"""Runs an executable with: |
- environment variable CR_SOURCE_ROOT set to the root directory. |
@@ -72,34 +139,38 @@ def run_executable(cmd, env): |
- environment variable CHROME_DEVEL_SANDBOX set if need |
- Reuses sys.executable automatically. |
""" |
- env = collections.defaultdict(str, env) |
+ extra_env = {} |
# Many tests assume a English interface... |
- env['LANG'] = 'en_US.UTF-8' |
+ extra_env['LANG'] = 'en_US.UTF-8' |
# Used by base/base_paths_linux.cc as an override. Just make sure the default |
# logic is used. |
env.pop('CR_SOURCE_ROOT', None) |
- enable_sandbox_if_required(cmd, env) |
+ extra_env.update(get_sandbox_env(cmd, env)) |
# Copy logic from tools/build/scripts/slave/runtest.py. |
asan = '--asan=1' in cmd |
lsan = '--lsan=1' in cmd |
- if lsan and sys.platform == 'linux2': |
- # Use the debug version of libstdc++ under LSan. If we don't, there will |
- # be a lot of incomplete stack traces in the reports. |
- env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:' |
- if asan and sys.platform == 'darwin': |
- isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0])) |
- # This is needed because the test binary has @executable_path embedded in it |
- # that the OS tries to resolve to the cache directory and not the mapped |
- # directory. |
- env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir) |
+ if asan: |
+ extra_env.update(get_asan_env(cmd, lsan)) |
+ if lsan: |
+ cmd.append('--no-sandbox') |
+ |
+ cmd = trim_cmd(cmd) |
# Ensure paths are correctly separated on windows. |
cmd[0] = cmd[0].replace('/', os.path.sep) |
cmd = fix_python_path(cmd) |
+ |
+ print('Additional test environment:\n%s\n' |
+ 'Command: %s\n' % ( |
+ '\n'.join(' %s=%s' % |
+ (k, v) for k, v in sorted(extra_env.iteritems())), |
+ ' '.join(cmd))) |
+ env.update(extra_env or {}) |
try: |
- if asan: |
+ # See above comment regarding offline symbolization. |
+ if asan and not lsan: |
# Need to pipe to the symbolizer script. |
p1 = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, |
stderr=sys.stdout) |