Home | History | Annotate | Download | only in port
      1 # Copyright (C) 2012 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #     * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #     * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #     * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import copy
     30 import logging
     31 import os
     32 import re
     33 import signal
     34 import sys
     35 import subprocess
     36 import threading
     37 import time
     38 
     39 from multiprocessing.pool import ThreadPool
     40 
     41 from webkitpy.common.system.executive import ScriptError
     42 from webkitpy.layout_tests.breakpad.dump_reader_multipart import DumpReaderAndroid
     43 from webkitpy.layout_tests.models import test_run_results
     44 from webkitpy.layout_tests.port import base
     45 from webkitpy.layout_tests.port import linux
     46 from webkitpy.layout_tests.port import driver
     47 from webkitpy.layout_tests.port import factory
     48 from webkitpy.layout_tests.port import server_process
     49 from webkitpy.common.system.profiler import SingleFileOutputProfiler
     50 
     51 _log = logging.getLogger(__name__)
     52 
     53 # The root directory for test resources, which has the same structure as the
     54 # source root directory of Chromium.
     55 # This path is defined in Chromium's base/test/test_support_android.cc.
     56 DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
     57 
     58 # The layout tests directory on device, which has two usages:
     59 # 1. as a virtual path in file urls that will be bridged to HTTP.
     60 # 2. pointing to some files that are pushed to the device for tests that
     61 # don't work on file-over-http (e.g. blob protocol tests).
     62 DEVICE_WEBKIT_BASE_DIR = DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/'
     63 DEVICE_LAYOUT_TESTS_DIR = DEVICE_WEBKIT_BASE_DIR + 'LayoutTests/'
     64 
     65 SCALING_GOVERNORS_PATTERN = "/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor"
     66 KPTR_RESTRICT_PATH = "/proc/sys/kernel/kptr_restrict"
     67 
     68 # All the test cases are still served to the test runner through file protocol,
     69 # but we use a file-to-http feature to bridge the file request to host's http
     70 # server to get the real test files and corresponding resources.
     71 # See webkit/support/platform_support_android.cc for the other side of this bridge.
     72 PERF_TEST_PATH_PREFIX = '/all-perf-tests'
     73 LAYOUT_TEST_PATH_PREFIX = '/all-tests'
     74 
     75 # All ports the Android forwarder to forward.
     76 # 8000, 8080 and 8443 are for http/https tests.
     77 # 8880 and 9323 are for websocket tests
     78 # (see http_server.py, apache_http_server.py and websocket_server.py).
     79 FORWARD_PORTS = '8000 8080 8443 8880 9323'
     80 
     81 MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
     82 MS_TRUETYPE_FONTS_PACKAGE = 'ttf-mscorefonts-installer'
     83 
     84 # Timeout in seconds to wait for starting/stopping the driver.
     85 DRIVER_START_STOP_TIMEOUT_SECS = 10
     86 
     87 HOST_FONT_FILES = [
     88     [[MS_TRUETYPE_FONTS_DIR], 'Arial.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     89     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     90     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     91     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     92     [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     93     [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     94     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     95     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     96     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     97     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     98     [[MS_TRUETYPE_FONTS_DIR], 'Georgia.ttf', MS_TRUETYPE_FONTS_PACKAGE],
     99     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    100     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    101     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    102     [[MS_TRUETYPE_FONTS_DIR], 'Impact.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    103     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    104     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    105     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    106     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    107     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    108     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    109     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    110     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    111     [[MS_TRUETYPE_FONTS_DIR], 'Verdana.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    112     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    113     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    114     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
    115     # The Microsoft font EULA
    116     [['/usr/share/doc/ttf-mscorefonts-installer/'], 'READ_ME!.gz', MS_TRUETYPE_FONTS_PACKAGE],
    117     # Other fonts: Arabic, CJK, Indic, Thai, etc.
    118     [['/usr/share/fonts/truetype/ttf-dejavu/'], 'DejaVuSans.ttf', 'ttf-dejavu'],
    119     [['/usr/share/fonts/truetype/kochi/'], 'kochi-mincho.ttf', 'ttf-kochi-mincho'],
    120     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_hi.ttf', 'ttf-indic-fonts-core'],
    121     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_ta.ttf', 'ttf-indic-fonts-core'],
    122     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'MuktiNarrow.ttf', 'ttf-indic-fonts-core'],
    123     [['/usr/share/fonts/truetype/thai/', '/usr/share/fonts/truetype/tlwg/'], 'Garuda.ttf', 'fonts-tlwg-garuda'],
    124     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/', '/usr/share/fonts/truetype/ttf-punjabi-fonts/'], 'lohit_pa.ttf', 'ttf-indic-fonts-core'],
    125 ]
    126 
    127 # Test resources that need to be accessed as files directly.
    128 # Each item can be the relative path of a directory or a file.
    129 TEST_RESOURCES_TO_PUSH = [
    130     # Blob tests need to access files directly.
    131     'editing/pasteboard/resources',
    132     'fast/files/resources',
    133     'http/tests/local/resources',
    134     'http/tests/local/formdata/resources',
    135     # User style URLs are accessed as local files in webkit_support.
    136     'http/tests/security/resources/cssStyle.css',
    137     # Media tests need to access audio/video as files.
    138     'media/content',
    139     'compositing/resources/video.mp4',
    140 ]
    141 
    142 MD5SUM_DEVICE_FILE_NAME = 'md5sum_bin'
    143 MD5SUM_HOST_FILE_NAME = 'md5sum_bin_host'
    144 MD5SUM_DEVICE_PATH = '/data/local/tmp/' + MD5SUM_DEVICE_FILE_NAME
    145 
    146 
    147 # Information required when running layout tests using content_shell as the test runner.
    148 class ContentShellDriverDetails():
    149     def device_cache_directory(self):
    150         return self.device_directory() + 'cache/'
    151 
    152     def device_fonts_directory(self):
    153         return self.device_directory() + 'fonts/'
    154 
    155     def device_forwarder_path(self):
    156         return self.device_directory() + 'forwarder'
    157 
    158     def device_fifo_directory(self):
    159         return '/data/data/' + self.package_name() + '/files/'
    160 
    161     def apk_name(self):
    162         return 'apks/ContentShell.apk'
    163 
    164     def package_name(self):
    165         return 'org.chromium.content_shell_apk'
    166 
    167     def activity_name(self):
    168         return self.package_name() + '/.ContentShellActivity'
    169 
    170     def library_name(self):
    171         return 'libcontent_shell_content_view.so'
    172 
    173     def additional_resources(self):
    174         return ['content_resources.pak', 'shell_resources.pak']
    175 
    176     def command_line_file(self):
    177         return '/data/local/tmp/content-shell-command-line'
    178 
    179     def device_crash_dumps_directory(self):
    180         return '/data/local/tmp/content-shell-crash-dumps'
    181 
    182     def additional_command_line_flags(self, use_breakpad):
    183         flags = ['--dump-render-tree', '--encode-binary']
    184         if use_breakpad:
    185             flags.extend(['--enable-crash-reporter', '--crash-dumps-dir=%s' % self.device_crash_dumps_directory()])
    186         return flags
    187 
    188     def device_directory(self):
    189         return DEVICE_SOURCE_ROOT_DIR + 'content_shell/'
    190 
    191 
    192 # The AndroidCommands class encapsulates commands to communicate with an attached device.
    193 class AndroidCommands(object):
    194     _adb_command_path = None
    195     _adb_command_path_options = []
    196 
    197     def __init__(self, executive, device_serial, debug_logging):
    198         self._executive = executive
    199         self._device_serial = device_serial
    200         self._debug_logging = debug_logging
    201 
    202     # Local public methods.
    203 
    204     def file_exists(self, full_path):
    205         assert full_path.startswith('/')
    206         return self.run(['shell', 'ls', '-d', full_path]).strip() == full_path
    207 
    208     def push(self, host_path, device_path, ignore_error=False):
    209         return self.run(['push', host_path, device_path], ignore_error=ignore_error)
    210 
    211     def pull(self, device_path, host_path, ignore_error=False):
    212         return self.run(['pull', device_path, host_path], ignore_error=ignore_error)
    213 
    214     def mkdir(self, device_path, chmod=None):
    215         self.run(['shell', 'mkdir', '-p', device_path])
    216         if chmod:
    217             self.run(['shell', 'chmod', chmod, device_path])
    218 
    219     def restart_adb(self):
    220         pids = self.extract_pids('adbd')
    221         if pids:
    222             output = self.run(['shell', 'kill', '-' + str(signal.SIGTERM)] + pids)
    223         self.run(['wait-for-device'])
    224 
    225     def restart_as_root(self):
    226         output = self.run(['root'])
    227         if 'adbd is already running as root' in output:
    228             return
    229 
    230         elif not 'restarting adbd as root' in output:
    231             self._log_error('Unrecognized output from adb root: %s' % output)
    232 
    233         self.run(['wait-for-device'])
    234 
    235     def extract_pids(self, process_name):
    236         pids = []
    237         output = self.run(['shell', 'ps'])
    238         for line in output.splitlines():
    239             data = line.split()
    240             try:
    241                 if process_name in data[-1]:  # name is in the last column
    242                     if process_name == data[-1]:
    243                         pids.insert(0, data[1])  # PID is in the second column
    244                     else:
    245                         pids.append(data[1])
    246             except IndexError:
    247                 pass
    248         return pids
    249 
    250     def run(self, command, ignore_error=False):
    251         self._log_debug('Run adb command: ' + str(command))
    252         if ignore_error:
    253             error_handler = self._executive.ignore_error
    254         else:
    255             error_handler = None
    256 
    257         result = self._executive.run_command(self.adb_command() + command, error_handler=error_handler, debug_logging=self._debug_logging)
    258 
    259         # We limit the length to avoid outputting too verbose commands, such as "adb logcat".
    260         self._log_debug('Run adb result: ' + result[:80])
    261         return result
    262 
    263     def get_serial(self):
    264         return self._device_serial
    265 
    266     def adb_command(self):
    267         return [AndroidCommands.adb_command_path(self._executive, self._debug_logging), '-s', self._device_serial]
    268 
    269     @staticmethod
    270     def set_adb_command_path_options(paths):
    271         AndroidCommands._adb_command_path_options = paths
    272 
    273     @staticmethod
    274     def adb_command_path(executive, debug_logging):
    275         if AndroidCommands._adb_command_path:
    276             return AndroidCommands._adb_command_path
    277 
    278         assert AndroidCommands._adb_command_path_options, 'No commands paths have been set to look for the "adb" command.'
    279 
    280         command_path = None
    281         command_version = None
    282         for path_option in AndroidCommands._adb_command_path_options:
    283             path_version = AndroidCommands._determine_adb_version(path_option, executive, debug_logging)
    284             if not path_version:
    285                 continue
    286             if command_version != None and path_version < command_version:
    287                 continue
    288 
    289             command_path = path_option
    290             command_version = path_version
    291 
    292         assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?'
    293 
    294         AndroidCommands._adb_command_path = command_path
    295         return command_path
    296 
    297     # Local private methods.
    298 
    299     def _log_error(self, message):
    300         _log.error('[%s] %s' % (self._device_serial, message))
    301 
    302     def _log_info(self, message):
    303         _log.info('[%s] %s' % (self._device_serial, message))
    304 
    305     def _log_debug(self, message):
    306         if self._debug_logging:
    307             _log.debug('[%s] %s' % (self._device_serial, message))
    308 
    309     @staticmethod
    310     def _determine_adb_version(adb_command_path, executive, debug_logging):
    311         re_version = re.compile('^.*version ([\d\.]+)$')
    312         try:
    313             output = executive.run_command([adb_command_path, 'version'], error_handler=executive.ignore_error,
    314                                            debug_logging=debug_logging)
    315         except OSError:
    316             return None
    317 
    318         result = re_version.match(output)
    319         if not output or not result:
    320             return None
    321 
    322         return [int(n) for n in result.group(1).split('.')]
    323 
    324 
    325 # A class to encapsulate device status and information, such as the AndroidCommands
    326 # instances and whether the device has been set up.
    327 class AndroidDevices(object):
    328     # Percentage of battery a device needs to have in order for it to be considered
    329     # to participate in running the layout tests.
    330     MINIMUM_BATTERY_PERCENTAGE = 30
    331 
    332     def __init__(self, executive, default_device=None, debug_logging=False):
    333         self._usable_devices = []
    334         self._default_device = default_device
    335         self._prepared_devices = []
    336         self._debug_logging = debug_logging
    337 
    338     def prepared_devices(self):
    339         return self._prepared_devices
    340 
    341     def usable_devices(self, executive):
    342         if self._usable_devices:
    343             return self._usable_devices
    344 
    345         if self._default_device:
    346             self._usable_devices = [AndroidCommands(executive, self._default_device, self._debug_logging)]
    347             return self._usable_devices
    348 
    349         # Example "adb devices" command output:
    350         #   List of devices attached
    351         #   0123456789ABCDEF        device
    352         re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
    353 
    354         result = executive.run_command([AndroidCommands.adb_command_path(executive, debug_logging=self._debug_logging), 'devices'],
    355                                        error_handler=executive.ignore_error, debug_logging=self._debug_logging)
    356         devices = re_device.findall(result)
    357         if not devices:
    358             return []
    359 
    360         for device_serial in sorted(devices):
    361             commands = AndroidCommands(executive, device_serial, self._debug_logging)
    362             if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM_BATTERY_PERCENTAGE:
    363                 _log.warning('Device with serial "%s" skipped because it has less than %d percent battery.'
    364                     % (commands.get_serial(), AndroidDevices.MINIMUM_BATTERY_PERCENTAGE))
    365                 continue
    366 
    367             if not self._is_device_screen_on(commands):
    368                 _log.warning('Device with serial "%s" skipped because the screen must be on.' % commands.get_serial())
    369                 continue
    370 
    371             self._usable_devices.append(commands)
    372 
    373         return self._usable_devices
    374 
    375     def get_device(self, executive, device_index):
    376         devices = self.usable_devices(executive)
    377         if device_index >= len(devices):
    378             raise AssertionError('Device index exceeds number of usable devices.')
    379 
    380         return devices[device_index]
    381 
    382     def is_device_prepared(self, device_serial):
    383         return device_serial in self._prepared_devices
    384 
    385     def set_device_prepared(self, device_serial):
    386         self._prepared_devices.append(device_serial)
    387 
    388     # Private methods
    389     def _battery_level_for_device(self, commands):
    390         battery_status = commands.run(['shell', 'dumpsys', 'battery'])
    391         if 'Error' in battery_status or "Can't find service: battery" in battery_status:
    392             _log.warning('Unable to read the battery level from device with serial "%s".' % commands.get_serial())
    393             return 0
    394 
    395         return int(re.findall('level: (\d+)', battery_status)[0])
    396 
    397     def _is_device_screen_on(self, commands):
    398         power_status = commands.run(['shell', 'dumpsys', 'power'])
    399         return 'mScreenOn=true' in power_status or 'mScreenOn=SCREEN_ON_BIT' in power_status
    400 
    401 
    402 class AndroidPort(base.Port):
    403     port_name = 'android'
    404 
    405     # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
    406     _adb_path = None
    407 
    408     SUPPORTED_VERSIONS = ('android')
    409 
    410     FALLBACK_PATHS = {'icecreamsandwich': ['android'] + linux.LinuxPort.latest_platform_fallback_path()}
    411 
    412     # Android has aac and mp3 codecs built in.
    413     PORT_HAS_AUDIO_CODECS_BUILT_IN = True
    414 
    415     BUILD_REQUIREMENTS_URL = 'https://code.google.com/p/chromium/wiki/AndroidBuildInstructions'
    416 
    417     def __init__(self, host, port_name, **kwargs):
    418         super(AndroidPort, self).__init__(host, port_name, **kwargs)
    419 
    420         self._operating_system = 'android'
    421         self._version = 'icecreamsandwich'
    422 
    423         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
    424         self._server_process_constructor = self._android_server_process_constructor
    425 
    426         if not self.get_option('disable_breakpad'):
    427             self._dump_reader = DumpReaderAndroid(host, self._build_path())
    428 
    429         if self.driver_name() != self.CONTENT_SHELL_NAME:
    430             raise AssertionError('Layout tests on Android only support content_shell as the driver.')
    431 
    432         self._driver_details = ContentShellDriverDetails()
    433 
    434         # Initialize the AndroidDevices class which tracks available devices.
    435         default_device = None
    436         if hasattr(self._options, 'adb_device') and len(self._options.adb_device):
    437             default_device = self._options.adb_device
    438 
    439         self._debug_logging = self.get_option('android_logging')
    440         self._devices = AndroidDevices(self._executive, default_device, self._debug_logging)
    441 
    442         # Tell AndroidCommands where to search for the "adb" command.
    443         AndroidCommands.set_adb_command_path_options(['adb',
    444             self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')])
    445 
    446         prepared_devices = self.get_option('prepared_devices', [])
    447         for serial in prepared_devices:
    448             self._devices.set_device_prepared(serial)
    449 
    450     def default_smoke_test_only(self):
    451         return True
    452 
    453     # Local public methods.
    454     def path_to_forwarder(self):
    455         return self._build_path('forwarder')
    456 
    457     def path_to_md5sum(self):
    458         return self._build_path(MD5SUM_DEVICE_FILE_NAME)
    459 
    460     def path_to_md5sum_host(self):
    461         return self._build_path(MD5SUM_HOST_FILE_NAME)
    462 
    463     def additional_drt_flag(self):
    464         return self._driver_details.additional_command_line_flags(use_breakpad=not self.get_option('disable_breakpad'))
    465 
    466     def default_timeout_ms(self):
    467         # Android platform has less computing power than desktop platforms.
    468         # Using 10 seconds allows us to pass most slow tests which are not
    469         # marked as slow tests on desktop platforms.
    470         return 10 * 1000
    471 
    472     def driver_stop_timeout(self):
    473         # The driver doesn't respond to closing stdin, so we might as well stop the driver immediately.
    474         return 0.0
    475 
    476     def default_child_processes(self):
    477         usable_devices = self._devices.usable_devices(self._executive)
    478         if not usable_devices:
    479             raise test_run_results.TestRunException(test_run_results.NO_DEVICES_EXIT_STATUS, "Unable to find any attached Android devices.")
    480         return len(usable_devices)
    481 
    482     def check_wdiff(self, logging=True):
    483         return self._host_port.check_wdiff(logging)
    484 
    485     def check_build(self, needs_http, printer):
    486         exit_status = super(AndroidPort, self).check_build(needs_http, printer)
    487         if exit_status:
    488             return exit_status
    489 
    490         result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility')
    491         result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result
    492         result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result
    493 
    494         if not result:
    495             # There is a race condition in adb at least <= 4.3 on Linux that causes it to go offline periodically
    496             # We set the processor affinity for any running adb process to attempt to work around this.
    497             # See crbug.com/268450
    498             if self.host.platform.is_linux():
    499                 pids = self._executive.running_pids(lambda name: 'adb' in name)
    500                 if not pids:
    501                     # Apparently adb is not running, which is unusual. Running any adb command should start it.
    502                     self._executive.run_command(['adb', 'devices'])
    503                     pids = self._executive.running_pids(lambda name: 'adb' in name)
    504                 if not pids:
    505                     _log.error("The adb daemon does not appear to be running.")
    506                     return False
    507 
    508                 for pid in pids:
    509                     self._executive.run_command(['taskset', '-p', '-c', '0', str(pid)])
    510 
    511         if not result:
    512             _log.error('For complete Android build requirements, please see:')
    513             _log.error('')
    514             _log.error('    http://code.google.com/p/chromium/wiki/AndroidBuildInstructions')
    515             return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
    516 
    517         return self._check_devices(printer)
    518 
    519     def _check_devices(self, printer):
    520         # Printer objects aren't threadsafe, so we need to protect calls to them.
    521         lock = threading.Lock()
    522         pool = None
    523 
    524         # Push the executables and other files to the devices; doing this now
    525         # means we can do this in parallel in the manager process and not mix
    526         # this in with starting and stopping workers.
    527         def setup_device(worker_number):
    528             d = self.create_driver(worker_number)
    529             serial = d._android_commands.get_serial()
    530 
    531             def log_safely(msg, throttled=True):
    532                 if throttled:
    533                     callback = printer.write_throttled_update
    534                 else:
    535                     callback = printer.write_update
    536                 lock.acquire()
    537                 try:
    538                     callback("[%s] %s" % (serial, msg))
    539                 finally:
    540                     lock.release()
    541 
    542             log_safely("preparing device", throttled=False)
    543             try:
    544                 d._setup_test(log_safely)
    545                 log_safely("device prepared", throttled=False)
    546             except (ScriptError, driver.DeviceFailure) as e:
    547                 lock.acquire()
    548                 _log.warning("[%s] failed to prepare_device: %s" % (serial, str(e)))
    549                 lock.release()
    550             except KeyboardInterrupt:
    551                 if pool:
    552                     pool.terminate()
    553 
    554         # FIXME: It would be nice if we knew how many workers we needed.
    555         num_workers = self.default_child_processes()
    556         num_child_processes = int(self.get_option('child_processes'))
    557         if num_child_processes:
    558             num_workers = min(num_workers, num_child_processes)
    559         if num_workers > 1:
    560             pool = ThreadPool(num_workers)
    561             try:
    562                 pool.map(setup_device, range(num_workers))
    563             except KeyboardInterrupt:
    564                 pool.terminate()
    565                 raise
    566         else:
    567             setup_device(0)
    568 
    569         if not self._devices.prepared_devices():
    570             _log.error('Could not prepare any devices for testing.')
    571             return test_run_results.NO_DEVICES_EXIT_STATUS
    572         return test_run_results.OK_EXIT_STATUS
    573 
    574     def setup_test_run(self):
    575         super(AndroidPort, self).setup_test_run()
    576 
    577         # By setting this on the options object, we can propagate the list
    578         # of prepared devices to the workers (it is read in __init__()).
    579         if self._devices._prepared_devices:
    580             self._options.prepared_devices = self._devices.prepared_devices()
    581         else:
    582             # We were called with --no-build, so assume the devices are up to date.
    583             self._options.prepared_devices = [d.get_serial() for d in self._devices.usable_devices(self.host.executive)]
    584 
    585     def num_workers(self, requested_num_workers):
    586         return min(len(self._options.prepared_devices), requested_num_workers)
    587 
    588     def check_sys_deps(self, needs_http):
    589         for (font_dirs, font_file, package) in HOST_FONT_FILES:
    590             exists = False
    591             for font_dir in font_dirs:
    592                 font_path = font_dir + font_file
    593                 if self._check_file_exists(font_path, '', logging=False):
    594                     exists = True
    595                     break
    596             if not exists:
    597                 _log.error('You are missing %s under %s. Try installing %s. See build instructions.' % (font_file, font_dirs, package))
    598                 return test_run_results.SYS_DEPS_EXIT_STATUS
    599         return test_run_results.OK_EXIT_STATUS
    600 
    601     def requires_http_server(self):
    602         """Chromium Android runs tests on devices, and uses the HTTP server to
    603         serve the actual layout tests to the test driver."""
    604         return True
    605 
    606     def start_http_server(self, additional_dirs, number_of_drivers):
    607         additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir()
    608         additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
    609         super(AndroidPort, self).start_http_server(additional_dirs, number_of_drivers)
    610 
    611     def create_driver(self, worker_number, no_timeout=False):
    612         return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_option('pixel_tests'),
    613                                      driver_details=self._driver_details,
    614                                      android_devices=self._devices,
    615                                      # Force no timeout to avoid test driver timeouts before NRWT.
    616                                      no_timeout=True)
    617 
    618     def driver_cmd_line(self):
    619         # Override to return the actual test driver's command line.
    620         return self.create_driver(0)._android_driver_cmd_line(self.get_option('pixel_tests'), [])
    621 
    622     def clobber_old_port_specific_results(self):
    623         if not self.get_option('disable_breakpad'):
    624             self._dump_reader.clobber_old_results()
    625 
    626     # Overridden protected methods.
    627 
    628     def _build_path(self, *comps):
    629         return self._host_port._build_path(*comps)
    630 
    631     def _build_path_with_configuration(self, configuration, *comps):
    632         return self._host_port._build_path_with_configuration(configuration, *comps)
    633 
    634     def path_to_apache(self):
    635         return self._host_port.path_to_apache()
    636 
    637     def path_to_apache_config_file(self):
    638         return self._host_port.path_to_apache_config_file()
    639 
    640     def _path_to_driver(self, configuration=None):
    641         return self._build_path_with_configuration(configuration, self._driver_details.apk_name())
    642 
    643     def _path_to_helper(self):
    644         return None
    645 
    646     def _path_to_image_diff(self):
    647         return self._host_port._path_to_image_diff()
    648 
    649     def path_to_lighttpd(self):
    650         return self._host_port._path_to_lighttpd()
    651 
    652     def path_to_lighttpd_modules(self):
    653         return self._host_port._path_to_lighttpd_modules()
    654 
    655     def path_to_lighttpd_php(self):
    656         return self._host_port._path_to_lighttpd_php()
    657 
    658     def _path_to_wdiff(self):
    659         return self._host_port._path_to_wdiff()
    660 
    661     def _shut_down_http_server(self, pid):
    662         return self._host_port._shut_down_http_server(pid)
    663 
    664     def _driver_class(self):
    665         return ChromiumAndroidDriver
    666 
    667     # Local private methods.
    668 
    669     @staticmethod
    670     def _android_server_process_constructor(port, server_name, cmd_line, env=None, logging=False):
    671         return server_process.ServerProcess(port, server_name, cmd_line, env,
    672                                             universal_newlines=True, treat_no_data_as_crash=True, logging=logging)
    673 
    674 
    675 class AndroidPerf(SingleFileOutputProfiler):
    676     _cached_perf_host_path = None
    677     _have_searched_for_perf_host = False
    678 
    679     def __init__(self, host, executable_path, output_dir, android_commands, symfs_path, kallsyms_path, identifier=None):
    680         super(AndroidPerf, self).__init__(host, executable_path, output_dir, "data", identifier)
    681         self._android_commands = android_commands
    682         self._perf_process = None
    683         self._symfs_path = symfs_path
    684         self._kallsyms_path = kallsyms_path
    685 
    686     def check_configuration(self):
    687         # Check that perf is installed
    688         if not self._android_commands.file_exists('/system/bin/perf'):
    689             print "Cannot find /system/bin/perf on device %s" % self._android_commands.get_serial()
    690             return False
    691 
    692         # Check that the device is a userdebug build (or at least has the necessary libraries).
    693         if self._android_commands.run(['shell', 'getprop', 'ro.build.type']).strip() != 'userdebug':
    694             print "Device %s is not flashed with a userdebug build of Android" % self._android_commands.get_serial()
    695             return False
    696 
    697         # FIXME: Check that the binary actually is perf-able (has stackframe pointers)?
    698         # objdump -s a function and make sure it modifies the fp?
    699         # Instruct users to rebuild after export GYP_DEFINES="profiling=1 $GYP_DEFINES"
    700         return True
    701 
    702     def print_setup_instructions(self):
    703         print """
    704 perf on android requires a 'userdebug' build of Android, see:
    705 http://source.android.com/source/building-devices.html"
    706 
    707 The perf command can be built from:
    708 https://android.googlesource.com/platform/external/linux-tools-perf/
    709 and requires libefl, libebl, libdw, and libdwfl available in:
    710 https://android.googlesource.com/platform/external/elfutils/
    711 
    712 The test driver must be built with profiling=1, make sure you've done:
    713 export GYP_DEFINES="profiling=1 $GYP_DEFINES"
    714 update-webkit --chromium-android
    715 build-webkit --chromium-android
    716 
    717 Googlers should read:
    718 http://goto.google.com/cr-android-perf-howto
    719 """
    720 
    721     def attach_to_pid(self, pid):
    722         assert(pid)
    723         assert(self._perf_process == None)
    724         # FIXME: This can't be a fixed timeout!
    725         cmd = self._android_commands.adb_command() + ['shell', 'perf', 'record', '-g', '-p', pid, 'sleep', 30]
    726         self._perf_process = self._host.executive.popen(cmd)
    727 
    728     def _perf_version_string(self, perf_path):
    729         try:
    730             return self._host.executive.run_command([perf_path, '--version'])
    731         except:
    732             return None
    733 
    734     def _find_perfhost_binary(self):
    735         perfhost_version = self._perf_version_string('perfhost_linux')
    736         if perfhost_version:
    737             return 'perfhost_linux'
    738         perf_version = self._perf_version_string('perf')
    739         if perf_version:
    740             return 'perf'
    741         return None
    742 
    743     def _perfhost_path(self):
    744         if self._have_searched_for_perf_host:
    745             return self._cached_perf_host_path
    746         self._have_searched_for_perf_host = True
    747         self._cached_perf_host_path = self._find_perfhost_binary()
    748         return self._cached_perf_host_path
    749 
    750     def _first_ten_lines_of_profile(self, perf_output):
    751         match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MULTILINE)
    752         return match.group(1) if match else None
    753 
    754     def profile_after_exit(self):
    755         perf_exitcode = self._perf_process.wait()
    756         if perf_exitcode != 0:
    757             print "Perf failed (exit code: %i), can't process results." % perf_exitcode
    758             return
    759 
    760         self._android_commands.pull('/data/perf.data', self._output_path)
    761 
    762         perfhost_path = self._perfhost_path()
    763         perfhost_report_command = [
    764             'report',
    765             '--input', self._output_path,
    766             '--symfs', self._symfs_path,
    767             '--kallsyms', self._kallsyms_path,
    768         ]
    769         if perfhost_path:
    770             perfhost_args = [perfhost_path] + perfhost_report_command + ['--call-graph', 'none']
    771             perf_output = self._host.executive.run_command(perfhost_args)
    772             # We could save off the full -g report to a file if users found that useful.
    773             print self._first_ten_lines_of_profile(perf_output)
    774         else:
    775             print """
    776 Failed to find perfhost_linux binary, can't process samples from the device.
    777 
    778 perfhost_linux can be built from:
    779 https://android.googlesource.com/platform/external/linux-tools-perf/
    780 also, modern versions of perf (available from apt-get install goobuntu-kernel-tools-common)
    781 may also be able to process the perf.data files from the device.
    782 
    783 Googlers should read:
    784 http://goto.google.com/cr-android-perf-howto
    785 for instructions on installing pre-built copies of perfhost_linux
    786 http://crbug.com/165250 discusses making these pre-built binaries externally available.
    787 """
    788 
    789         perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_linux'
    790         print "To view the full profile, run:"
    791         print ' '.join([perfhost_display_patch] + perfhost_report_command)
    792 
    793 
    794 class ChromiumAndroidDriver(driver.Driver):
    795     def __init__(self, port, worker_number, pixel_tests, driver_details, android_devices, no_timeout=False):
    796         super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_tests, no_timeout)
    797         self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fifo'
    798         self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fifo'
    799         self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.fifo'
    800         self._read_stdout_process = None
    801         self._read_stderr_process = None
    802         self._forwarder_process = None
    803         self._original_governors = {}
    804         self._original_kptr_restrict = None
    805 
    806         self._android_devices = android_devices
    807         self._android_commands = android_devices.get_device(port._executive, worker_number)
    808         self._driver_details = driver_details
    809         self._debug_logging = self._port._debug_logging
    810         self._created_cmd_line = False
    811         self._device_failed = False
    812 
    813         # FIXME: If we taught ProfileFactory about "target" devices we could
    814         # just use the logic in Driver instead of duplicating it here.
    815         if self._port.get_option("profile"):
    816             # FIXME: This should be done once, instead of per-driver!
    817             symfs_path = self._find_or_create_symfs()
    818             kallsyms_path = self._update_kallsyms_cache(symfs_path)
    819             # FIXME: We should pass this some sort of "Bridge" object abstraction around ADB instead of a path/device pair.
    820             self._profiler = AndroidPerf(self._port.host, self._port._path_to_driver(), self._port.results_directory(),
    821                 self._android_commands, symfs_path, kallsyms_path)
    822             # FIXME: This is a layering violation and should be moved to Port.check_sys_deps
    823             # once we have an abstraction around an adb_path/device_serial pair to make it
    824             # easy to make these class methods on AndroidPerf.
    825             if not self._profiler.check_configuration():
    826                 self._profiler.print_setup_instructions()
    827                 sys.exit(1)
    828         else:
    829             self._profiler = None
    830 
    831     def __del__(self):
    832         self._teardown_performance()
    833         self._clean_up_cmd_line()
    834         super(ChromiumAndroidDriver, self).__del__()
    835 
    836     def _update_kallsyms_cache(self, output_dir):
    837         kallsyms_name = "%s-kallsyms" % self._android_commands.get_serial()
    838         kallsyms_cache_path = self._port.host.filesystem.join(output_dir, kallsyms_name)
    839 
    840         self._android_commands.restart_as_root()
    841 
    842         saved_kptr_restrict = self._android_commands.run(['shell', 'cat', KPTR_RESTRICT_PATH]).strip()
    843         self._android_commands.run(['shell', 'echo', '0', '>', KPTR_RESTRICT_PATH])
    844 
    845         print "Updating kallsyms file (%s) from device" % kallsyms_cache_path
    846         self._android_commands.pull("/proc/kallsyms", kallsyms_cache_path)
    847 
    848         self._android_commands.run(['shell', 'echo', saved_kptr_restrict, '>', KPTR_RESTRICT_PATH])
    849 
    850         return kallsyms_cache_path
    851 
    852     def _find_or_create_symfs(self):
    853         environment = self._port.host.copy_current_environment()
    854         env = environment.to_dictionary()
    855         fs = self._port.host.filesystem
    856 
    857         if 'ANDROID_SYMFS' in env:
    858             symfs_path = env['ANDROID_SYMFS']
    859         else:
    860             symfs_path = fs.join(self._port.results_directory(), 'symfs')
    861             print "ANDROID_SYMFS not set, using %s" % symfs_path
    862 
    863         # find the installed path, and the path of the symboled built library
    864         # FIXME: We should get the install path from the device!
    865         symfs_library_path = fs.join(symfs_path, "data/app-lib/%s-1/%s" % (self._driver_details.package_name(), self._driver_details.library_name()))
    866         built_library_path = self._port._build_path('lib', self._driver_details.library_name())
    867         assert(fs.exists(built_library_path))
    868 
    869         # FIXME: Ideally we'd check the sha1's first and make a soft-link instead of copying (since we probably never care about windows).
    870         print "Updating symfs libary (%s) from built copy (%s)" % (symfs_library_path, built_library_path)
    871         fs.maybe_make_directory(fs.dirname(symfs_library_path))
    872         fs.copyfile(built_library_path, symfs_library_path)
    873 
    874         return symfs_path
    875 
    876     def _setup_md5sum_and_push_data_if_needed(self, log_callback):
    877         self._md5sum_path = self._port.path_to_md5sum()
    878         if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
    879             if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_PATH):
    880                 self._abort('Could not push md5sum to device')
    881 
    882         self._push_executable(log_callback)
    883         self._push_fonts(log_callback)
    884         self._push_test_resources(log_callback)
    885 
    886     def _setup_test(self, log_callback):
    887         # FIXME: Move this routine and its subroutines off of the AndroidDriver
    888         # class and onto AndroidCommands or some other helper class, so that we
    889         # can initialize the device without needing to create a driver.
    890 
    891         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
    892             return
    893 
    894         self._android_commands.restart_adb()
    895         self._android_commands.restart_as_root()
    896         self._setup_md5sum_and_push_data_if_needed(log_callback)
    897         self._setup_performance()
    898 
    899         # Required by webkit_support::GetWebKitRootDirFilePath().
    900         # Other directories will be created automatically by adb push.
    901         self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome')
    902 
    903         # Allow the test driver to get full read and write access to the directory on the device,
    904         # as well as for the FIFOs. We'll need a world writable directory.
    905         self._android_commands.mkdir(self._driver_details.device_directory(), chmod='777')
    906         self._android_commands.mkdir(self._driver_details.device_fifo_directory(), chmod='777')
    907 
    908         # Make sure that the disk cache on the device resets to a clean state.
    909         self._android_commands.run(['shell', 'rm', '-r', self._driver_details.device_cache_directory()])
    910 
    911         # Mark this device as having been set up.
    912         self._android_devices.set_device_prepared(self._android_commands.get_serial())
    913 
    914     def _log_error(self, message):
    915         _log.error('[%s] %s' % (self._android_commands.get_serial(), message))
    916 
    917     def _log_warning(self, message):
    918         _log.warning('[%s] %s' % (self._android_commands.get_serial(), message))
    919 
    920     def _log_debug(self, message):
    921         if self._debug_logging:
    922             _log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
    923 
    924     def _abort(self, message):
    925         self._device_failed = True
    926         raise driver.DeviceFailure('[%s] %s' % (self._android_commands.get_serial(), message))
    927 
    928     @staticmethod
    929     def _extract_hashes_from_md5sum_output(md5sum_output):
    930         assert md5sum_output
    931         return [line.split('  ')[0] for line in md5sum_output]
    932 
    933     def _files_match(self, host_file, device_file):
    934         assert self._port.host.filesystem.exists(host_file)
    935         device_hashes = self._extract_hashes_from_md5sum_output(
    936                 self._port.host.executive.popen(self._android_commands.adb_command() + ['shell', MD5SUM_DEVICE_PATH, device_file],
    937                                                 stdout=subprocess.PIPE).stdout)
    938         host_hashes = self._extract_hashes_from_md5sum_output(
    939                 self._port.host.executive.popen(args=['%s_host' % self._md5sum_path, host_file],
    940                                                 stdout=subprocess.PIPE).stdout)
    941         return host_hashes and device_hashes == host_hashes
    942 
    943     def _push_file_if_needed(self, host_file, device_file, log_callback):
    944         basename = self._port.host.filesystem.basename(host_file)
    945         log_callback("checking %s" % basename)
    946         if not self._files_match(host_file, device_file):
    947             log_callback("pushing %s" % basename)
    948             self._android_commands.push(host_file, device_file)
    949 
    950     def _push_executable(self, log_callback):
    951         self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path(), log_callback)
    952         for resource in self._driver_details.additional_resources():
    953             self._push_file_if_needed(self._port._build_path(resource), self._driver_details.device_directory() + resource, log_callback)
    954 
    955         self._push_file_if_needed(self._port._build_path('android_main_fonts.xml'), self._driver_details.device_directory() + 'android_main_fonts.xml', log_callback)
    956         self._push_file_if_needed(self._port._build_path('android_fallback_fonts.xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml', log_callback)
    957 
    958         log_callback("checking apk")
    959         if self._files_match(self._port._build_path('apks', 'ContentShell.apk'),
    960                              '/data/app/org.chromium.content_shell_apk-1.apk'):
    961             return
    962 
    963         log_callback("uninstalling apk")
    964         self._android_commands.run(['uninstall', self._driver_details.package_name()])
    965         driver_host_path = self._port._path_to_driver()
    966         log_callback("installing apk")
    967         install_result = self._android_commands.run(['install', driver_host_path])
    968         if install_result.find('Success') == -1:
    969             self._abort('Failed to install %s onto device: %s' % (driver_host_path, install_result))
    970 
    971     def _push_fonts(self, log_callback):
    972         path_to_ahem_font = self._port._build_path('AHEM____.TTF')
    973         self._push_file_if_needed(path_to_ahem_font, self._driver_details.device_fonts_directory() + 'AHEM____.TTF', log_callback)
    974         for (host_dirs, font_file, package) in HOST_FONT_FILES:
    975             for host_dir in host_dirs:
    976                 host_font_path = host_dir + font_file
    977                 if self._port._check_file_exists(host_font_path, '', logging=False):
    978                     self._push_file_if_needed(host_font_path, self._driver_details.device_fonts_directory() + font_file, log_callback)
    979 
    980     def _push_test_resources(self, log_callback):
    981         for resource in TEST_RESOURCES_TO_PUSH:
    982             self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource, log_callback)
    983 
    984     def _get_last_stacktrace(self):
    985         tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones/tombstone_*'])
    986         if not tombstones or tombstones.startswith('/data/tombstones/tombstone_*: No such file or directory'):
    987             self._log_error('The driver crashed, but no tombstone found!')
    988             return ''
    989 
    990         if tombstones.startswith('/data/tombstones/tombstone_*: Permission denied'):
    991             # FIXME: crbug.com/321489 ... figure out why this happens.
    992             self._log_error('The driver crashed, but we could not read the tombstones!')
    993             return ''
    994 
    995         tombstones = tombstones.rstrip().split('\n')
    996         last_tombstone = None
    997         for tombstone in tombstones:
    998             # Format of fields:
    999             # 0          1      2      3     4          5     6
   1000             # permission uid    gid    size  date       time  filename
   1001             # -rw------- 1000   1000   45859 2011-04-13 06:00 tombstone_00
   1002             fields = tombstone.split()
   1003             if len(fields) != 7:
   1004                 self._log_warning("unexpected line in tombstone output, skipping: '%s'" % tombstone)
   1005                 continue
   1006 
   1007             if not last_tombstone or fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]:
   1008                 last_tombstone = fields
   1009             else:
   1010                 break
   1011 
   1012         if not last_tombstone:
   1013             self._log_error('The driver crashed, but we could not find any valid tombstone!')
   1014             return ''
   1015 
   1016         # Use Android tool vendor/google/tools/stack to convert the raw
   1017         # stack trace into a human readable format, if needed.
   1018         # It takes a long time, so don't do it here.
   1019         return '%s\n%s' % (' '.join(last_tombstone),
   1020                            self._android_commands.run(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]]))
   1021 
   1022     def _get_logcat(self):
   1023         return self._android_commands.run(['logcat', '-d', '-v', 'threadtime'])
   1024 
   1025     def _setup_performance(self):
   1026         # Disable CPU scaling and drop ram cache to reduce noise in tests
   1027         if not self._original_governors:
   1028             governor_files = self._android_commands.run(['shell', 'ls', SCALING_GOVERNORS_PATTERN])
   1029             if governor_files.find('No such file or directory') == -1:
   1030                 for file in governor_files.split():
   1031                     self._original_governors[file] = self._android_commands.run(['shell', 'cat', file]).strip()
   1032                     self._android_commands.run(['shell', 'echo', 'performance', '>', file])
   1033 
   1034     def _teardown_performance(self):
   1035         for file, original_content in self._original_governors.items():
   1036             self._android_commands.run(['shell', 'echo', original_content, '>', file])
   1037         self._original_governors = {}
   1038 
   1039     def _get_crash_log(self, stdout, stderr, newer_than):
   1040         if not stdout:
   1041             stdout = ''
   1042         stdout += '********* [%s] Logcat:\n%s' % (self._android_commands.get_serial(), self._get_logcat())
   1043         if not stderr:
   1044             stderr = ''
   1045         stderr += '********* [%s] Tombstone file:\n%s' % (self._android_commands.get_serial(), self._get_last_stacktrace())
   1046 
   1047         if not self._port.get_option('disable_breakpad'):
   1048             crashes = self._pull_crash_dumps_from_device()
   1049             for crash in crashes:
   1050                 stderr += '********* [%s] breakpad minidump %s:\n%s' % (self._port.host.filesystem.basename(crash), self._android_commands.get_serial(), self._port._dump_reader._get_stack_from_dump(crash))
   1051 
   1052         return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
   1053 
   1054     def cmd_line(self, pixel_tests, per_test_args):
   1055         # The returned command line is used to start _server_process. In our case, it's an interactive 'adb shell'.
   1056         # The command line passed to the driver process is returned by _driver_cmd_line() instead.
   1057         return self._android_commands.adb_command() + ['shell']
   1058 
   1059     def _android_driver_cmd_line(self, pixel_tests, per_test_args):
   1060         return driver.Driver.cmd_line(self, pixel_tests, per_test_args)
   1061 
   1062     @staticmethod
   1063     def _loop_with_timeout(condition, timeout_secs):
   1064         deadline = time.time() + timeout_secs
   1065         while time.time() < deadline:
   1066             if condition():
   1067                 return True
   1068         return False
   1069 
   1070     def _all_pipes_created(self):
   1071         return (self._android_commands.file_exists(self._in_fifo_path) and
   1072                 self._android_commands.file_exists(self._out_fifo_path) and
   1073                 self._android_commands.file_exists(self._err_fifo_path))
   1074 
   1075     def _remove_all_pipes(self):
   1076         for file in [self._in_fifo_path, self._out_fifo_path, self._err_fifo_path]:
   1077             self._android_commands.run(['shell', 'rm', file])
   1078 
   1079         return (not self._android_commands.file_exists(self._in_fifo_path) and
   1080                 not self._android_commands.file_exists(self._out_fifo_path) and
   1081                 not self._android_commands.file_exists(self._err_fifo_path))
   1082 
   1083     def start(self, pixel_tests, per_test_args):
   1084         # We override the default start() so that we can call _android_driver_cmd_line()
   1085         # instead of cmd_line().
   1086         new_cmd_line = self._android_driver_cmd_line(pixel_tests, per_test_args)
   1087 
   1088         # Since _android_driver_cmd_line() is different than cmd_line() we need to provide
   1089         # our own mechanism for detecting when the process should be stopped.
   1090         if self._current_cmd_line is None:
   1091             self._current_android_cmd_line = None
   1092         if new_cmd_line != self._current_android_cmd_line:
   1093             self.stop()
   1094         self._current_android_cmd_line = new_cmd_line
   1095 
   1096         super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
   1097 
   1098     def _start(self, pixel_tests, per_test_args):
   1099         if not self._android_devices.is_device_prepared(self._android_commands.get_serial()):
   1100             raise driver.DeviceFailure("%s is not prepared in _start()" % self._android_commands.get_serial())
   1101 
   1102         for retries in range(3):
   1103             try:
   1104                 if self._start_once(pixel_tests, per_test_args):
   1105                     return
   1106             except ScriptError as e:
   1107                 self._abort('ScriptError("%s") in _start()' % str(e))
   1108 
   1109             self._log_error('Failed to start the content_shell application. Retries=%d. Log:%s' % (retries, self._get_logcat()))
   1110             self.stop()
   1111             time.sleep(2)
   1112         self._abort('Failed to start the content_shell application multiple times. Giving up.')
   1113 
   1114     def _start_once(self, pixel_tests, per_test_args):
   1115         super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args, wait_for_ready=False)
   1116 
   1117         self._log_debug('Starting forwarder')
   1118         self._forwarder_process = self._port._server_process_constructor(
   1119             self._port, 'Forwarder', self._android_commands.adb_command() + ['shell', '%s -D %s' % (self._driver_details.device_forwarder_path(), FORWARD_PORTS)])
   1120         self._forwarder_process.start()
   1121 
   1122         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
   1123         if not self._wait_for_server_process_output(self._forwarder_process, deadline, 'Forwarding device port'):
   1124             return False
   1125 
   1126         self._android_commands.run(['logcat', '-c'])
   1127 
   1128         cmd_line_file_path = self._driver_details.command_line_file()
   1129         original_cmd_line_file_path = cmd_line_file_path + '.orig'
   1130         if self._android_commands.file_exists(cmd_line_file_path) and not self._android_commands.file_exists(original_cmd_line_file_path):
   1131             # We check for both the normal path and the backup because we do not want to step
   1132             # on the backup. Otherwise, we'd clobber the backup whenever we changed the
   1133             # command line during the run.
   1134             self._android_commands.run(['shell', 'mv', cmd_line_file_path, original_cmd_line_file_path])
   1135 
   1136         self._android_commands.run(['shell', 'echo'] + self._android_driver_cmd_line(pixel_tests, per_test_args) + ['>', self._driver_details.command_line_file()])
   1137         self._created_cmd_line = True
   1138 
   1139         self._android_commands.run(['shell', 'rm', '-rf', self._driver_details.device_crash_dumps_directory()])
   1140         self._android_commands.mkdir(self._driver_details.device_crash_dumps_directory(), chmod='777')
   1141 
   1142         start_result = self._android_commands.run(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', self._driver_details.activity_name()])
   1143         if start_result.find('Exception') != -1:
   1144             self._log_error('Failed to start the content_shell application. Exception:\n' + start_result)
   1145             return False
   1146 
   1147         if not ChromiumAndroidDriver._loop_with_timeout(self._all_pipes_created, DRIVER_START_STOP_TIMEOUT_SECS):
   1148             return False
   1149 
   1150         # Read back the shell prompt to ensure adb shell ready.
   1151         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
   1152         self._server_process.start()
   1153         self._read_prompt(deadline)
   1154         self._log_debug('Interactive shell started')
   1155 
   1156         # Start a process to read from the stdout fifo of the test driver and print to stdout.
   1157         self._log_debug('Redirecting stdout to ' + self._out_fifo_path)
   1158         self._read_stdout_process = self._port._server_process_constructor(
   1159             self._port, 'ReadStdout', self._android_commands.adb_command() + ['shell', 'cat', self._out_fifo_path])
   1160         self._read_stdout_process.start()
   1161 
   1162         # Start a process to read from the stderr fifo of the test driver and print to stdout.
   1163         self._log_debug('Redirecting stderr to ' + self._err_fifo_path)
   1164         self._read_stderr_process = self._port._server_process_constructor(
   1165             self._port, 'ReadStderr', self._android_commands.adb_command() + ['shell', 'cat', self._err_fifo_path])
   1166         self._read_stderr_process.start()
   1167 
   1168         self._log_debug('Redirecting stdin to ' + self._in_fifo_path)
   1169         self._server_process.write('cat >%s\n' % self._in_fifo_path)
   1170 
   1171         # Combine the stdout and stderr pipes into self._server_process.
   1172         self._server_process.replace_outputs(self._read_stdout_process._proc.stdout, self._read_stderr_process._proc.stdout)
   1173 
   1174         def deadlock_detector(processes, normal_startup_event):
   1175             if not ChromiumAndroidDriver._loop_with_timeout(lambda: normal_startup_event.is_set(), DRIVER_START_STOP_TIMEOUT_SECS):
   1176                 # If normal_startup_event is not set in time, the main thread must be blocked at
   1177                 # reading/writing the fifo. Kill the fifo reading/writing processes to let the
   1178                 # main thread escape from the deadlocked state. After that, the main thread will
   1179                 # treat this as a crash.
   1180                 self._log_error('Deadlock detected. Processes killed.')
   1181                 for i in processes:
   1182                     i.kill()
   1183 
   1184         # Start a thread to kill the pipe reading/writing processes on deadlock of the fifos during startup.
   1185         normal_startup_event = threading.Event()
   1186         threading.Thread(name='DeadlockDetector', target=deadlock_detector,
   1187                          args=([self._server_process, self._read_stdout_process, self._read_stderr_process], normal_startup_event)).start()
   1188 
   1189         # The test driver might crash during startup or when the deadlock detector hits
   1190         # a deadlock and kills the fifo reading/writing processes.
   1191         if not self._wait_for_server_process_output(self._server_process, deadline, '#READY'):
   1192             return False
   1193 
   1194         # Inform the deadlock detector that the startup is successful without deadlock.
   1195         normal_startup_event.set()
   1196         self._log_debug("content_shell is ready")
   1197         return True
   1198 
   1199     def _pid_from_android_ps_output(self, ps_output, package_name):
   1200         # ps output seems to be fixed width, we only care about the name and the pid
   1201         # u0_a72    21630 125   947920 59364 ffffffff 400beee4 S org.chromium.native_test
   1202         for line in ps_output.split('\n'):
   1203             if line.find(self._driver_details.package_name()) != -1:
   1204                 match = re.match(r'\S+\s+(\d+)', line)
   1205                 return int(match.group(1))
   1206 
   1207     def _pid_on_target(self):
   1208         # FIXME: There must be a better way to do this than grepping ps output!
   1209         ps_output = self._android_commands.run(['shell', 'ps'])
   1210         return self._pid_from_android_ps_output(ps_output, self._driver_details.package_name())
   1211 
   1212     def stop(self):
   1213         if not self._device_failed:
   1214             # Do not try to stop the application if there's something wrong with the device; adb may hang.
   1215             # FIXME: crbug.com/305040. Figure out if it's really hanging (and why).
   1216             self._android_commands.run(['shell', 'am', 'force-stop', self._driver_details.package_name()])
   1217 
   1218         if self._read_stdout_process:
   1219             self._read_stdout_process.kill()
   1220             self._read_stdout_process = None
   1221 
   1222         if self._read_stderr_process:
   1223             self._read_stderr_process.kill()
   1224             self._read_stderr_process = None
   1225 
   1226         super(ChromiumAndroidDriver, self).stop()
   1227 
   1228         if self._forwarder_process:
   1229             self._forwarder_process.kill()
   1230             self._forwarder_process = None
   1231 
   1232         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
   1233             if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRIVER_START_STOP_TIMEOUT_SECS):
   1234                 self._abort('Failed to remove fifo files. May be locked.')
   1235 
   1236         self._clean_up_cmd_line()
   1237 
   1238     def _pull_crash_dumps_from_device(self):
   1239         result = []
   1240         if not self._android_commands.file_exists(self._driver_details.device_crash_dumps_directory()):
   1241             return result
   1242         dumps = self._android_commands.run(['shell', 'ls', self._driver_details.device_crash_dumps_directory()])
   1243         for dump in dumps.splitlines():
   1244             device_dump = '%s/%s' % (self._driver_details.device_crash_dumps_directory(), dump)
   1245             local_dump = self._port._filesystem.join(self._port._dump_reader.crash_dumps_directory(), dump)
   1246 
   1247             # FIXME: crbug.com/321489. Figure out why these commands would fail ...
   1248             err = self._android_commands.run(['shell', 'chmod', '777', device_dump])
   1249             if not err:
   1250                 self._android_commands.pull(device_dump, local_dump)
   1251             if not err:
   1252                 self._android_commands.run(['shell', 'rm', '-f', device_dump])
   1253 
   1254             if self._port._filesystem.exists(local_dump):
   1255                 result.append(local_dump)
   1256         return result
   1257 
   1258     def _clean_up_cmd_line(self):
   1259         if not self._created_cmd_line:
   1260             return
   1261 
   1262         cmd_line_file_path = self._driver_details.command_line_file()
   1263         original_cmd_line_file_path = cmd_line_file_path + '.orig'
   1264         if self._android_commands.file_exists(original_cmd_line_file_path):
   1265             self._android_commands.run(['shell', 'mv', original_cmd_line_file_path, cmd_line_file_path])
   1266         elif self._android_commands.file_exists(cmd_line_file_path):
   1267             self._android_commands.run(['shell', 'rm', cmd_line_file_path])
   1268         self._created_cmd_line = False
   1269 
   1270     def _command_from_driver_input(self, driver_input):
   1271         command = super(ChromiumAndroidDriver, self)._command_from_driver_input(driver_input)
   1272         if command.startswith('/'):
   1273             fs = self._port._filesystem
   1274             # FIXME: what happens if command lies outside of the layout_tests_dir on the host?
   1275             relative_test_filename = fs.relpath(command, fs.dirname(self._port.layout_tests_dir()))
   1276             command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename
   1277         return command
   1278 
   1279     def _read_prompt(self, deadline):
   1280         last_char = ''
   1281         while True:
   1282             current_char = self._server_process.read_stdout(deadline, 1)
   1283             if current_char == ' ':
   1284                 if last_char in ('#', '$'):
   1285                     return
   1286             last_char = current_char
   1287