1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
6 Classes in this file define additional actions that need to be taken to run a
7 test under some kind of runtime error detection tool.
9 The interface is intended to be used as follows.
11 1. For tests that simply run a native process (i.e. no activity is spawned):
13 Call tool.CopyFiles(device).
14 Prepend test command line with tool.GetTestWrapper().
16 2. For tests that spawn an activity:
18 Call tool.CopyFiles(device).
19 Call tool.SetupEnvironment().
20 Run the test as usual.
21 Call tool.CleanUpEnvironment().
23 # pylint: disable=R0201
31 from pylib.constants import DIR_SOURCE_ROOT
32 from pylib.device import device_errors
35 def SetChromeTimeoutScale(device, scale):
36 """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale."""
37 path = '/data/local/tmp/chrome_timeout_scale'
38 if not scale or scale == 1.0:
39 # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0
40 device.RunShellCommand('rm %s' % path)
42 device.WriteFile(path, '%f' % scale, as_root=True)
45 class BaseTool(object):
46 """A tool that does nothing."""
52 def GetTestWrapper(self):
53 """Returns a string that is to be prepended to the test command line."""
56 def GetUtilWrapper(self):
57 """Returns the wrapper name for the utilities.
60 A string that is to be prepended to the command line of utility
61 processes (forwarder, etc.).
66 def CopyFiles(cls, device):
67 """Copies tool-specific files to the device, create directories, etc."""
70 def SetupEnvironment(self):
71 """Sets up the system environment for a test.
73 This is a good place to set system properties.
77 def CleanUpEnvironment(self):
78 """Cleans up environment."""
81 def GetTimeoutScale(self):
82 """Returns a multiplier that should be applied to timeout values."""
85 def NeedsDebugInfo(self):
86 """Whether this tool requires debug info.
89 True if this tool can not work with stripped binaries.
94 class AddressSanitizerTool(BaseTool):
95 """AddressSanitizer tool."""
97 WRAPPER_NAME = '/system/bin/asanwrapper'
98 # Disable memcmp overlap check.There are blobs (gl drivers)
99 # on some android devices that use memcmp on overlapping regions,
100 # nothing we can do about that.
101 EXTRA_OPTIONS = 'strict_memcmp=0,use_sigaltstack=1'
103 def __init__(self, device):
104 super(AddressSanitizerTool, self).__init__()
105 self._device = device
106 # Configure AndroidCommands to run utils (such as md5sum_bin) under ASan.
107 # This is required because ASan is a compiler-based tool, and md5sum
108 # includes instrumented code from base.
109 device.old_interface.SetUtilWrapper(self.GetUtilWrapper())
112 def CopyFiles(cls, device):
113 """Copies ASan tools to the device."""
114 libs = glob.glob(os.path.join(DIR_SOURCE_ROOT,
115 'third_party/llvm-build/Release+Asserts/',
116 'lib/clang/*/lib/linux/',
117 'libclang_rt.asan-arm-android.so'))
118 assert len(libs) == 1
119 subprocess.call([os.path.join(DIR_SOURCE_ROOT,
120 'tools/android/asan/asan_device_setup.sh'),
121 '--device', str(device),
123 '--extra-options', AddressSanitizerTool.EXTRA_OPTIONS])
124 device.WaitUntilFullyBooted()
126 def GetTestWrapper(self):
127 return AddressSanitizerTool.WRAPPER_NAME
129 def GetUtilWrapper(self):
130 """Returns the wrapper for utilities, such as forwarder.
132 AddressSanitizer wrapper must be added to all instrumented binaries,
133 including forwarder and the like. This can be removed if such binaries
134 were built without instrumentation. """
135 return self.GetTestWrapper()
137 def SetupEnvironment(self):
139 self._device.EnableRoot()
140 except device_errors.CommandFailedError as e:
141 # Try to set the timeout scale anyway.
142 # TODO(jbudorick) Handle this exception appropriately after interface
143 # conversions are finished.
144 logging.error(str(e))
145 SetChromeTimeoutScale(self._device, self.GetTimeoutScale())
147 def CleanUpEnvironment(self):
148 SetChromeTimeoutScale(self._device, None)
150 def GetTimeoutScale(self):
155 class ValgrindTool(BaseTool):
156 """Base abstract class for Valgrind tools."""
158 VG_DIR = '/data/local/tmp/valgrind'
159 VGLOGS_DIR = '/data/local/tmp/vglogs'
161 def __init__(self, device):
162 super(ValgrindTool, self).__init__()
163 self._device = device
164 # exactly 31 chars, SystemProperties::PROP_NAME_MAX
165 self._wrap_properties = ['wrap.com.google.android.apps.ch',
166 'wrap.org.chromium.native_test']
169 def CopyFiles(cls, device):
170 """Copies Valgrind tools to the device."""
171 device.RunShellCommand(
172 'rm -r %s; mkdir %s' % (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR))
173 device.RunShellCommand(
174 'rm -r %s; mkdir %s' % (ValgrindTool.VGLOGS_DIR,
175 ValgrindTool.VGLOGS_DIR))
176 files = cls.GetFilesForTool()
177 device.PushChangedFiles(
178 [((os.path.join(DIR_SOURCE_ROOT, f),
179 os.path.join(ValgrindTool.VG_DIR, os.path.basename(f)))
182 def SetupEnvironment(self):
183 """Sets up device environment."""
184 self._device.RunShellCommand('chmod 777 /data/local/tmp')
185 self._device.RunShellCommand('setenforce 0')
186 for prop in self._wrap_properties:
187 self._device.RunShellCommand(
188 'setprop %s "logwrapper %s"' % (prop, self.GetTestWrapper()))
189 SetChromeTimeoutScale(self._device, self.GetTimeoutScale())
191 def CleanUpEnvironment(self):
192 """Cleans up device environment."""
193 for prop in self._wrap_properties:
194 self._device.RunShellCommand('setprop %s ""' % (prop,))
195 SetChromeTimeoutScale(self._device, None)
198 def GetFilesForTool():
199 """Returns a list of file names for the tool."""
200 raise NotImplementedError()
202 def NeedsDebugInfo(self):
203 """Whether this tool requires debug info.
206 True if this tool can not work with stripped binaries.
211 class MemcheckTool(ValgrindTool):
214 def __init__(self, device):
215 super(MemcheckTool, self).__init__(device)
218 def GetFilesForTool():
219 """Returns a list of file names for the tool."""
220 return ['tools/valgrind/android/vg-chrome-wrapper.sh',
221 'tools/valgrind/memcheck/suppressions.txt',
222 'tools/valgrind/memcheck/suppressions_android.txt']
224 def GetTestWrapper(self):
225 """Returns a string that is to be prepended to the test command line."""
226 return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper.sh'
228 def GetTimeoutScale(self):
229 """Returns a multiplier that should be applied to timeout values."""
233 class TSanTool(ValgrindTool):
234 """ThreadSanitizer tool. See http://code.google.com/p/data-race-test ."""
236 def __init__(self, device):
237 super(TSanTool, self).__init__(device)
240 def GetFilesForTool():
241 """Returns a list of file names for the tool."""
242 return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh',
243 'tools/valgrind/tsan/suppressions.txt',
244 'tools/valgrind/tsan/suppressions_android.txt',
245 'tools/valgrind/tsan/ignores.txt']
247 def GetTestWrapper(self):
248 """Returns a string that is to be prepended to the test command line."""
249 return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper-tsan.sh'
251 def GetTimeoutScale(self):
252 """Returns a multiplier that should be applied to timeout values."""
257 'memcheck': MemcheckTool,
258 'memcheck-renderer': MemcheckTool,
260 'tsan-renderer': TSanTool,
261 'asan': AddressSanitizerTool,
265 def CreateTool(tool_name, device):
266 """Creates a tool with the specified tool name.
269 tool_name: Name of the tool to create.
270 device: A DeviceUtils instance.
272 A tool for the specified tool_name.
277 ctor = TOOL_REGISTRY.get(tool_name)
281 print 'Unknown tool %s, available tools: %s' % (
282 tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
285 def PushFilesForTool(tool_name, device):
286 """Pushes the files required for |tool_name| to |device|.
289 tool_name: Name of the tool to create.
290 device: A DeviceUtils instance.
295 clazz = TOOL_REGISTRY.get(tool_name)
297 clazz.CopyFiles(device)
299 print 'Unknown tool %s, available tools: %s' % (
300 tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))