11cb0ef41Sopenharmony_ci# Copyright 2018 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci# found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci""" 61cb0ef41Sopenharmony_ciWrapper around the Android device abstraction from src/build/android. 71cb0ef41Sopenharmony_ci""" 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciimport logging 101cb0ef41Sopenharmony_ciimport os 111cb0ef41Sopenharmony_ciimport sys 121cb0ef41Sopenharmony_ciimport re 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ciBASE_DIR = os.path.normpath( 151cb0ef41Sopenharmony_ci os.path.join(os.path.dirname(__file__), '..', '..', '..')) 161cb0ef41Sopenharmony_ciANDROID_DIR = os.path.join(BASE_DIR, 'build', 'android') 171cb0ef41Sopenharmony_ciDEVICE_DIR = '/data/local/tmp/v8/' 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciclass TimeoutException(Exception): 211cb0ef41Sopenharmony_ci def __init__(self, timeout, output=None): 221cb0ef41Sopenharmony_ci self.timeout = timeout 231cb0ef41Sopenharmony_ci self.output = output 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ciclass CommandFailedException(Exception): 271cb0ef41Sopenharmony_ci def __init__(self, status, output): 281cb0ef41Sopenharmony_ci self.status = status 291cb0ef41Sopenharmony_ci self.output = output 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ciclass _Driver(object): 331cb0ef41Sopenharmony_ci """Helper class to execute shell commands on an Android device.""" 341cb0ef41Sopenharmony_ci def __init__(self, device=None): 351cb0ef41Sopenharmony_ci assert os.path.exists(ANDROID_DIR) 361cb0ef41Sopenharmony_ci sys.path.insert(0, ANDROID_DIR) 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci # We import the dependencies only on demand, so that this file can be 391cb0ef41Sopenharmony_ci # imported unconditionally. 401cb0ef41Sopenharmony_ci import devil_chromium 411cb0ef41Sopenharmony_ci from devil.android import device_errors # pylint: disable=import-error 421cb0ef41Sopenharmony_ci from devil.android import device_utils # pylint: disable=import-error 431cb0ef41Sopenharmony_ci from devil.android.perf import cache_control # pylint: disable=import-error 441cb0ef41Sopenharmony_ci from devil.android.perf import perf_control # pylint: disable=import-error 451cb0ef41Sopenharmony_ci global cache_control 461cb0ef41Sopenharmony_ci global device_errors 471cb0ef41Sopenharmony_ci global perf_control 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci devil_chromium.Initialize() 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci # Find specified device or a single attached device if none was specified. 521cb0ef41Sopenharmony_ci # In case none or multiple devices are attached, this raises an exception. 531cb0ef41Sopenharmony_ci self.device = device_utils.DeviceUtils.HealthyDevices( 541cb0ef41Sopenharmony_ci retries=5, enable_usb_resets=True, device_arg=device)[0] 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci # This remembers what we have already pushed to the device. 571cb0ef41Sopenharmony_ci self.pushed = set() 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci def tear_down(self): 601cb0ef41Sopenharmony_ci """Clean up files after running all tests.""" 611cb0ef41Sopenharmony_ci self.device.RemovePath(DEVICE_DIR, force=True, recursive=True) 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci def push_file(self, host_dir, file_name, target_rel='.', 641cb0ef41Sopenharmony_ci skip_if_missing=False): 651cb0ef41Sopenharmony_ci """Push a single file to the device (cached). 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci Args: 681cb0ef41Sopenharmony_ci host_dir: Absolute parent directory of the file to push. 691cb0ef41Sopenharmony_ci file_name: Name of the file to push. 701cb0ef41Sopenharmony_ci target_rel: Parent directory of the target location on the device 711cb0ef41Sopenharmony_ci (relative to the device's base dir for testing). 721cb0ef41Sopenharmony_ci skip_if_missing: Keeps silent about missing files when set. Otherwise logs 731cb0ef41Sopenharmony_ci error. 741cb0ef41Sopenharmony_ci """ 751cb0ef41Sopenharmony_ci # TODO(sergiyb): Implement this method using self.device.PushChangedFiles to 761cb0ef41Sopenharmony_ci # avoid accessing low-level self.device.adb. 771cb0ef41Sopenharmony_ci file_on_host = os.path.join(host_dir, file_name) 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci # Only push files not yet pushed in one execution. 801cb0ef41Sopenharmony_ci if file_on_host in self.pushed: 811cb0ef41Sopenharmony_ci return 821cb0ef41Sopenharmony_ci 831cb0ef41Sopenharmony_ci file_on_device_tmp = os.path.join(DEVICE_DIR, '_tmp_', file_name) 841cb0ef41Sopenharmony_ci file_on_device = os.path.join(DEVICE_DIR, target_rel, file_name) 851cb0ef41Sopenharmony_ci folder_on_device = os.path.dirname(file_on_device) 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci # Only attempt to push files that exist. 881cb0ef41Sopenharmony_ci if not os.path.exists(file_on_host): 891cb0ef41Sopenharmony_ci if not skip_if_missing: 901cb0ef41Sopenharmony_ci logging.critical('Missing file on host: %s' % file_on_host) 911cb0ef41Sopenharmony_ci return 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ci # Work-around for 'text file busy' errors. Push the files to a temporary 941cb0ef41Sopenharmony_ci # location and then copy them with a shell command. 951cb0ef41Sopenharmony_ci output = self.device.adb.Push(file_on_host, file_on_device_tmp) 961cb0ef41Sopenharmony_ci # Success looks like this: '3035 KB/s (12512056 bytes in 4.025s)'. 971cb0ef41Sopenharmony_ci # Errors look like this: 'failed to copy ... '. 981cb0ef41Sopenharmony_ci if output and not re.search('^[0-9]', output.splitlines()[-1]): 991cb0ef41Sopenharmony_ci logging.critical('PUSH FAILED: ' + output) 1001cb0ef41Sopenharmony_ci self.device.adb.Shell('mkdir -p %s' % folder_on_device) 1011cb0ef41Sopenharmony_ci self.device.adb.Shell('cp %s %s' % (file_on_device_tmp, file_on_device)) 1021cb0ef41Sopenharmony_ci self.pushed.add(file_on_host) 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci def push_executable(self, shell_dir, target_dir, binary): 1051cb0ef41Sopenharmony_ci """Push files required to run a V8 executable. 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci Args: 1081cb0ef41Sopenharmony_ci shell_dir: Absolute parent directory of the executable on the host. 1091cb0ef41Sopenharmony_ci target_dir: Parent directory of the executable on the device (relative to 1101cb0ef41Sopenharmony_ci devices' base dir for testing). 1111cb0ef41Sopenharmony_ci binary: Name of the binary to push. 1121cb0ef41Sopenharmony_ci """ 1131cb0ef41Sopenharmony_ci self.push_file(shell_dir, binary, target_dir) 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci # Push external startup data. Backwards compatible for revisions where 1161cb0ef41Sopenharmony_ci # these files didn't exist. Or for bots that don't produce these files. 1171cb0ef41Sopenharmony_ci self.push_file( 1181cb0ef41Sopenharmony_ci shell_dir, 1191cb0ef41Sopenharmony_ci 'natives_blob.bin', 1201cb0ef41Sopenharmony_ci target_dir, 1211cb0ef41Sopenharmony_ci skip_if_missing=True, 1221cb0ef41Sopenharmony_ci ) 1231cb0ef41Sopenharmony_ci self.push_file( 1241cb0ef41Sopenharmony_ci shell_dir, 1251cb0ef41Sopenharmony_ci 'snapshot_blob.bin', 1261cb0ef41Sopenharmony_ci target_dir, 1271cb0ef41Sopenharmony_ci skip_if_missing=True, 1281cb0ef41Sopenharmony_ci ) 1291cb0ef41Sopenharmony_ci self.push_file( 1301cb0ef41Sopenharmony_ci shell_dir, 1311cb0ef41Sopenharmony_ci 'icudtl.dat', 1321cb0ef41Sopenharmony_ci target_dir, 1331cb0ef41Sopenharmony_ci skip_if_missing=True, 1341cb0ef41Sopenharmony_ci ) 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ci def run(self, target_dir, binary, args, rel_path, timeout, env=None, 1371cb0ef41Sopenharmony_ci logcat_file=False): 1381cb0ef41Sopenharmony_ci """Execute a command on the device's shell. 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci Args: 1411cb0ef41Sopenharmony_ci target_dir: Parent directory of the executable on the device (relative to 1421cb0ef41Sopenharmony_ci devices' base dir for testing). 1431cb0ef41Sopenharmony_ci binary: Name of the binary. 1441cb0ef41Sopenharmony_ci args: List of arguments to pass to the binary. 1451cb0ef41Sopenharmony_ci rel_path: Relative path on device to use as CWD. 1461cb0ef41Sopenharmony_ci timeout: Timeout in seconds. 1471cb0ef41Sopenharmony_ci env: The environment variables with which the command should be run. 1481cb0ef41Sopenharmony_ci logcat_file: File into which to stream adb logcat log. 1491cb0ef41Sopenharmony_ci """ 1501cb0ef41Sopenharmony_ci binary_on_device = os.path.join(DEVICE_DIR, target_dir, binary) 1511cb0ef41Sopenharmony_ci cmd = [binary_on_device] + args 1521cb0ef41Sopenharmony_ci def run_inner(): 1531cb0ef41Sopenharmony_ci try: 1541cb0ef41Sopenharmony_ci output = self.device.RunShellCommand( 1551cb0ef41Sopenharmony_ci cmd, 1561cb0ef41Sopenharmony_ci cwd=os.path.join(DEVICE_DIR, rel_path), 1571cb0ef41Sopenharmony_ci check_return=True, 1581cb0ef41Sopenharmony_ci env=env, 1591cb0ef41Sopenharmony_ci timeout=timeout, 1601cb0ef41Sopenharmony_ci retries=0, 1611cb0ef41Sopenharmony_ci ) 1621cb0ef41Sopenharmony_ci return '\n'.join(output) 1631cb0ef41Sopenharmony_ci except device_errors.AdbCommandFailedError as e: 1641cb0ef41Sopenharmony_ci raise CommandFailedException(e.status, e.output) 1651cb0ef41Sopenharmony_ci except device_errors.CommandTimeoutError as e: 1661cb0ef41Sopenharmony_ci raise TimeoutException(timeout, e.output) 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci if logcat_file: 1701cb0ef41Sopenharmony_ci with self.device.GetLogcatMonitor(output_file=logcat_file) as logmon: 1711cb0ef41Sopenharmony_ci result = run_inner() 1721cb0ef41Sopenharmony_ci logmon.Close() 1731cb0ef41Sopenharmony_ci return result 1741cb0ef41Sopenharmony_ci else: 1751cb0ef41Sopenharmony_ci return run_inner() 1761cb0ef41Sopenharmony_ci 1771cb0ef41Sopenharmony_ci def drop_ram_caches(self): 1781cb0ef41Sopenharmony_ci """Drop ran caches on device.""" 1791cb0ef41Sopenharmony_ci cache = cache_control.CacheControl(self.device) 1801cb0ef41Sopenharmony_ci cache.DropRamCaches() 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci def set_high_perf_mode(self): 1831cb0ef41Sopenharmony_ci """Set device into high performance mode.""" 1841cb0ef41Sopenharmony_ci perf = perf_control.PerfControl(self.device) 1851cb0ef41Sopenharmony_ci perf.SetHighPerfMode() 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci def set_default_perf_mode(self): 1881cb0ef41Sopenharmony_ci """Set device into default performance mode.""" 1891cb0ef41Sopenharmony_ci perf = perf_control.PerfControl(self.device) 1901cb0ef41Sopenharmony_ci perf.SetDefaultPerfMode() 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci_ANDROID_DRIVER = None 1941cb0ef41Sopenharmony_cidef android_driver(device=None): 1951cb0ef41Sopenharmony_ci """Singleton access method to the driver class.""" 1961cb0ef41Sopenharmony_ci global _ANDROID_DRIVER 1971cb0ef41Sopenharmony_ci if not _ANDROID_DRIVER: 1981cb0ef41Sopenharmony_ci _ANDROID_DRIVER = _Driver(device) 1991cb0ef41Sopenharmony_ci return _ANDROID_DRIVER 200