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