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 devil
.android
import device_errors
32 from pylib
.constants
import DIR_SOURCE_ROOT
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
108 def CopyFiles(cls
, device
):
109 """Copies ASan tools to the device."""
110 libs
= glob
.glob(os
.path
.join(DIR_SOURCE_ROOT
,
111 'third_party/llvm-build/Release+Asserts/',
112 'lib/clang/*/lib/linux/',
113 'libclang_rt.asan-arm-android.so'))
114 assert len(libs
) == 1
118 'tools/android/asan/third_party/asan_device_setup.sh'),
119 '--device', str(device
),
121 '--extra-options', AddressSanitizerTool
.EXTRA_OPTIONS
])
122 device
.WaitUntilFullyBooted()
124 def GetTestWrapper(self
):
125 return AddressSanitizerTool
.WRAPPER_NAME
127 def GetUtilWrapper(self
):
128 """Returns the wrapper for utilities, such as forwarder.
130 AddressSanitizer wrapper must be added to all instrumented binaries,
131 including forwarder and the like. This can be removed if such binaries
132 were built without instrumentation. """
133 return self
.GetTestWrapper()
135 def SetupEnvironment(self
):
137 self
._device
.EnableRoot()
138 except device_errors
.CommandFailedError
as e
:
139 # Try to set the timeout scale anyway.
140 # TODO(jbudorick) Handle this exception appropriately after interface
141 # conversions are finished.
142 logging
.error(str(e
))
143 SetChromeTimeoutScale(self
._device
, self
.GetTimeoutScale())
145 def CleanUpEnvironment(self
):
146 SetChromeTimeoutScale(self
._device
, None)
148 def GetTimeoutScale(self
):
153 class ValgrindTool(BaseTool
):
154 """Base abstract class for Valgrind tools."""
156 VG_DIR
= '/data/local/tmp/valgrind'
157 VGLOGS_DIR
= '/data/local/tmp/vglogs'
159 def __init__(self
, device
):
160 super(ValgrindTool
, self
).__init
__()
161 self
._device
= device
162 # exactly 31 chars, SystemProperties::PROP_NAME_MAX
163 self
._wrap
_properties
= ['wrap.com.google.android.apps.ch',
164 'wrap.org.chromium.native_test']
167 def CopyFiles(cls
, device
):
168 """Copies Valgrind tools to the device."""
169 device
.RunShellCommand(
170 'rm -r %s; mkdir %s' % (ValgrindTool
.VG_DIR
, ValgrindTool
.VG_DIR
))
171 device
.RunShellCommand(
172 'rm -r %s; mkdir %s' % (ValgrindTool
.VGLOGS_DIR
,
173 ValgrindTool
.VGLOGS_DIR
))
174 files
= cls
.GetFilesForTool()
175 device
.PushChangedFiles(
176 [((os
.path
.join(DIR_SOURCE_ROOT
, f
),
177 os
.path
.join(ValgrindTool
.VG_DIR
, os
.path
.basename(f
)))
180 def SetupEnvironment(self
):
181 """Sets up device environment."""
182 self
._device
.RunShellCommand('chmod 777 /data/local/tmp')
183 self
._device
.RunShellCommand('setenforce 0')
184 for prop
in self
._wrap
_properties
:
185 self
._device
.RunShellCommand(
186 'setprop %s "logwrapper %s"' % (prop
, self
.GetTestWrapper()))
187 SetChromeTimeoutScale(self
._device
, self
.GetTimeoutScale())
189 def CleanUpEnvironment(self
):
190 """Cleans up device environment."""
191 for prop
in self
._wrap
_properties
:
192 self
._device
.RunShellCommand('setprop %s ""' % (prop
,))
193 SetChromeTimeoutScale(self
._device
, None)
196 def GetFilesForTool():
197 """Returns a list of file names for the tool."""
198 raise NotImplementedError()
200 def NeedsDebugInfo(self
):
201 """Whether this tool requires debug info.
204 True if this tool can not work with stripped binaries.
209 class MemcheckTool(ValgrindTool
):
212 def __init__(self
, device
):
213 super(MemcheckTool
, self
).__init
__(device
)
216 def GetFilesForTool():
217 """Returns a list of file names for the tool."""
218 return ['tools/valgrind/android/vg-chrome-wrapper.sh',
219 'tools/valgrind/memcheck/suppressions.txt',
220 'tools/valgrind/memcheck/suppressions_android.txt']
222 def GetTestWrapper(self
):
223 """Returns a string that is to be prepended to the test command line."""
224 return ValgrindTool
.VG_DIR
+ '/' + 'vg-chrome-wrapper.sh'
226 def GetTimeoutScale(self
):
227 """Returns a multiplier that should be applied to timeout values."""
231 class TSanTool(ValgrindTool
):
232 """ThreadSanitizer tool. See http://code.google.com/p/data-race-test ."""
234 def __init__(self
, device
):
235 super(TSanTool
, self
).__init
__(device
)
238 def GetFilesForTool():
239 """Returns a list of file names for the tool."""
240 return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh',
241 'tools/valgrind/tsan/suppressions.txt',
242 'tools/valgrind/tsan/suppressions_android.txt',
243 'tools/valgrind/tsan/ignores.txt']
245 def GetTestWrapper(self
):
246 """Returns a string that is to be prepended to the test command line."""
247 return ValgrindTool
.VG_DIR
+ '/' + 'vg-chrome-wrapper-tsan.sh'
249 def GetTimeoutScale(self
):
250 """Returns a multiplier that should be applied to timeout values."""
255 'memcheck': MemcheckTool
,
256 'memcheck-renderer': MemcheckTool
,
258 'tsan-renderer': TSanTool
,
259 'asan': AddressSanitizerTool
,
263 def CreateTool(tool_name
, device
):
264 """Creates a tool with the specified tool name.
267 tool_name: Name of the tool to create.
268 device: A DeviceUtils instance.
270 A tool for the specified tool_name.
275 ctor
= TOOL_REGISTRY
.get(tool_name
)
279 print 'Unknown tool %s, available tools: %s' % (
280 tool_name
, ', '.join(sorted(TOOL_REGISTRY
.keys())))
283 def PushFilesForTool(tool_name
, device
):
284 """Pushes the files required for |tool_name| to |device|.
287 tool_name: Name of the tool to create.
288 device: A DeviceUtils instance.
293 clazz
= TOOL_REGISTRY
.get(tool_name
)
295 clazz
.CopyFiles(device
)
297 print 'Unknown tool %s, available tools: %s' % (
298 tool_name
, ', '.join(sorted(TOOL_REGISTRY
.keys())))