Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / build / android / pylib / valgrind_tools.py
blob2ecaa19e8f978b3234fe40e00aa0f2f9cfa686a2
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.
5 """
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().
22 """
23 # pylint: disable=R0201
25 import glob
26 import logging
27 import os.path
28 import subprocess
29 import sys
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)
41 else:
42 device.WriteFile(path, '%f' % scale, as_root=True)
45 class BaseTool(object):
46 """A tool that does nothing."""
48 def __init__(self):
49 """Does nothing."""
50 pass
52 def GetTestWrapper(self):
53 """Returns a string that is to be prepended to the test command line."""
54 return ''
56 def GetUtilWrapper(self):
57 """Returns the wrapper name for the utilities.
59 Returns:
60 A string that is to be prepended to the command line of utility
61 processes (forwarder, etc.).
62 """
63 return ''
65 @classmethod
66 def CopyFiles(cls, device):
67 """Copies tool-specific files to the device, create directories, etc."""
68 pass
70 def SetupEnvironment(self):
71 """Sets up the system environment for a test.
73 This is a good place to set system properties.
74 """
75 pass
77 def CleanUpEnvironment(self):
78 """Cleans up environment."""
79 pass
81 def GetTimeoutScale(self):
82 """Returns a multiplier that should be applied to timeout values."""
83 return 1.0
85 def NeedsDebugInfo(self):
86 """Whether this tool requires debug info.
88 Returns:
89 True if this tool can not work with stripped binaries.
90 """
91 return False
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
107 @classmethod
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
115 subprocess.call(
116 [os.path.join(
117 DIR_SOURCE_ROOT,
118 'tools/android/asan/third_party/asan_device_setup.sh'),
119 '--device', str(device),
120 '--lib', libs[0],
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):
136 try:
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):
149 # Very slow startup.
150 return 20.0
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']
166 @classmethod
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)))
178 for f in files)])
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)
195 @staticmethod
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.
203 Returns:
204 True if this tool can not work with stripped binaries.
206 return True
209 class MemcheckTool(ValgrindTool):
210 """Memcheck tool."""
212 def __init__(self, device):
213 super(MemcheckTool, self).__init__(device)
215 @staticmethod
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."""
228 return 30
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)
237 @staticmethod
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."""
251 return 30.0
254 TOOL_REGISTRY = {
255 'memcheck': MemcheckTool,
256 'memcheck-renderer': MemcheckTool,
257 'tsan': TSanTool,
258 'tsan-renderer': TSanTool,
259 'asan': AddressSanitizerTool,
263 def CreateTool(tool_name, device):
264 """Creates a tool with the specified tool name.
266 Args:
267 tool_name: Name of the tool to create.
268 device: A DeviceUtils instance.
269 Returns:
270 A tool for the specified tool_name.
272 if not tool_name:
273 return BaseTool()
275 ctor = TOOL_REGISTRY.get(tool_name)
276 if ctor:
277 return ctor(device)
278 else:
279 print 'Unknown tool %s, available tools: %s' % (
280 tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
281 sys.exit(1)
283 def PushFilesForTool(tool_name, device):
284 """Pushes the files required for |tool_name| to |device|.
286 Args:
287 tool_name: Name of the tool to create.
288 device: A DeviceUtils instance.
290 if not tool_name:
291 return
293 clazz = TOOL_REGISTRY.get(tool_name)
294 if clazz:
295 clazz.CopyFiles(device)
296 else:
297 print 'Unknown tool %s, available tools: %s' % (
298 tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
299 sys.exit(1)