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 # pylint: disable=R0201
      6 
      7 import glob
      8 import logging
      9 import os.path
     10 import subprocess
     11 import sys
     12 
     13 from devil.android import device_errors
     14 from devil.android.valgrind_tools import base_tool
     15 from pylib.constants import DIR_SOURCE_ROOT
     16 
     17 
     18 def SetChromeTimeoutScale(device, scale):
     19   """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale."""
     20   path = '/data/local/tmp/chrome_timeout_scale'
     21   if not scale or scale == 1.0:
     22     # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0
     23     device.RunShellCommand('rm %s' % path)
     24   else:
     25     device.WriteFile(path, '%f' % scale, as_root=True)
     26 
     27 
     28 
     29 class AddressSanitizerTool(base_tool.BaseTool):
     30   """AddressSanitizer tool."""
     31 
     32   WRAPPER_NAME = '/system/bin/asanwrapper'
     33   # Disable memcmp overlap check.There are blobs (gl drivers)
     34   # on some android devices that use memcmp on overlapping regions,
     35   # nothing we can do about that.
     36   EXTRA_OPTIONS = 'strict_memcmp=0,use_sigaltstack=1'
     37 
     38   def __init__(self, device):
     39     super(AddressSanitizerTool, self).__init__()
     40     self._device = device
     41 
     42   @classmethod
     43   def CopyFiles(cls, device):
     44     """Copies ASan tools to the device."""
     45     libs = glob.glob(os.path.join(DIR_SOURCE_ROOT,
     46                                   'third_party/llvm-build/Release+Asserts/',
     47                                   'lib/clang/*/lib/linux/',
     48                                   'libclang_rt.asan-arm-android.so'))
     49     assert len(libs) == 1
     50     subprocess.call(
     51         [os.path.join(
     52              DIR_SOURCE_ROOT,
     53              'tools/android/asan/third_party/asan_device_setup.sh'),
     54          '--device', str(device),
     55          '--lib', libs[0],
     56          '--extra-options', AddressSanitizerTool.EXTRA_OPTIONS])
     57     device.WaitUntilFullyBooted()
     58 
     59   def GetTestWrapper(self):
     60     return AddressSanitizerTool.WRAPPER_NAME
     61 
     62   def GetUtilWrapper(self):
     63     """Returns the wrapper for utilities, such as forwarder.
     64 
     65     AddressSanitizer wrapper must be added to all instrumented binaries,
     66     including forwarder and the like. This can be removed if such binaries
     67     were built without instrumentation. """
     68     return self.GetTestWrapper()
     69 
     70   def SetupEnvironment(self):
     71     try:
     72       self._device.EnableRoot()
     73     except device_errors.CommandFailedError as e:
     74       # Try to set the timeout scale anyway.
     75       # TODO(jbudorick) Handle this exception appropriately after interface
     76       #                 conversions are finished.
     77       logging.error(str(e))
     78     SetChromeTimeoutScale(self._device, self.GetTimeoutScale())
     79 
     80   def CleanUpEnvironment(self):
     81     SetChromeTimeoutScale(self._device, None)
     82 
     83   def GetTimeoutScale(self):
     84     # Very slow startup.
     85     return 20.0
     86 
     87 
     88 class ValgrindTool(base_tool.BaseTool):
     89   """Base abstract class for Valgrind tools."""
     90 
     91   VG_DIR = '/data/local/tmp/valgrind'
     92   VGLOGS_DIR = '/data/local/tmp/vglogs'
     93 
     94   def __init__(self, device):
     95     super(ValgrindTool, self).__init__()
     96     self._device = device
     97     # exactly 31 chars, SystemProperties::PROP_NAME_MAX
     98     self._wrap_properties = ['wrap.com.google.android.apps.ch',
     99                              'wrap.org.chromium.native_test']
    100 
    101   @classmethod
    102   def CopyFiles(cls, device):
    103     """Copies Valgrind tools to the device."""
    104     device.RunShellCommand(
    105         'rm -r %s; mkdir %s' % (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR))
    106     device.RunShellCommand(
    107         'rm -r %s; mkdir %s' % (ValgrindTool.VGLOGS_DIR,
    108                                 ValgrindTool.VGLOGS_DIR))
    109     files = cls.GetFilesForTool()
    110     device.PushChangedFiles(
    111         [((os.path.join(DIR_SOURCE_ROOT, f),
    112           os.path.join(ValgrindTool.VG_DIR, os.path.basename(f)))
    113          for f in files)])
    114 
    115   def SetupEnvironment(self):
    116     """Sets up device environment."""
    117     self._device.RunShellCommand('chmod 777 /data/local/tmp')
    118     self._device.RunShellCommand('setenforce 0')
    119     for prop in self._wrap_properties:
    120       self._device.RunShellCommand(
    121           'setprop %s "logwrapper %s"' % (prop, self.GetTestWrapper()))
    122     SetChromeTimeoutScale(self._device, self.GetTimeoutScale())
    123 
    124   def CleanUpEnvironment(self):
    125     """Cleans up device environment."""
    126     for prop in self._wrap_properties:
    127       self._device.RunShellCommand('setprop %s ""' % (prop,))
    128     SetChromeTimeoutScale(self._device, None)
    129 
    130   @staticmethod
    131   def GetFilesForTool():
    132     """Returns a list of file names for the tool."""
    133     raise NotImplementedError()
    134 
    135   def NeedsDebugInfo(self):
    136     """Whether this tool requires debug info.
    137 
    138     Returns:
    139       True if this tool can not work with stripped binaries.
    140     """
    141     return True
    142 
    143 
    144 class MemcheckTool(ValgrindTool):
    145   """Memcheck tool."""
    146 
    147   def __init__(self, device):
    148     super(MemcheckTool, self).__init__(device)
    149 
    150   @staticmethod
    151   def GetFilesForTool():
    152     """Returns a list of file names for the tool."""
    153     return ['tools/valgrind/android/vg-chrome-wrapper.sh',
    154             'tools/valgrind/memcheck/suppressions.txt',
    155             'tools/valgrind/memcheck/suppressions_android.txt']
    156 
    157   def GetTestWrapper(self):
    158     """Returns a string that is to be prepended to the test command line."""
    159     return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper.sh'
    160 
    161   def GetTimeoutScale(self):
    162     """Returns a multiplier that should be applied to timeout values."""
    163     return 30
    164 
    165 
    166 class TSanTool(ValgrindTool):
    167   """ThreadSanitizer tool. See http://code.google.com/p/data-race-test ."""
    168 
    169   def __init__(self, device):
    170     super(TSanTool, self).__init__(device)
    171 
    172   @staticmethod
    173   def GetFilesForTool():
    174     """Returns a list of file names for the tool."""
    175     return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh',
    176             'tools/valgrind/tsan/suppressions.txt',
    177             'tools/valgrind/tsan/suppressions_android.txt',
    178             'tools/valgrind/tsan/ignores.txt']
    179 
    180   def GetTestWrapper(self):
    181     """Returns a string that is to be prepended to the test command line."""
    182     return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper-tsan.sh'
    183 
    184   def GetTimeoutScale(self):
    185     """Returns a multiplier that should be applied to timeout values."""
    186     return 30.0
    187 
    188 
    189 TOOL_REGISTRY = {
    190     'memcheck': MemcheckTool,
    191     'memcheck-renderer': MemcheckTool,
    192     'tsan': TSanTool,
    193     'tsan-renderer': TSanTool,
    194     'asan': AddressSanitizerTool,
    195 }
    196 
    197 
    198 def CreateTool(tool_name, device):
    199   """Creates a tool with the specified tool name.
    200 
    201   Args:
    202     tool_name: Name of the tool to create.
    203     device: A DeviceUtils instance.
    204   Returns:
    205     A tool for the specified tool_name.
    206   """
    207   if not tool_name:
    208     return base_tool.BaseTool()
    209 
    210   ctor = TOOL_REGISTRY.get(tool_name)
    211   if ctor:
    212     return ctor(device)
    213   else:
    214     print 'Unknown tool %s, available tools: %s' % (
    215         tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
    216     sys.exit(1)
    217 
    218 def PushFilesForTool(tool_name, device):
    219   """Pushes the files required for |tool_name| to |device|.
    220 
    221   Args:
    222     tool_name: Name of the tool to create.
    223     device: A DeviceUtils instance.
    224   """
    225   if not tool_name:
    226     return
    227 
    228   clazz = TOOL_REGISTRY.get(tool_name)
    229   if clazz:
    230     clazz.CopyFiles(device)
    231   else:
    232     print 'Unknown tool %s, available tools: %s' % (
    233         tool_name, ', '.join(sorted(TOOL_REGISTRY.keys())))
    234     sys.exit(1)
    235 
    236