valgrind_tools.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 sys
26
27from constants import CHROME_DIR
28
29
30def SetChromeTimeoutScale(adb, scale):
31  """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale."""
32  path = '/data/local/tmp/chrome_timeout_scale'
33  if not scale or scale == 1.0:
34    # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0
35    adb.RunShellCommand('rm %s' % path)
36  else:
37    adb.SetFileContents(path, '%f' % scale)
38
39
40class BaseTool(object):
41  """A tool that does nothing."""
42
43  def GetTestWrapper(self):
44    """Returns a string that is to be prepended to the test command line."""
45    return ''
46
47  def GetUtilWrapper(self):
48    """Returns the wrapper name for the utilities.
49
50    Returns:
51       A string that is to be prepended to the command line of utility
52    processes (forwarder, etc.).
53    """
54    return ''
55
56  def CopyFiles(self):
57    """Copies tool-specific files to the device, create directories, etc."""
58    pass
59
60  def SetupEnvironment(self):
61    """Sets up the system environment for a test.
62
63    This is a good place to set system properties.
64    """
65    pass
66
67  def CleanUpEnvironment(self):
68    """Cleans up environment."""
69    pass
70
71  def GetTimeoutScale(self):
72    """Returns a multiplier that should be applied to timeout values."""
73    return 1.0
74
75  def NeedsDebugInfo(self):
76    """Whether this tool requires debug info.
77
78    Returns:
79      True if this tool can not work with stripped binaries.
80    """
81    return False
82
83
84class AddressSanitizerTool(BaseTool):
85  """AddressSanitizer tool."""
86
87  TMP_DIR = '/data/local/tmp/asan'
88  WRAPPER_NAME = 'asanwrapper.sh'
89
90  def __init__(self, adb):
91    self._adb = adb
92    self._wrap_properties = ['wrap.com.google.android.apps.ch',
93                             'wrap.org.chromium.native_test']
94
95  def CopyFiles(self):
96    """Copies ASan tools to the device."""
97    files = ['tools/android/asan/asanwrapper.sh',
98             'third_party/llvm-build/Release+Asserts/lib/clang/3.2/lib/linux/' +
99                 'libclang_rt.asan-arm-android.so']
100    for f in files:
101      self._adb.PushIfNeeded(os.path.join(CHROME_DIR, f),
102                             os.path.join(AddressSanitizerTool.TMP_DIR,
103                                          os.path.basename(f)))
104
105  def GetTestWrapper(self):
106    return os.path.join(AddressSanitizerTool.TMP_DIR,
107                        AddressSanitizerTool.WRAPPER_NAME)
108
109  def GetUtilWrapper(self):
110    """Returns the wrapper for utilities, such as forwarder.
111
112    AddressSanitizer wrapper must be added to all instrumented binaries,
113    including forwarder and the like. This can be removed if such binaries
114    were built without instrumentation. """
115    return self.GetTestWrapper()
116
117  def SetupEnvironment(self):
118    self._adb.EnableAdbRoot()
119    for prop in self._wrap_properties:
120      self._adb.RunShellCommand('setprop %s "logwrapper %s"' % (
121          prop, self.GetTestWrapper()))
122    SetChromeTimeoutScale(self._adb, self.GetTimeoutScale())
123
124  def CleanUpEnvironment(self):
125    for prop in self._wrap_properties:
126      self._adb.RunShellCommand('setprop %s ""' % (prop,))
127    SetChromeTimeoutScale(self._adb, None)
128
129  def GetTimeoutScale(self):
130    # Very slow startup.
131    return 20.0
132
133
134class ValgrindTool(BaseTool):
135  """Base abstract class for Valgrind tools."""
136
137  VG_DIR = '/data/local/tmp/valgrind'
138  VGLOGS_DIR = '/data/local/tmp/vglogs'
139
140  def __init__(self, adb):
141    self._adb = adb
142    # exactly 31 chars, SystemProperties::PROP_NAME_MAX
143    self._wrap_properties = ['wrap.com.google.android.apps.ch',
144                             'wrap.org.chromium.native_test']
145
146  def CopyFiles(self):
147    """Copies Valgrind tools to the device."""
148    self._adb.RunShellCommand('rm -r %s; mkdir %s' %
149                              (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR))
150    self._adb.RunShellCommand('rm -r %s; mkdir %s' %
151                              (ValgrindTool.VGLOGS_DIR,
152                               ValgrindTool.VGLOGS_DIR))
153    files = self.GetFilesForTool()
154    for f in files:
155      self._adb.PushIfNeeded(os.path.join(CHROME_DIR, f),
156                             os.path.join(ValgrindTool.VG_DIR,
157                                          os.path.basename(f)))
158
159  def SetupEnvironment(self):
160    """Sets up device environment."""
161    self._adb.RunShellCommand('chmod 777 /data/local/tmp')
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