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', 'content_shell.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         # Also make sure that the output is ascii-encoded to avoid confusing other parts of
    261         # the system.
    262         self._log_debug('Run adb result: ' + result[:80].encode('ascii', errors='replace'))
    263         return result
    264 
    265     def get_serial(self):
    266         return self._device_serial
    267 
    268     def adb_command(self):
    269         return [AndroidCommands.adb_command_path(self._executive, self._debug_logging), '-s', self._device_serial]
    270 
    271     @staticmethod
    272     def set_adb_command_path_options(paths):
    273         AndroidCommands._adb_command_path_options = paths
    274 
    275     @staticmethod
    276     def adb_command_path(executive, debug_logging):
    277         if AndroidCommands._adb_command_path:
    278             return AndroidCommands._adb_command_path
    279 
    280         assert AndroidCommands._adb_command_path_options, 'No commands paths have been set to look for the "adb" command.'
    281 
    282         command_path = None
    283         command_version = None
    284         for path_option in AndroidCommands._adb_command_path_options:
    285             path_version = AndroidCommands._determine_adb_version(path_option, executive, debug_logging)
    286             if not path_version:
    287                 continue
    288             if command_version != None and path_version < command_version:
    289                 continue
    290 
    291             command_path = path_option
    292             command_version = path_version
    293 
    294         assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?'
    295 
    296         AndroidCommands._adb_command_path = command_path
    297         return command_path
    298 
    299     # Local private methods.
    300 
    301     def _log_error(self, message):
    302         _log.error('[%s] %s' % (self._device_serial, message))
    303 
    304     def _log_info(self, message):
    305         _log.info('[%s] %s' % (self._device_serial, message))
    306 
    307     def _log_debug(self, message):
    308         if self._debug_logging:
    309             _log.debug('[%s] %s' % (self._device_serial, message))
    310 
    311     @staticmethod
    312     def _determine_adb_version(adb_command_path, executive, debug_logging):
    313         re_version = re.compile('^.*version ([\d\.]+)$')
    314         try:
    315             output = executive.run_command([adb_command_path, 'version'], error_handler=executive.ignore_error,
    316                                            debug_logging=debug_logging)
    317         except OSError:
    318             return None
    319 
    320         result = re_version.match(output)
    321         if not output or not result:
    322             return None
    323 
    324         return [int(n) for n in result.group(1).split('.')]
    325 
    326 
    327 # A class to encapsulate device status and information, such as the AndroidCommands
    328 # instances and whether the device has been set up.
    329 class AndroidDevices(object):
    330     # Percentage of battery a device needs to have in order for it to be considered
    331     # to participate in running the layout tests.
    332     MINIMUM_BATTERY_PERCENTAGE = 30
    333 
    334     def __init__(self, executive, default_device=None, debug_logging=False):
    335         self._usable_devices = []
    336         self._default_device = default_device
    337         self._prepared_devices = []
    338         self._debug_logging = debug_logging
    339 
    340     def prepared_devices(self):
    341         return self._prepared_devices
    342 
    343     def usable_devices(self, executive):
    344         if self._usable_devices:
    345             return self._usable_devices
    346 
    347         if self._default_device:
    348             self._usable_devices = [AndroidCommands(executive, self._default_device, self._debug_logging)]
    349             return self._usable_devices
    350 
    351         # Example "adb devices" command output:
    352         #   List of devices attached
    353         #   0123456789ABCDEF        device
    354         re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
    355 
    356         result = executive.run_command([AndroidCommands.adb_command_path(executive, debug_logging=self._debug_logging), 'devices'],
    357                                        error_handler=executive.ignore_error, debug_logging=self._debug_logging)
    358         devices = re_device.findall(result)
    359         if not devices:
    360             return []
    361 
    362         for device_serial in sorted(devices):
    363             commands = AndroidCommands(executive, device_serial, self._debug_logging)
    364             if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM_BATTERY_PERCENTAGE:
    365                 _log.warning('Device with serial "%s" skipped because it has less than %d percent battery.'
    366                     % (commands.get_serial(), AndroidDevices.MINIMUM_BATTERY_PERCENTAGE))
    367                 continue
    368 
    369             if not self._is_device_screen_on(commands):
    370                 _log.warning('Device with serial "%s" skipped because the screen must be on.' % commands.get_serial())
    371                 continue
    372 
    373             self._usable_devices.append(commands)
    374 
    375         return self._usable_devices
    376 
    377     def get_device(self, executive, device_index):
    378         devices = self.usable_devices(executive)
    379         if device_index >= len(devices):
    380             raise AssertionError('Device index exceeds number of usable devices.')
    381 
    382         return devices[device_index]
    383 
    384     def is_device_prepared(self, device_serial):
    385         return device_serial in self._prepared_devices
    386 
    387     def set_device_prepared(self, device_serial):
    388         self._prepared_devices.append(device_serial)
    389 
    390     # Private methods
    391     def _battery_level_for_device(self, commands):
    392         battery_status = commands.run(['shell', 'dumpsys', 'battery'])
    393         if 'Error' in battery_status or "Can't find service: battery" in battery_status:
    394             _log.warning('Unable to read the battery level from device with serial "%s".' % commands.get_serial())
    395             return 0
    396 
    397         return int(re.findall('level: (\d+)', battery_status)[0])
    398 
    399     def _is_device_screen_on(self, commands):
    400         power_status = commands.run(['shell', 'dumpsys', 'power'])
    401         return 'mScreenOn=true' in power_status or 'mScreenOn=SCREEN_ON_BIT' in power_status or 'Display Power: state=ON' in power_status
    402 
    403 
    404 class AndroidPort(base.Port):
    405     port_name = 'android'
    406 
    407     # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
    408     _adb_path = None
    409 
    410     SUPPORTED_VERSIONS = ('android')
    411 
    412     FALLBACK_PATHS = {'icecreamsandwich': ['android'] + linux.LinuxPort.latest_platform_fallback_path()}
    413 
    414     # Android has aac and mp3 codecs built in.
    415     PORT_HAS_AUDIO_CODECS_BUILT_IN = True
    416 
    417     BUILD_REQUIREMENTS_URL = 'https://code.google.com/p/chromium/wiki/AndroidBuildInstructions'
    418 
    419     def __init__(self, host, port_name, **kwargs):
    420         super(AndroidPort, self).__init__(host, port_name, **kwargs)
    421 
    422         self._operating_system = 'android'
    423         self._version = 'icecreamsandwich'
    424 
    425         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
    426         self._server_process_constructor = self._android_server_process_constructor
    427 
    428         if not self.get_option('disable_breakpad'):
    429             self._dump_reader = DumpReaderAndroid(host, self._build_path())
    430 
    431         if self.driver_name() != self.CONTENT_SHELL_NAME:
    432             raise AssertionError('Layout tests on Android only support content_shell as the driver.')
    433 
    434         self._driver_details = ContentShellDriverDetails()
    435 
    436         # Initialize the AndroidDevices class which tracks available devices.
    437         default_device = None
    438         if hasattr(self._options, 'adb_device') and len(self._options.adb_device):
    439             default_device = self._options.adb_device
    440 
    441         self._debug_logging = self.get_option('android_logging')
    442         self._devices = AndroidDevices(self._executive, default_device, self._debug_logging)
    443 
    444         # Tell AndroidCommands where to search for the "adb" command.
    445         AndroidCommands.set_adb_command_path_options(['adb',
    446             self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')])
    447 
    448         prepared_devices = self.get_option('prepared_devices', [])
    449         for serial in prepared_devices:
    450             self._devices.set_device_prepared(serial)
    451 
    452     def default_smoke_test_only(self):
    453         return True
    454 
    455     # Local public methods.
    456     def path_to_forwarder(self):
    457         return self._build_path('forwarder')
    458 
    459     def path_to_md5sum(self):
    460         return self._build_path(MD5SUM_DEVICE_FILE_NAME)
    461 
    462     def path_to_md5sum_host(self):
    463         return self._build_path(MD5SUM_HOST_FILE_NAME)
    464 
    465     def additional_drt_flag(self):
    466         return self._driver_details.additional_command_line_flags(use_breakpad=not self.get_option('disable_breakpad'))
    467 
    468     def default_timeout_ms(self):
    469         # Android platform has less computing power than desktop platforms.
    470         # Using 10 seconds allows us to pass most slow tests which are not
    471         # marked as slow tests on desktop platforms.
    472         return 10 * 1000
    473 
    474     def driver_stop_timeout(self):
    475         # The driver doesn't respond to closing stdin, so we might as well stop the driver immediately.
    476         return 0.0
    477 
    478     def default_child_processes(self):
    479         usable_devices = self._devices.usable_devices(self._executive)
    480         if not usable_devices:
    481             raise test_run_results.TestRunException(test_run_results.NO_DEVICES_EXIT_STATUS, "Unable to find any attached Android devices.")
    482         return len(usable_devices)
    483 
    484     def check_wdiff(self, logging=True):
    485         return self._host_port.check_wdiff(logging)
    486 
    487     def check_build(self, needs_http, printer):
    488         exit_status = super(AndroidPort, self).check_build(needs_http, printer)
    489         if exit_status:
    490             return exit_status
    491 
    492         result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility')
    493         result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result
    494         result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result
    495 
    496         if not result:
    497             # There is a race condition in adb at least <= 4.3 on Linux that causes it to go offline periodically
    498             # We set the processor affinity for any running adb process to attempt to work around this.
    499             # See crbug.com/268450
    500             if self.host.platform.is_linux():
    501                 pids = self._executive.running_pids(lambda name: 'adb' in name)
    502                 if not pids:
    503                     # Apparently adb is not running, which is unusual. Running any adb command should start it.
    504                     self._executive.run_command(['adb', 'devices'])
    505                     pids = self._executive.running_pids(lambda name: 'adb' in name)
    506                 if not pids:
    507                     _log.error("The adb daemon does not appear to be running.")
    508                     return False
    509 
    510                 for pid in pids:
    511                     self._executive.run_command(['taskset', '-p', '-c', '0', str(pid)])
    512 
    513         if not result:
    514             _log.error('For complete Android build requirements, please see:')
    515             _log.error('')
    516             _log.error('    http://code.google.com/p/chromium/wiki/AndroidBuildInstructions')
    517             return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
    518 
    519         return self._check_devices(printer)
    520 
    521     def _check_devices(self, printer):
    522         # Printer objects aren't threadsafe, so we need to protect calls to them.
    523         lock = threading.Lock()
    524         pool = None
    525 
    526         # Push the executables and other files to the devices; doing this now
    527         # means we can do this in parallel in the manager process and not mix
    528         # this in with starting and stopping workers.
    529         def setup_device(worker_number):
    530             d = self.create_driver(worker_number)
    531             serial = d._android_commands.get_serial()
    532 
    533             def log_safely(msg, throttled=True):
    534                 if throttled:
    535                     callback = printer.write_throttled_update
    536                 else:
    537                     callback = printer.write_update
    538                 lock.acquire()
    539                 try:
    540                     callback("[%s] %s" % (serial, msg))
    541                 finally:
    542                     lock.release()
    543 
    544             log_safely("preparing device", throttled=False)
    545             try:
    546                 d._setup_test(log_safely)
    547                 log_safely("device prepared", throttled=False)
    548             except (ScriptError, driver.DeviceFailure) as e:
    549                 lock.acquire()
    550                 _log.warning("[%s] failed to prepare_device: %s" % (serial, str(e)))
    551                 lock.release()
    552             except KeyboardInterrupt:
    553                 if pool:
    554                     pool.terminate()
    555 
    556         # FIXME: It would be nice if we knew how many workers we needed.
    557         num_workers = self.default_child_processes()
    558         num_child_processes = int(self.get_option('child_processes'))
    559         if num_child_processes:
    560             num_workers = min(num_workers, num_child_processes)
    561         if num_workers > 1:
    562             pool = ThreadPool(num_workers)
    563             try:
    564                 pool.map(setup_device, range(num_workers))
    565             except KeyboardInterrupt:
    566                 pool.terminate()
    567                 raise
    568         else:
    569             setup_device(0)
    570 
    571         if not self._devices.prepared_devices():
    572             _log.error('Could not prepare any devices for testing.')
    573             return test_run_results.NO_DEVICES_EXIT_STATUS
    574         return test_run_results.OK_EXIT_STATUS
    575 
    576     def setup_test_run(self):
    577         super(AndroidPort, self).setup_test_run()
    578 
    579         # By setting this on the options object, we can propagate the list
    580         # of prepared devices to the workers (it is read in __init__()).
    581         if self._devices._prepared_devices:
    582             self._options.prepared_devices = self._devices.prepared_devices()
    583         else:
    584             # We were called with --no-build, so assume the devices are up to date.
    585             self._options.prepared_devices = [d.get_serial() for d in self._devices.usable_devices(self.host.executive)]
    586 
    587     def num_workers(self, requested_num_workers):
    588         return min(len(self._options.prepared_devices), requested_num_workers)
    589 
    590     def check_sys_deps(self, needs_http):
    591         for (font_dirs, font_file, package) in HOST_FONT_FILES:
    592             exists = False
    593             for font_dir in font_dirs:
    594                 font_path = font_dir + font_file
    595                 if self._check_file_exists(font_path, '', logging=False):
    596                     exists = True
    597                     break
    598             if not exists:
    599                 _log.error('You are missing %s under %s. Try installing %s. See build instructions.' % (font_file, font_dirs, package))
    600                 return test_run_results.SYS_DEPS_EXIT_STATUS
    601         return test_run_results.OK_EXIT_STATUS
    602 
    603     def requires_http_server(self):
    604         """Chromium Android runs tests on devices, and uses the HTTP server to
    605         serve the actual layout tests to the test driver."""
    606         return True
    607 
    608     def start_http_server(self, additional_dirs, number_of_drivers):
    609         additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir()
    610         additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
    611         super(AndroidPort, self).start_http_server(additional_dirs, number_of_drivers)
    612 
    613     def create_driver(self, worker_number, no_timeout=False):
    614         return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_option('pixel_tests'),
    615                                      driver_details=self._driver_details,
    616                                      android_devices=self._devices,
    617                                      # Force no timeout to avoid test driver timeouts before NRWT.
    618                                      no_timeout=True)
    619 
    620     def driver_cmd_line(self):
    621         # Override to return the actual test driver's command line.
    622         return self.create_driver(0)._android_driver_cmd_line(self.get_option('pixel_tests'), [])
    623 
    624     def clobber_old_port_specific_results(self):
    625         if not self.get_option('disable_breakpad'):
    626             self._dump_reader.clobber_old_results()
    627 
    628     # Overridden protected methods.
    629 
    630     def _build_path(self, *comps):
    631         return self._host_port._build_path(*comps)
    632 
    633     def _build_path_with_configuration(self, configuration, *comps):
    634         return self._host_port._build_path_with_configuration(configuration, *comps)
    635 
    636     def path_to_apache(self):
    637         return self._host_port.path_to_apache()
    638 
    639     def path_to_apache_config_file(self):
    640         return self._host_port.path_to_apache_config_file()
    641 
    642     def _path_to_driver(self, configuration=None):
    643         return self._build_path_with_configuration(configuration, self._driver_details.apk_name())
    644 
    645     def _path_to_helper(self):
    646         return None
    647 
    648     def _path_to_image_diff(self):
    649         return self._host_port._path_to_image_diff()
    650 
    651     def _path_to_wdiff(self):
    652         return self._host_port._path_to_wdiff()
    653 
    654     def _shut_down_http_server(self, pid):
    655         return self._host_port._shut_down_http_server(pid)
    656 
    657     def _driver_class(self):
    658         return ChromiumAndroidDriver
    659 
    660     # Local private methods.
    661 
    662     @staticmethod
    663     def _android_server_process_constructor(port, server_name, cmd_line, env=None, logging=False):
    664         return server_process.ServerProcess(port, server_name, cmd_line, env,
    665                                             universal_newlines=True, treat_no_data_as_crash=True, logging=logging)
    666 
    667 
    668 class AndroidPerf(SingleFileOutputProfiler):
    669     _cached_perf_host_path = None
    670     _have_searched_for_perf_host = False
    671 
    672     def __init__(self, host, executable_path, output_dir, android_commands, symfs_path, kallsyms_path, identifier=None):
    673         super(AndroidPerf, self).__init__(host, executable_path, output_dir, "data", identifier)
    674         self._android_commands = android_commands
    675         self._perf_process = None
    676         self._symfs_path = symfs_path
    677         self._kallsyms_path = kallsyms_path
    678 
    679     def check_configuration(self):
    680         # Check that perf is installed
    681         if not self._android_commands.file_exists('/system/bin/perf'):
    682             print "Cannot find /system/bin/perf on device %s" % self._android_commands.get_serial()
    683             return False
    684 
    685         # Check that the device is a userdebug build (or at least has the necessary libraries).
    686         if self._android_commands.run(['shell', 'getprop', 'ro.build.type']).strip() != 'userdebug':
    687             print "Device %s is not flashed with a userdebug build of Android" % self._android_commands.get_serial()
    688             return False
    689 
    690         # FIXME: Check that the binary actually is perf-able (has stackframe pointers)?
    691         # objdump -s a function and make sure it modifies the fp?
    692         # Instruct users to rebuild after export GYP_DEFINES="profiling=1 $GYP_DEFINES"
    693         return True
    694 
    695     def print_setup_instructions(self):
    696         print """
    697 perf on android requires a 'userdebug' build of Android, see:
    698 http://source.android.com/source/building-devices.html"
    699 
    700 The perf command can be built from:
    701 https://android.googlesource.com/platform/external/linux-tools-perf/
    702 and requires libefl, libebl, libdw, and libdwfl available in:
    703 https://android.googlesource.com/platform/external/elfutils/
    704 
    705 The test driver must be built with profiling=1, make sure you've done:
    706 export GYP_DEFINES="profiling=1 $GYP_DEFINES"
    707 update-webkit --chromium-android
    708 build-webkit --chromium-android
    709 
    710 Googlers should read:
    711 http://goto.google.com/cr-android-perf-howto
    712 """
    713 
    714     def attach_to_pid(self, pid):
    715         assert(pid)
    716         assert(self._perf_process == None)
    717         # FIXME: This can't be a fixed timeout!
    718         cmd = self._android_commands.adb_command() + ['shell', 'perf', 'record', '-g', '-p', pid, 'sleep', 30]
    719         self._perf_process = self._host.executive.popen(cmd)
    720 
    721     def _perf_version_string(self, perf_path):
    722         try:
    723             return self._host.executive.run_command([perf_path, '--version'])
    724         except:
    725             return None
    726 
    727     def _find_perfhost_binary(self):
    728         perfhost_version = self._perf_version_string('perfhost_linux')
    729         if perfhost_version:
    730             return 'perfhost_linux'
    731         perf_version = self._perf_version_string('perf')
    732         if perf_version:
    733             return 'perf'
    734         return None
    735 
    736     def _perfhost_path(self):
    737         if self._have_searched_for_perf_host:
    738             return self._cached_perf_host_path
    739         self._have_searched_for_perf_host = True
    740         self._cached_perf_host_path = self._find_perfhost_binary()
    741         return self._cached_perf_host_path
    742 
    743     def _first_ten_lines_of_profile(self, perf_output):
    744         match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MULTILINE)
    745         return match.group(1) if match else None
    746 
    747     def profile_after_exit(self):
    748         perf_exitcode = self._perf_process.wait()
    749         if perf_exitcode != 0:
    750             print "Perf failed (exit code: %i), can't process results." % perf_exitcode
    751             return
    752 
    753         self._android_commands.pull('/data/perf.data', self._output_path)
    754 
    755         perfhost_path = self._perfhost_path()
    756         perfhost_report_command = [
    757             'report',
    758             '--input', self._output_path,
    759             '--symfs', self._symfs_path,
    760             '--kallsyms', self._kallsyms_path,
    761         ]
    762         if perfhost_path:
    763             perfhost_args = [perfhost_path] + perfhost_report_command + ['--call-graph', 'none']
    764             perf_output = self._host.executive.run_command(perfhost_args)
    765             # We could save off the full -g report to a file if users found that useful.
    766             print self._first_ten_lines_of_profile(perf_output)
    767         else:
    768             print """
    769 Failed to find perfhost_linux binary, can't process samples from the device.
    770 
    771 perfhost_linux can be built from:
    772 https://android.googlesource.com/platform/external/linux-tools-perf/
    773 also, modern versions of perf (available from apt-get install goobuntu-kernel-tools-common)
    774 may also be able to process the perf.data files from the device.
    775 
    776 Googlers should read:
    777 http://goto.google.com/cr-android-perf-howto
    778 for instructions on installing pre-built copies of perfhost_linux
    779 http://crbug.com/165250 discusses making these pre-built binaries externally available.
    780 """
    781 
    782         perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_linux'
    783         print "To view the full profile, run:"
    784         print ' '.join([perfhost_display_patch] + perfhost_report_command)
    785 
    786 
    787 class ChromiumAndroidDriver(driver.Driver):
    788     def __init__(self, port, worker_number, pixel_tests, driver_details, android_devices, no_timeout=False):
    789         super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_tests, no_timeout)
    790         self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fifo'
    791         self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fifo'
    792         self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.fifo'
    793         self._read_stdout_process = None
    794         self._read_stderr_process = None
    795         self._forwarder_process = None
    796         self._original_governors = {}
    797         self._original_kptr_restrict = None
    798 
    799         self._android_devices = android_devices
    800         self._android_commands = android_devices.get_device(port._executive, worker_number)
    801         self._driver_details = driver_details
    802         self._debug_logging = self._port._debug_logging
    803         self._created_cmd_line = False
    804         self._device_failed = False
    805 
    806         # FIXME: If we taught ProfileFactory about "target" devices we could
    807         # just use the logic in Driver instead of duplicating it here.
    808         if self._port.get_option("profile"):
    809             # FIXME: This should be done once, instead of per-driver!
    810             symfs_path = self._find_or_create_symfs()
    811             kallsyms_path = self._update_kallsyms_cache(symfs_path)
    812             # FIXME: We should pass this some sort of "Bridge" object abstraction around ADB instead of a path/device pair.
    813             self._profiler = AndroidPerf(self._port.host, self._port._path_to_driver(), self._port.results_directory(),
    814                 self._android_commands, symfs_path, kallsyms_path)
    815             # FIXME: This is a layering violation and should be moved to Port.check_sys_deps
    816             # once we have an abstraction around an adb_path/device_serial pair to make it
    817             # easy to make these class methods on AndroidPerf.
    818             if not self._profiler.check_configuration():
    819                 self._profiler.print_setup_instructions()
    820                 sys.exit(1)
    821         else:
    822             self._profiler = None
    823 
    824     def __del__(self):
    825         self._teardown_performance()
    826         self._clean_up_cmd_line()
    827         super(ChromiumAndroidDriver, self).__del__()
    828 
    829     def _update_kallsyms_cache(self, output_dir):
    830         kallsyms_name = "%s-kallsyms" % self._android_commands.get_serial()
    831         kallsyms_cache_path = self._port.host.filesystem.join(output_dir, kallsyms_name)
    832 
    833         self._android_commands.restart_as_root()
    834 
    835         saved_kptr_restrict = self._android_commands.run(['shell', 'cat', KPTR_RESTRICT_PATH]).strip()
    836         self._android_commands.run(['shell', 'echo', '0', '>', KPTR_RESTRICT_PATH])
    837 
    838         print "Updating kallsyms file (%s) from device" % kallsyms_cache_path
    839         self._android_commands.pull("/proc/kallsyms", kallsyms_cache_path)
    840 
    841         self._android_commands.run(['shell', 'echo', saved_kptr_restrict, '>', KPTR_RESTRICT_PATH])
    842 
    843         return kallsyms_cache_path
    844 
    845     def _find_or_create_symfs(self):
    846         environment = self._port.host.copy_current_environment()
    847         env = environment.to_dictionary()
    848         fs = self._port.host.filesystem
    849 
    850         if 'ANDROID_SYMFS' in env:
    851             symfs_path = env['ANDROID_SYMFS']
    852         else:
    853             symfs_path = fs.join(self._port.results_directory(), 'symfs')
    854             print "ANDROID_SYMFS not set, using %s" % symfs_path
    855 
    856         # find the installed path, and the path of the symboled built library
    857         # FIXME: We should get the install path from the device!
    858         symfs_library_path = fs.join(symfs_path, "data/app-lib/%s-1/%s" % (self._driver_details.package_name(), self._driver_details.library_name()))
    859         built_library_path = self._port._build_path('lib', self._driver_details.library_name())
    860         assert(fs.exists(built_library_path))
    861 
    862         # FIXME: Ideally we'd check the sha1's first and make a soft-link instead of copying (since we probably never care about windows).
    863         print "Updating symfs libary (%s) from built copy (%s)" % (symfs_library_path, built_library_path)
    864         fs.maybe_make_directory(fs.dirname(symfs_library_path))
    865         fs.copyfile(built_library_path, symfs_library_path)
    866 
    867         return symfs_path
    868 
    869     def _setup_md5sum_and_push_data_if_needed(self, log_callback):
    870         self._md5sum_path = self._port.path_to_md5sum()
    871         if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
    872             if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_PATH):
    873                 self._abort('Could not push md5sum to device')
    874 
    875         self._push_executable(log_callback)
    876         self._push_fonts(log_callback)
    877         self._push_test_resources(log_callback)
    878 
    879     def _setup_test(self, log_callback):
    880         # FIXME: Move this routine and its subroutines off of the AndroidDriver
    881         # class and onto AndroidCommands or some other helper class, so that we
    882         # can initialize the device without needing to create a driver.
    883 
    884         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
    885             return
    886 
    887         self._android_commands.restart_adb()
    888         self._android_commands.restart_as_root()
    889         self._setup_md5sum_and_push_data_if_needed(log_callback)
    890         self._setup_performance()
    891 
    892         # Required by webkit_support::GetWebKitRootDirFilePath().
    893         # Other directories will be created automatically by adb push.
    894         self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome')
    895 
    896         # Allow the test driver to get full read and write access to the directory on the device,
    897         # as well as for the FIFOs. We'll need a world writable directory.
    898         self._android_commands.mkdir(self._driver_details.device_directory(), chmod='777')
    899         self._android_commands.mkdir(self._driver_details.device_fifo_directory(), chmod='777')
    900 
    901         # Make sure that the disk cache on the device resets to a clean state.
    902         self._android_commands.run(['shell', 'rm', '-r', self._driver_details.device_cache_directory()])
    903 
    904         # Mark this device as having been set up.
    905         self._android_devices.set_device_prepared(self._android_commands.get_serial())
    906 
    907     def _log_error(self, message):
    908         _log.error('[%s] %s' % (self._android_commands.get_serial(), message))
    909 
    910     def _log_warning(self, message):
    911         _log.warning('[%s] %s' % (self._android_commands.get_serial(), message))
    912 
    913     def _log_debug(self, message):
    914         if self._debug_logging:
    915             _log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
    916 
    917     def _abort(self, message):
    918         self._device_failed = True
    919         raise driver.DeviceFailure('[%s] %s' % (self._android_commands.get_serial(), message))
    920 
    921     @staticmethod
    922     def _extract_hashes_from_md5sum_output(md5sum_output):
    923         assert md5sum_output
    924         return [line.split('  ')[0] for line in md5sum_output]
    925 
    926     def _files_match(self, host_file, device_file):
    927         assert self._port.host.filesystem.exists(host_file)
    928         device_hashes = self._extract_hashes_from_md5sum_output(
    929                 self._port.host.executive.popen(self._android_commands.adb_command() + ['shell', MD5SUM_DEVICE_PATH, device_file],
    930                                                 stdout=subprocess.PIPE).stdout)
    931         host_hashes = self._extract_hashes_from_md5sum_output(
    932                 self._port.host.executive.popen(args=['%s_host' % self._md5sum_path, host_file],
    933                                                 stdout=subprocess.PIPE).stdout)
    934         return host_hashes and device_hashes == host_hashes
    935 
    936     def _push_file_if_needed(self, host_file, device_file, log_callback):
    937         basename = self._port.host.filesystem.basename(host_file)
    938         log_callback("checking %s" % basename)
    939         if not self._files_match(host_file, device_file):
    940             log_callback("pushing %s" % basename)
    941             self._android_commands.push(host_file, device_file)
    942 
    943     def _push_executable(self, log_callback):
    944         self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path(), log_callback)
    945         for resource in self._driver_details.additional_resources():
    946             self._push_file_if_needed(self._port._build_path(resource), self._driver_details.device_directory() + resource, log_callback)
    947 
    948         self._push_file_if_needed(self._port._build_path('android_main_fonts.xml'), self._driver_details.device_directory() + 'android_main_fonts.xml', log_callback)
    949         self._push_file_if_needed(self._port._build_path('android_fallback_fonts.xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml', log_callback)
    950 
    951         log_callback("checking apk")
    952         if self._files_match(self._port._build_path('apks', 'ContentShell.apk'),
    953                              '/data/app/org.chromium.content_shell_apk-1.apk'):
    954             return
    955 
    956         log_callback("uninstalling apk")
    957         self._android_commands.run(['uninstall', self._driver_details.package_name()])
    958         driver_host_path = self._port._path_to_driver()
    959         log_callback("installing apk")
    960         install_result = self._android_commands.run(['install', driver_host_path])
    961         if install_result.find('Success') == -1:
    962             self._abort('Failed to install %s onto device: %s' % (driver_host_path, install_result))
    963 
    964     def _push_fonts(self, log_callback):
    965         path_to_ahem_font = self._port._build_path('AHEM____.TTF')
    966         self._push_file_if_needed(path_to_ahem_font, self._driver_details.device_fonts_directory() + 'AHEM____.TTF', log_callback)
    967         for (host_dirs, font_file, package) in HOST_FONT_FILES:
    968             for host_dir in host_dirs:
    969                 host_font_path = host_dir + font_file
    970                 if self._port._check_file_exists(host_font_path, '', logging=False):
    971                     self._push_file_if_needed(host_font_path, self._driver_details.device_fonts_directory() + font_file, log_callback)
    972 
    973     def _push_test_resources(self, log_callback):
    974         for resource in TEST_RESOURCES_TO_PUSH:
    975             self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource, log_callback)
    976 
    977     def _get_last_stacktrace(self):
    978         tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones/tombstone_*'])
    979         if not tombstones or tombstones.startswith('/data/tombstones/tombstone_*: No such file or directory'):
    980             self._log_error('The driver crashed, but no tombstone found!')
    981             return ''
    982 
    983         if tombstones.startswith('/data/tombstones/tombstone_*: Permission denied'):
    984             # FIXME: crbug.com/321489 ... figure out why this happens.
    985             self._log_error('The driver crashed, but we could not read the tombstones!')
    986             return ''
    987 
    988         tombstones = tombstones.rstrip().split('\n')
    989         last_tombstone = None
    990         for tombstone in tombstones:
    991             # Format of fields:
    992             # 0          1      2      3     4          5     6
    993             # permission uid    gid    size  date       time  filename
    994             # -rw------- 1000   1000   45859 2011-04-13 06:00 tombstone_00
    995             fields = tombstone.split()
    996             if len(fields) != 7:
    997                 self._log_warning("unexpected line in tombstone output, skipping: '%s'" % tombstone)
    998                 continue
    999 
   1000             if not last_tombstone or fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]:
   1001                 last_tombstone = fields
   1002             else:
   1003                 break
   1004 
   1005         if not last_tombstone:
   1006             self._log_error('The driver crashed, but we could not find any valid tombstone!')
   1007             return ''
   1008 
   1009         # Use Android tool vendor/google/tools/stack to convert the raw
   1010         # stack trace into a human readable format, if needed.
   1011         # It takes a long time, so don't do it here.
   1012         return '%s\n%s' % (' '.join(last_tombstone),
   1013                            self._android_commands.run(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]]))
   1014 
   1015     def _get_logcat(self):
   1016         return self._android_commands.run(['logcat', '-d', '-v', 'threadtime'])
   1017 
   1018     def _setup_performance(self):
   1019         # Disable CPU scaling and drop ram cache to reduce noise in tests
   1020         if not self._original_governors:
   1021             governor_files = self._android_commands.run(['shell', 'ls', SCALING_GOVERNORS_PATTERN])
   1022             if governor_files.find('No such file or directory') == -1:
   1023                 for file in governor_files.split():
   1024                     self._original_governors[file] = self._android_commands.run(['shell', 'cat', file]).strip()
   1025                     self._android_commands.run(['shell', 'echo', 'performance', '>', file])
   1026 
   1027     def _teardown_performance(self):
   1028         for file, original_content in self._original_governors.items():
   1029             self._android_commands.run(['shell', 'echo', original_content, '>', file])
   1030         self._original_governors = {}
   1031 
   1032     def _get_crash_log(self, stdout, stderr, newer_than):
   1033         if not stdout:
   1034             stdout = ''
   1035         stdout += '********* [%s] Logcat:\n%s' % (self._android_commands.get_serial(), self._get_logcat())
   1036         if not stderr:
   1037             stderr = ''
   1038         stderr += '********* [%s] Tombstone file:\n%s' % (self._android_commands.get_serial(), self._get_last_stacktrace())
   1039 
   1040         if not self._port.get_option('disable_breakpad'):
   1041             crashes = self._pull_crash_dumps_from_device()
   1042             for crash in crashes:
   1043                 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))
   1044 
   1045         return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
   1046 
   1047     def cmd_line(self, pixel_tests, per_test_args):
   1048         # The returned command line is used to start _server_process. In our case, it's an interactive 'adb shell'.
   1049         # The command line passed to the driver process is returned by _driver_cmd_line() instead.
   1050         return self._android_commands.adb_command() + ['shell']
   1051 
   1052     def _android_driver_cmd_line(self, pixel_tests, per_test_args):
   1053         return driver.Driver.cmd_line(self, pixel_tests, per_test_args)
   1054 
   1055     @staticmethod
   1056     def _loop_with_timeout(condition, timeout_secs):
   1057         deadline = time.time() + timeout_secs
   1058         while time.time() < deadline:
   1059             if condition():
   1060                 return True
   1061         return False
   1062 
   1063     def _all_pipes_created(self):
   1064         return (self._android_commands.file_exists(self._in_fifo_path) and
   1065                 self._android_commands.file_exists(self._out_fifo_path) and
   1066                 self._android_commands.file_exists(self._err_fifo_path))
   1067 
   1068     def _remove_all_pipes(self):
   1069         for file in [self._in_fifo_path, self._out_fifo_path, self._err_fifo_path]:
   1070             self._android_commands.run(['shell', 'rm', file])
   1071 
   1072         return (not self._android_commands.file_exists(self._in_fifo_path) and
   1073                 not self._android_commands.file_exists(self._out_fifo_path) and
   1074                 not self._android_commands.file_exists(self._err_fifo_path))
   1075 
   1076     def start(self, pixel_tests, per_test_args, deadline):
   1077         # We override the default start() so that we can call _android_driver_cmd_line()
   1078         # instead of cmd_line().
   1079         new_cmd_line = self._android_driver_cmd_line(pixel_tests, per_test_args)
   1080 
   1081         # Since _android_driver_cmd_line() is different than cmd_line() we need to provide
   1082         # our own mechanism for detecting when the process should be stopped.
   1083         if self._current_cmd_line is None:
   1084             self._current_android_cmd_line = None
   1085         if new_cmd_line != self._current_android_cmd_line:
   1086             self.stop()
   1087         self._current_android_cmd_line = new_cmd_line
   1088 
   1089         super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args, deadline)
   1090 
   1091     def _start(self, pixel_tests, per_test_args):
   1092         if not self._android_devices.is_device_prepared(self._android_commands.get_serial()):
   1093             raise driver.DeviceFailure("%s is not prepared in _start()" % self._android_commands.get_serial())
   1094 
   1095         for retries in range(3):
   1096             try:
   1097                 if self._start_once(pixel_tests, per_test_args):
   1098                     return
   1099             except ScriptError as e:
   1100                 self._abort('ScriptError("%s") in _start()' % str(e))
   1101 
   1102             self._log_error('Failed to start the content_shell application. Retries=%d. Log:%s' % (retries, self._get_logcat()))
   1103             self.stop()
   1104             time.sleep(2)
   1105         self._abort('Failed to start the content_shell application multiple times. Giving up.')
   1106 
   1107     def _start_once(self, pixel_tests, per_test_args):
   1108         super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args, wait_for_ready=False)
   1109 
   1110         self._log_debug('Starting forwarder')
   1111         self._forwarder_process = self._port._server_process_constructor(
   1112             self._port, 'Forwarder', self._android_commands.adb_command() + ['shell', '%s -D %s' % (self._driver_details.device_forwarder_path(), FORWARD_PORTS)])
   1113         self._forwarder_process.start()
   1114 
   1115         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
   1116         if not self._wait_for_server_process_output(self._forwarder_process, deadline, 'Forwarding device port'):
   1117             return False
   1118 
   1119         self._android_commands.run(['logcat', '-c'])
   1120 
   1121         cmd_line_file_path = self._driver_details.command_line_file()
   1122         original_cmd_line_file_path = cmd_line_file_path + '.orig'
   1123         if self._android_commands.file_exists(cmd_line_file_path) and not self._android_commands.file_exists(original_cmd_line_file_path):
   1124             # We check for both the normal path and the backup because we do not want to step
   1125             # on the backup. Otherwise, we'd clobber the backup whenever we changed the
   1126             # command line during the run.
   1127             self._android_commands.run(['shell', 'mv', cmd_line_file_path, original_cmd_line_file_path])
   1128 
   1129         self._android_commands.run(['shell', 'echo'] + self._android_driver_cmd_line(pixel_tests, per_test_args) + ['>', self._driver_details.command_line_file()])
   1130         self._created_cmd_line = True
   1131 
   1132         self._android_commands.run(['shell', 'rm', '-rf', self._driver_details.device_crash_dumps_directory()])
   1133         self._android_commands.mkdir(self._driver_details.device_crash_dumps_directory(), chmod='777')
   1134 
   1135         start_result = self._android_commands.run(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', self._driver_details.activity_name()])
   1136         if start_result.find('Exception') != -1:
   1137             self._log_error('Failed to start the content_shell application. Exception:\n' + start_result)
   1138             return False
   1139 
   1140         if not ChromiumAndroidDriver._loop_with_timeout(self._all_pipes_created, DRIVER_START_STOP_TIMEOUT_SECS):
   1141             return False
   1142 
   1143         # Read back the shell prompt to ensure adb shell ready.
   1144         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
   1145         self._server_process.start()
   1146         self._read_prompt(deadline)
   1147         self._log_debug('Interactive shell started')
   1148 
   1149         # Start a process to read from the stdout fifo of the test driver and print to stdout.
   1150         self._log_debug('Redirecting stdout to ' + self._out_fifo_path)
   1151         self._read_stdout_process = self._port._server_process_constructor(
   1152             self._port, 'ReadStdout', self._android_commands.adb_command() + ['shell', 'cat', self._out_fifo_path])
   1153         self._read_stdout_process.start()
   1154 
   1155         # Start a process to read from the stderr fifo of the test driver and print to stdout.
   1156         self._log_debug('Redirecting stderr to ' + self._err_fifo_path)
   1157         self._read_stderr_process = self._port._server_process_constructor(
   1158             self._port, 'ReadStderr', self._android_commands.adb_command() + ['shell', 'cat', self._err_fifo_path])
   1159         self._read_stderr_process.start()
   1160 
   1161         self._log_debug('Redirecting stdin to ' + self._in_fifo_path)
   1162         self._server_process.write('cat >%s\n' % self._in_fifo_path)
   1163 
   1164         # Combine the stdout and stderr pipes into self._server_process.
   1165         self._server_process.replace_outputs(self._read_stdout_process._proc.stdout, self._read_stderr_process._proc.stdout)
   1166 
   1167         def deadlock_detector(processes, normal_startup_event):
   1168             if not ChromiumAndroidDriver._loop_with_timeout(lambda: normal_startup_event.is_set(), DRIVER_START_STOP_TIMEOUT_SECS):
   1169                 # If normal_startup_event is not set in time, the main thread must be blocked at
   1170                 # reading/writing the fifo. Kill the fifo reading/writing processes to let the
   1171                 # main thread escape from the deadlocked state. After that, the main thread will
   1172                 # treat this as a crash.
   1173                 self._log_error('Deadlock detected. Processes killed.')
   1174                 for i in processes:
   1175                     i.kill()
   1176 
   1177         # Start a thread to kill the pipe reading/writing processes on deadlock of the fifos during startup.
   1178         normal_startup_event = threading.Event()
   1179         threading.Thread(name='DeadlockDetector', target=deadlock_detector,
   1180                          args=([self._server_process, self._read_stdout_process, self._read_stderr_process], normal_startup_event)).start()
   1181 
   1182         # The test driver might crash during startup or when the deadlock detector hits
   1183         # a deadlock and kills the fifo reading/writing processes.
   1184         if not self._wait_for_server_process_output(self._server_process, deadline, '#READY'):
   1185             return False
   1186 
   1187         # Inform the deadlock detector that the startup is successful without deadlock.
   1188         normal_startup_event.set()
   1189         self._log_debug("content_shell is ready")
   1190         return True
   1191 
   1192     def _pid_from_android_ps_output(self, ps_output, package_name):
   1193         # ps output seems to be fixed width, we only care about the name and the pid
   1194         # u0_a72    21630 125   947920 59364 ffffffff 400beee4 S org.chromium.native_test
   1195         for line in ps_output.split('\n'):
   1196             if line.find(self._driver_details.package_name()) != -1:
   1197                 match = re.match(r'\S+\s+(\d+)', line)
   1198                 return int(match.group(1))
   1199 
   1200     def _pid_on_target(self):
   1201         # FIXME: There must be a better way to do this than grepping ps output!
   1202         ps_output = self._android_commands.run(['shell', 'ps'])
   1203         return self._pid_from_android_ps_output(ps_output, self._driver_details.package_name())
   1204 
   1205     def stop(self):
   1206         if not self._device_failed:
   1207             # Do not try to stop the application if there's something wrong with the device; adb may hang.
   1208             # FIXME: crbug.com/305040. Figure out if it's really hanging (and why).
   1209             self._android_commands.run(['shell', 'am', 'force-stop', self._driver_details.package_name()])
   1210 
   1211         if self._read_stdout_process:
   1212             self._read_stdout_process.kill()
   1213             self._read_stdout_process = None
   1214 
   1215         if self._read_stderr_process:
   1216             self._read_stderr_process.kill()
   1217             self._read_stderr_process = None
   1218 
   1219         super(ChromiumAndroidDriver, self).stop()
   1220 
   1221         if self._forwarder_process:
   1222             self._forwarder_process.kill()
   1223             self._forwarder_process = None
   1224 
   1225         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
   1226             if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRIVER_START_STOP_TIMEOUT_SECS):
   1227                 self._abort('Failed to remove fifo files. May be locked.')
   1228 
   1229         self._clean_up_cmd_line()
   1230 
   1231     def _pull_crash_dumps_from_device(self):
   1232         result = []
   1233         if not self._android_commands.file_exists(self._driver_details.device_crash_dumps_directory()):
   1234             return result
   1235         dumps = self._android_commands.run(['shell', 'ls', self._driver_details.device_crash_dumps_directory()])
   1236         for dump in dumps.splitlines():
   1237             device_dump = '%s/%s' % (self._driver_details.device_crash_dumps_directory(), dump)
   1238             local_dump = self._port._filesystem.join(self._port._dump_reader.crash_dumps_directory(), dump)
   1239 
   1240             # FIXME: crbug.com/321489. Figure out why these commands would fail ...
   1241             err = self._android_commands.run(['shell', 'chmod', '777', device_dump])
   1242             if not err:
   1243                 self._android_commands.pull(device_dump, local_dump)
   1244             if not err:
   1245                 self._android_commands.run(['shell', 'rm', '-f', device_dump])
   1246 
   1247             if self._port._filesystem.exists(local_dump):
   1248                 result.append(local_dump)
   1249         return result
   1250 
   1251     def _clean_up_cmd_line(self):
   1252         if not self._created_cmd_line:
   1253             return
   1254 
   1255         cmd_line_file_path = self._driver_details.command_line_file()
   1256         original_cmd_line_file_path = cmd_line_file_path + '.orig'
   1257         if self._android_commands.file_exists(original_cmd_line_file_path):
   1258             self._android_commands.run(['shell', 'mv', original_cmd_line_file_path, cmd_line_file_path])
   1259         elif self._android_commands.file_exists(cmd_line_file_path):
   1260             self._android_commands.run(['shell', 'rm', cmd_line_file_path])
   1261         self._created_cmd_line = False
   1262 
   1263     def _command_from_driver_input(self, driver_input):
   1264         command = super(ChromiumAndroidDriver, self)._command_from_driver_input(driver_input)
   1265         if command.startswith('/'):
   1266             fs = self._port._filesystem
   1267             # FIXME: what happens if command lies outside of the layout_tests_dir on the host?
   1268             relative_test_filename = fs.relpath(command, fs.dirname(self._port.layout_tests_dir()))
   1269             command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename
   1270         return command
   1271 
   1272     def _read_prompt(self, deadline):
   1273         last_char = ''
   1274         while True:
   1275             current_char = self._server_process.read_stdout(deadline, 1)
   1276             if current_char == ' ':
   1277                 if last_char in ('#', '$'):
   1278                     return
   1279             last_char = current_char
   1280