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