michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: """ michael@0: Classes in this file define additional actions that need to be taken to run a michael@0: test under some kind of runtime error detection tool. michael@0: michael@0: The interface is intended to be used as follows. michael@0: michael@0: 1. For tests that simply run a native process (i.e. no activity is spawned): michael@0: michael@0: Call tool.CopyFiles(). michael@0: Prepend test command line with tool.GetTestWrapper(). michael@0: michael@0: 2. For tests that spawn an activity: michael@0: michael@0: Call tool.CopyFiles(). michael@0: Call tool.SetupEnvironment(). michael@0: Run the test as usual. michael@0: Call tool.CleanUpEnvironment(). michael@0: """ michael@0: michael@0: import os.path michael@0: import sys michael@0: michael@0: from constants import CHROME_DIR michael@0: michael@0: michael@0: def SetChromeTimeoutScale(adb, scale): michael@0: """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale.""" michael@0: path = '/data/local/tmp/chrome_timeout_scale' michael@0: if not scale or scale == 1.0: michael@0: # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0 michael@0: adb.RunShellCommand('rm %s' % path) michael@0: else: michael@0: adb.SetFileContents(path, '%f' % scale) michael@0: michael@0: michael@0: class BaseTool(object): michael@0: """A tool that does nothing.""" michael@0: michael@0: def GetTestWrapper(self): michael@0: """Returns a string that is to be prepended to the test command line.""" michael@0: return '' michael@0: michael@0: def GetUtilWrapper(self): michael@0: """Returns the wrapper name for the utilities. michael@0: michael@0: Returns: michael@0: A string that is to be prepended to the command line of utility michael@0: processes (forwarder, etc.). michael@0: """ michael@0: return '' michael@0: michael@0: def CopyFiles(self): michael@0: """Copies tool-specific files to the device, create directories, etc.""" michael@0: pass michael@0: michael@0: def SetupEnvironment(self): michael@0: """Sets up the system environment for a test. michael@0: michael@0: This is a good place to set system properties. michael@0: """ michael@0: pass michael@0: michael@0: def CleanUpEnvironment(self): michael@0: """Cleans up environment.""" michael@0: pass michael@0: michael@0: def GetTimeoutScale(self): michael@0: """Returns a multiplier that should be applied to timeout values.""" michael@0: return 1.0 michael@0: michael@0: def NeedsDebugInfo(self): michael@0: """Whether this tool requires debug info. michael@0: michael@0: Returns: michael@0: True if this tool can not work with stripped binaries. michael@0: """ michael@0: return False michael@0: michael@0: michael@0: class AddressSanitizerTool(BaseTool): michael@0: """AddressSanitizer tool.""" michael@0: michael@0: WRAPPER_PATH = '/system/bin/asanwrapper' michael@0: michael@0: def __init__(self, adb): michael@0: self._adb = adb michael@0: self._wrap_properties = ['wrap.com.google.android.apps.ch', michael@0: 'wrap.org.chromium.native_test'] michael@0: michael@0: def CopyFiles(self): michael@0: """Copies ASan tools to the device.""" michael@0: files = ['system/lib/libasan_preload.so', michael@0: 'system/bin/asanwrapper', michael@0: 'system/bin/asan/app_process', michael@0: 'system/bin/linker'] michael@0: android_product_out = os.environ['ANDROID_PRODUCT_OUT'] michael@0: self._adb.MakeSystemFolderWritable() michael@0: for f in files: michael@0: self._adb.PushIfNeeded(os.path.join(android_product_out, f), michael@0: os.path.join('/', f)) michael@0: michael@0: def GetTestWrapper(self): michael@0: return AddressSanitizerTool.WRAPPER_PATH michael@0: michael@0: def GetUtilWrapper(self): michael@0: """Returns the wrapper for utilities, such as forwarder. michael@0: michael@0: AddressSanitizer wrapper must be added to all instrumented binaries, michael@0: including forwarder and the like. This can be removed if such binaries michael@0: were built without instrumentation. """ michael@0: return AddressSanitizerTool.WRAPPER_PATH michael@0: michael@0: def SetupEnvironment(self): michael@0: for prop in self._wrap_properties: michael@0: self._adb.RunShellCommand('setprop %s "logwrapper %s"' % ( michael@0: prop, self.GetTestWrapper())) michael@0: SetChromeTimeoutScale(self._adb, self.GetTimeoutScale()) michael@0: michael@0: def CleanUpEnvironment(self): michael@0: for prop in self._wrap_properties: michael@0: self._adb.RunShellCommand('setprop %s ""' % (prop,)) michael@0: SetChromeTimeoutScale(self._adb, None) michael@0: michael@0: def GetTimeoutScale(self): michael@0: # Very slow startup. michael@0: return 20.0 michael@0: michael@0: michael@0: class ValgrindTool(BaseTool): michael@0: """Base abstract class for Valgrind tools.""" michael@0: michael@0: VG_DIR = '/data/local/tmp/valgrind' michael@0: VGLOGS_DIR = '/data/local/tmp/vglogs' michael@0: michael@0: def __init__(self, adb): michael@0: self._adb = adb michael@0: # exactly 31 chars, SystemProperties::PROP_NAME_MAX michael@0: self._wrap_properties = ['wrap.com.google.android.apps.ch', michael@0: 'wrap.org.chromium.native_test'] michael@0: michael@0: def CopyFiles(self): michael@0: """Copies Valgrind tools to the device.""" michael@0: self._adb.RunShellCommand('rm -r %s; mkdir %s' % michael@0: (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR)) michael@0: self._adb.RunShellCommand('rm -r %s; mkdir %s' % michael@0: (ValgrindTool.VGLOGS_DIR, michael@0: ValgrindTool.VGLOGS_DIR)) michael@0: files = self.GetFilesForTool() michael@0: for f in files: michael@0: self._adb.PushIfNeeded(os.path.join(CHROME_DIR, f), michael@0: os.path.join(ValgrindTool.VG_DIR, michael@0: os.path.basename(f))) michael@0: michael@0: def SetupEnvironment(self): michael@0: """Sets up device environment.""" michael@0: self._adb.RunShellCommand('chmod 777 /data/local/tmp') michael@0: for prop in self._wrap_properties: michael@0: self._adb.RunShellCommand('setprop %s "logwrapper %s"' % ( michael@0: prop, self.GetTestWrapper())) michael@0: SetChromeTimeoutScale(self._adb, self.GetTimeoutScale()) michael@0: michael@0: def CleanUpEnvironment(self): michael@0: """Cleans up device environment.""" michael@0: for prop in self._wrap_properties: michael@0: self._adb.RunShellCommand('setprop %s ""' % (prop,)) michael@0: SetChromeTimeoutScale(self._adb, None) michael@0: michael@0: def GetFilesForTool(self): michael@0: """Returns a list of file names for the tool.""" michael@0: raise NotImplementedError() michael@0: michael@0: def NeedsDebugInfo(self): michael@0: """Whether this tool requires debug info. michael@0: michael@0: Returns: michael@0: True if this tool can not work with stripped binaries. michael@0: """ michael@0: return True michael@0: michael@0: michael@0: class MemcheckTool(ValgrindTool): michael@0: """Memcheck tool.""" michael@0: michael@0: def __init__(self, adb): michael@0: super(MemcheckTool, self).__init__(adb) michael@0: michael@0: def GetFilesForTool(self): michael@0: """Returns a list of file names for the tool.""" michael@0: return ['tools/valgrind/android/vg-chrome-wrapper.sh', michael@0: 'tools/valgrind/memcheck/suppressions.txt', michael@0: 'tools/valgrind/memcheck/suppressions_android.txt'] michael@0: michael@0: def GetTestWrapper(self): michael@0: """Returns a string that is to be prepended to the test command line.""" michael@0: return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper.sh' michael@0: michael@0: def GetTimeoutScale(self): michael@0: """Returns a multiplier that should be applied to timeout values.""" michael@0: return 30 michael@0: michael@0: michael@0: class TSanTool(ValgrindTool): michael@0: """ThreadSanitizer tool. See http://code.google.com/p/data-race-test .""" michael@0: michael@0: def __init__(self, adb): michael@0: super(TSanTool, self).__init__(adb) michael@0: michael@0: def GetFilesForTool(self): michael@0: """Returns a list of file names for the tool.""" michael@0: return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh', michael@0: 'tools/valgrind/tsan/suppressions.txt', michael@0: 'tools/valgrind/tsan/suppressions_android.txt', michael@0: 'tools/valgrind/tsan/ignores.txt'] michael@0: michael@0: def GetTestWrapper(self): michael@0: """Returns a string that is to be prepended to the test command line.""" michael@0: return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper-tsan.sh' michael@0: michael@0: def GetTimeoutScale(self): michael@0: """Returns a multiplier that should be applied to timeout values.""" michael@0: return 30.0 michael@0: michael@0: michael@0: TOOL_REGISTRY = { michael@0: 'memcheck': lambda x: MemcheckTool(x), michael@0: 'memcheck-renderer': lambda x: MemcheckTool(x), michael@0: 'tsan': lambda x: TSanTool(x), michael@0: 'tsan-renderer': lambda x: TSanTool(x), michael@0: 'asan': lambda x: AddressSanitizerTool(x), michael@0: } michael@0: michael@0: michael@0: def CreateTool(tool_name, adb): michael@0: """Creates a tool with the specified tool name. michael@0: michael@0: Args: michael@0: tool_name: Name of the tool to create. michael@0: adb: ADB interface the tool will use. michael@0: Returns: michael@0: A tool for the specified tool_name. michael@0: """ michael@0: if not tool_name: michael@0: return BaseTool() michael@0: michael@0: ctor = TOOL_REGISTRY.get(tool_name) michael@0: if ctor: michael@0: return ctor(adb) michael@0: else: michael@0: print 'Unknown tool %s, available tools: %s' % ( michael@0: tool_name, ', '.join(sorted(TOOL_REGISTRY.keys()))) michael@0: sys.exit(1)