valgrind_tools.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5"""
6Classes in this file define additional actions that need to be taken to run a
7test under some kind of runtime error detection tool.
8
9The interface is intended to be used as follows.
10
111. For tests that simply run a native process (i.e. no activity is spawned):
12
13Call tool.CopyFiles().
14Prepend test command line with tool.GetTestWrapper().
15
162. For tests that spawn an activity:
17
18Call tool.CopyFiles().
19Call tool.SetupEnvironment().
20Run the test as usual.
21Call tool.CleanUpEnvironment().
22"""
23
24import os.path
25import subprocess
26import sys
27from glob import glob
28
29from constants import DIR_SOURCE_ROOT
30
31
32def SetChromeTimeoutScale(adb, scale):
33  """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale."""
34  path = '/data/local/tmp/chrome_timeout_scale'
35  if not scale or scale == 1.0:
36    # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0
37    adb.RunShellCommand('rm %s' % path)
38  else:
39    adb.SetProtectedFileContents(path, '%f' % scale)
40
41
42class BaseTool(object):
43  """A tool that does nothing."""
44
45  def GetTestWrapper(self):
46    """Returns a string that is to be prepended to the test command line."""
47    return ''
48
49  def GetUtilWrapper(self):
50    """Returns the wrapper name for the utilities.
51
52    Returns:
53       A string that is to be prepended to the command line of utility
54    processes (forwarder, etc.).
55    """
56    return ''
57
58  def CopyFiles(self):
59    """Copies tool-specific files to the device, create directories, etc."""
60    pass
61
62  def SetupEnvironment(self):
63    """Sets up the system environment for a test.
64
65    This is a good place to set system properties.
66    """
67    pass
68
69  def CleanUpEnvironment(self):
70    """Cleans up environment."""
71    pass
72
73  def GetTimeoutScale(self):
74    """Returns a multiplier that should be applied to timeout values."""
75    return 1.0
76
77  def NeedsDebugInfo(self):
78    """Whether this tool requires debug info.
79
80    Returns:
81      True if this tool can not work with stripped binaries.
82    """
83    return False
84
85
86class AddressSanitizerTool(BaseTool):
87  """AddressSanitizer tool."""
88
89  WRAPPER_NAME = '/system/bin/asanwrapper'
90  # Disable memcmp overlap check.There are blobs (gl drivers)
91  # on some android devices that use memcmp on overlapping regions,
92  # nothing we can do about that.
93  EXTRA_OPTIONS = 'strict_memcmp=0'
94
95  def __init__(self, adb):
96    self._adb = adb
97    # Configure AndroidCommands to run utils (such as md5sum_bin) under ASan.
98    # This is required because ASan is a compiler-based tool, and md5sum
99    # includes instrumented code from base.
100    adb.SetUtilWrapper(self.GetUtilWrapper())
101
102  def CopyFiles(self):
103    """Copies ASan tools to the device."""
104    subprocess.call([os.path.join(DIR_SOURCE_ROOT,
105                                  'tools/android/asan/asan_device_setup.sh'),
106                     '--device', self._adb.GetDevice(),
107                     '--extra-options', AddressSanitizerTool.EXTRA_OPTIONS])
108    self._adb.WaitForDevicePm()
109
110  def GetTestWrapper(self):
111    return AddressSanitizerTool.WRAPPER_NAME
112
113  def GetUtilWrapper(self):
114    """Returns the wrapper for utilities, such as forwarder.
115
116    AddressSanitizer wrapper must be added to all instrumented binaries,
117    including forwarder and the like. This can be removed if such binaries
118    were built without instrumentation. """
119    return self.GetTestWrapper()
120
121  def SetupEnvironment(self):
122    self._adb.EnableAdbRoot()
123    SetChromeTimeoutScale(self._adb, self.GetTimeoutScale())
124
125  def CleanUpEnvironment(self):
126    SetChromeTimeoutScale(self._adb, None)
127
128  def GetTimeoutScale(self):
129    # Very slow startup.
130    return 20.0
131
132
133class ValgrindTool(BaseTool):
134  """Base abstract class for Valgrind tools."""
135
136  VG_DIR = '/data/local/tmp/valgrind'
137  VGLOGS_DIR = '/data/local/tmp/vglogs'
138
139  def __init__(self, adb):
140    self._adb = adb
141    # exactly 31 chars, SystemProperties::PROP_NAME_MAX
142    self._wrap_properties = ['wrap.com.google.android.apps.ch',
143                             'wrap.org.chromium.native_test']
144
145  def CopyFiles(self):
146    """Copies Valgrind tools to the device."""
147    self._adb.RunShellCommand('rm -r %s; mkdir %s' %
148                              (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR))
149    self._adb.RunShellCommand('rm -r %s; mkdir %s' %
150                              (ValgrindTool.VGLOGS_DIR,
151                               ValgrindTool.VGLOGS_DIR))
152    files = self.GetFilesForTool()
153    for f in files:
154      self._adb.PushIfNeeded(os.path.join(DIR_SOURCE_ROOT, f),
155                             os.path.join(ValgrindTool.VG_DIR,
156                                          os.path.basename(f)))
157
158  def SetupEnvironment(self):
159    """Sets up device environment."""
160    self._adb.RunShellCommand('chmod 777 /data/local/tmp')
161    self._adb.RunShellCommand('setenforce 0')
162    for prop in self._wrap_properties:
163      self._adb.RunShellCommand('setprop %s "logwrapper %s"' % (
164          prop, self.GetTestWrapper()))
165    SetChromeTimeoutScale(self._adb, self.GetTimeoutScale())
166
167  def CleanUpEnvironment(self):
168    """Cleans up device environment."""
169    for prop in self._wrap_properties:
170      self._adb.RunShellCommand('setprop %s ""' % (prop,))
171    SetChromeTimeoutScale(self._adb, None)
172
173  def GetFilesForTool(self):
174    """Returns a list of file names for the tool."""
175    raise NotImplementedError()
176
177  def NeedsDebugInfo(self):
178    """Whether this tool requires debug info.
179
180    Returns:
181      True if this tool can not work with stripped binaries.
182    """
183    return True
184
185
186class MemcheckTool(ValgrindTool):
187  """Memcheck tool."""
188
189  def __init__(self, adb):
190    super(MemcheckTool, self).__init__(adb)
191
192  def GetFilesForTool(self):
193    """Returns a list of file names for the tool."""
194    return ['tools/valgrind/android/vg-chrome-wrapper.sh',
195            'tools/valgrind/memcheck/suppressions.txt',
196            'tools/valgrind/memcheck/suppressions_android.txt']
197
198  def GetTestWrapper(self):
199    """Returns a string that is to be prepended to the test command line."""
200    return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper.sh'
201
202  def GetTimeoutScale(self):
203    """Returns a multiplier that should be applied to timeout values."""
204    return 30
205
206
207class TSanTool(ValgrindTool):
208  """ThreadSanitizer tool. See http://code.google.com/p/data-race-test ."""
209
210  def __init__(self, adb):
211    super(TSanTool, self).__init__(adb)
212
213  def GetFilesForTool(self):
214    """Returns a list of file names for the tool."""
215    return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh',
216            'tools/valgrind/tsan/suppressions.txt',
217            'tools/valgrind/tsan/suppressions_android.txt',
218            'tools/valgrind/tsan/ignores.txt']
219
220  def GetTestWrapper(self):
221    """Returns a string that is to be prepended to the test command line."""
222    return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper-tsan.sh'
223
224  def GetTimeoutScale(self):
225    """Returns a multiplier that should be applied to timeout values."""
226    return 30.0
227
228
229TOOL_REGISTRY = {
230    'memcheck': lambda x: MemcheckTool(x),
231    'memcheck-renderer': lambda x: MemcheckTool(x),
232    'tsan': lambda x: TSanTool(x),
233    'tsan-renderer': lambda x: TSanTool(x),
234    'asan': lambda x: AddressSanitizerTool(x),
235}
236
237
238def CreateTool(tool_name, adb):
239  """Creates a tool with the specified tool name.
240
241  Args:
242    tool_name: Name of the tool to create.
243    adb: ADB interface the tool will use.
244  Returns:
245    A tool for the specified tool_name.
246  """
247  if not tool_name:
248    return BaseTool()
249
250  ctor = TOOL_REGISTRY.get(tool_name)
251  if ctor:
252    return ctor(adb)
253  else:
254    print 'Unknown tool %s, available tools: %s' % (
255        tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
256    sys.exit(1)
257