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