Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(603)

Side by Side Diff: testing/test_env.py

Issue 659543003: Fix LSan on swarming. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/base.isolate ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Sets environment variables needed to run a chromium unit test.""" 6 """Sets environment variables needed to run a chromium unit test."""
7 7
8 import collections
9 import os 8 import os
10 import stat 9 import stat
11 import subprocess 10 import subprocess
12 import sys 11 import sys
13 12
14 # This is hardcoded to be src/ relative to this script. 13 # This is hardcoded to be src/ relative to this script.
15 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 14 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16 15
17 CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX' 16 CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
18 CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox' 17 CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
(...skipping 13 matching lines...) Expand all
32 31
33 sandbox_stat = os.stat(sandbox_path) 32 sandbox_stat = os.stat(sandbox_path)
34 if ((sandbox_stat.st_mode & stat.S_ISUID) and 33 if ((sandbox_stat.st_mode & stat.S_ISUID) and
35 (sandbox_stat.st_mode & stat.S_IRUSR) and 34 (sandbox_stat.st_mode & stat.S_IRUSR) and
36 (sandbox_stat.st_mode & stat.S_IXUSR) and 35 (sandbox_stat.st_mode & stat.S_IXUSR) and
37 (sandbox_stat.st_uid == 0)): 36 (sandbox_stat.st_uid == 0)):
38 return True 37 return True
39 return False 38 return False
40 39
41 40
42 def enable_sandbox_if_required(cmd, env, verbose=False): 41 def get_sandbox_env(cmd, env, verbose=False):
43 """Checks enables the sandbox if it is required, otherwise it disables it.""" 42 """Checks enables the sandbox if it is required, otherwise it disables it.
43 Returns the environment flags to set."""
44 extra_env = {}
44 chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH) 45 chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH)
45 46
46 if should_enable_sandbox(cmd, chrome_sandbox_path): 47 if should_enable_sandbox(cmd, chrome_sandbox_path):
47 if verbose: 48 if verbose:
48 print 'Enabling sandbox. Setting environment variable:' 49 print 'Enabling sandbox. Setting environment variable:'
49 print ' %s="%s"' % (CHROME_SANDBOX_ENV, chrome_sandbox_path) 50 print ' %s="%s"' % (CHROME_SANDBOX_ENV, chrome_sandbox_path)
50 env[CHROME_SANDBOX_ENV] = chrome_sandbox_path 51 extra_env[CHROME_SANDBOX_ENV] = chrome_sandbox_path
51 else: 52 else:
52 if verbose: 53 if verbose:
53 print 'Disabling sandbox. Setting environment variable:' 54 print 'Disabling sandbox. Setting environment variable:'
54 print ' CHROME_DEVEL_SANDBOX=""' 55 print ' CHROME_DEVEL_SANDBOX=""'
55 env['CHROME_DEVEL_SANDBOX'] = '' 56 extra_env['CHROME_DEVEL_SANDBOX'] = ''
57
58 return extra_env
59
60
61 def trim_cmd(cmd):
62 """Removes internal flags from cmd since they're just used to communicate from
63 the host machine to this script running on the swarm slaves."""
64 internal_flags = frozenset(['--asan=0', '--asan=1', '--lsan=0', '--lsan=1'])
65 return [i for i in cmd if i not in internal_flags]
56 66
57 67
58 def fix_python_path(cmd): 68 def fix_python_path(cmd):
59 """Returns the fixed command line to call the right python executable.""" 69 """Returns the fixed command line to call the right python executable."""
60 out = cmd[:] 70 out = cmd[:]
61 if out[0] == 'python': 71 if out[0] == 'python':
62 out[0] = sys.executable 72 out[0] = sys.executable
63 elif out[0].endswith('.py'): 73 elif out[0].endswith('.py'):
64 out.insert(0, sys.executable) 74 out.insert(0, sys.executable)
65 return out 75 return out
66 76
67 77
78 def get_asan_env(cmd, lsan):
79 """Returns the envirnoment flags needed for ASan and LSan."""
80
81 extra_env = {}
82
83 # Instruct GTK to use malloc while running ASan or LSan tests.
84 # TODO(earthdok): enabling G_SLICE gives these leaks, locally and on swarming
85 #0 0x62c01b in __interceptor_malloc (/tmp/run_tha_testXukBDT/out/Release/brows er_tests+0x62c01b)
86 #1 0x7fb64ab64a38 in g_malloc /build/buildd/glib2.0-2.32.4/./glib/gmem.c:159
87 #extra_env['G_SLICE'] = 'always-malloc'
88
89 extra_env['NSS_DISABLE_ARENA_FREE_LIST'] = '1'
90 extra_env['NSS_DISABLE_UNLOAD'] = '1'
91
92 # TODO(glider): remove the symbolizer path once
93 # https://code.google.com/p/address-sanitizer/issues/detail?id=134 is fixed.
94 symbolizer_path = os.path.abspath(os.path.join(ROOT_DIR, 'third_party',
95 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer'))
96
97 asan_options = []
98 if lsan:
99 asan_options.append('detect_leaks=1')
100 if sys.platform == 'linux2':
101 # Use the debug version of libstdc++ under LSan. If we don't, there will
102 # be a lot of incomplete stack traces in the reports.
103 extra_env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:'
104
105 # LSan is not sandbox-compatible, so we can use online symbolization. In
106 # fact, it needs symbolization to be able to apply suppressions.
107 symbolization_options = ['symbolize=1',
108 'external_symbolizer_path=%s' % symbolizer_path]
109
110 suppressions_file = os.path.join(ROOT_DIR, 'tools', 'lsan',
111 'suppressions.txt')
112 lsan_options = ['suppressions=%s' % suppressions_file,
113 'print_suppressions=1']
114 extra_env['LSAN_OPTIONS'] = ' '.join(lsan_options)
115 else:
116 # ASan uses a script for offline symbolization.
117 # Important note: when running ASan with leak detection enabled, we must use
118 # the LSan symbolization options above.
119 symbolization_options = ['symbolize=0']
120
121 asan_options.extend(symbolization_options)
122
123 extra_env['ASAN_OPTIONS'] = ' '.join(asan_options)
124
125 if sys.platform == 'darwin':
126 isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0]))
127 # This is needed because the test binary has @executable_path embedded in it
128 # it that the OS tries to resolve to the cache directory and not the mapped
129 # directory.
130 extra_env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir)
131
132 return extra_env
133
134
68 def run_executable(cmd, env): 135 def run_executable(cmd, env):
69 """Runs an executable with: 136 """Runs an executable with:
70 - environment variable CR_SOURCE_ROOT set to the root directory. 137 - environment variable CR_SOURCE_ROOT set to the root directory.
71 - environment variable LANGUAGE to en_US.UTF-8. 138 - environment variable LANGUAGE to en_US.UTF-8.
72 - environment variable CHROME_DEVEL_SANDBOX set if need 139 - environment variable CHROME_DEVEL_SANDBOX set if need
73 - Reuses sys.executable automatically. 140 - Reuses sys.executable automatically.
74 """ 141 """
75 env = collections.defaultdict(str, env) 142 extra_env = {}
76 # Many tests assume a English interface... 143 # Many tests assume a English interface...
77 env['LANG'] = 'en_US.UTF-8' 144 extra_env['LANG'] = 'en_US.UTF-8'
78 # Used by base/base_paths_linux.cc as an override. Just make sure the default 145 # Used by base/base_paths_linux.cc as an override. Just make sure the default
79 # logic is used. 146 # logic is used.
80 env.pop('CR_SOURCE_ROOT', None) 147 env.pop('CR_SOURCE_ROOT', None)
81 enable_sandbox_if_required(cmd, env) 148 extra_env.update(get_sandbox_env(cmd, env))
82 149
83 # Copy logic from tools/build/scripts/slave/runtest.py. 150 # Copy logic from tools/build/scripts/slave/runtest.py.
84 asan = '--asan=1' in cmd 151 asan = '--asan=1' in cmd
85 lsan = '--lsan=1' in cmd 152 lsan = '--lsan=1' in cmd
86 if lsan and sys.platform == 'linux2':
87 # Use the debug version of libstdc++ under LSan. If we don't, there will
88 # be a lot of incomplete stack traces in the reports.
89 env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:'
90 153
91 if asan and sys.platform == 'darwin': 154 if asan:
92 isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0])) 155 extra_env.update(get_asan_env(cmd, lsan))
93 # This is needed because the test binary has @executable_path embedded in it 156 if lsan:
94 # that the OS tries to resolve to the cache directory and not the mapped 157 cmd.append('--no-sandbox')
95 # directory. 158
96 env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir) 159 cmd = trim_cmd(cmd)
97 160
98 # Ensure paths are correctly separated on windows. 161 # Ensure paths are correctly separated on windows.
99 cmd[0] = cmd[0].replace('/', os.path.sep) 162 cmd[0] = cmd[0].replace('/', os.path.sep)
100 cmd = fix_python_path(cmd) 163 cmd = fix_python_path(cmd)
164
165 print('Additional test environment:\n%s\n'
166 'Command: %s\n' % (
167 '\n'.join(' %s=%s' %
168 (k, v) for k, v in sorted(extra_env.iteritems())),
169 ' '.join(cmd)))
170 env.update(extra_env or {})
101 try: 171 try:
102 if asan: 172 # See above comment regarding offline symbolization.
173 if asan and not lsan:
103 # Need to pipe to the symbolizer script. 174 # Need to pipe to the symbolizer script.
104 p1 = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, 175 p1 = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE,
105 stderr=sys.stdout) 176 stderr=sys.stdout)
106 p2 = subprocess.Popen(["../tools/valgrind/asan/asan_symbolize.py"], 177 p2 = subprocess.Popen(["../tools/valgrind/asan/asan_symbolize.py"],
107 stdin=p1.stdout) 178 stdin=p1.stdout)
108 p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits. 179 p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
109 p2.wait() 180 p2.wait()
110 return p2.returncode 181 return p2.returncode
111 else: 182 else:
112 return subprocess.call(cmd, env=env) 183 return subprocess.call(cmd, env=env)
113 except OSError: 184 except OSError:
114 print >> sys.stderr, 'Failed to start %s' % cmd 185 print >> sys.stderr, 'Failed to start %s' % cmd
115 raise 186 raise
116 187
117 188
118 def main(): 189 def main():
119 return run_executable(sys.argv[1:], os.environ.copy()) 190 return run_executable(sys.argv[1:], os.environ.copy())
120 191
121 192
122 if __name__ == '__main__': 193 if __name__ == '__main__':
123 sys.exit(main()) 194 sys.exit(main())
OLDNEW
« no previous file with comments | « base/base.isolate ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698