diff options
Diffstat (limited to 'deps/v8/tools/testrunner/local')
-rw-r--r-- | deps/v8/tools/testrunner/local/android.py | 207 | ||||
-rw-r--r-- | deps/v8/tools/testrunner/local/command.py | 113 | ||||
-rw-r--r-- | deps/v8/tools/testrunner/local/statusfile.py | 2 |
3 files changed, 311 insertions, 11 deletions
diff --git a/deps/v8/tools/testrunner/local/android.py b/deps/v8/tools/testrunner/local/android.py new file mode 100644 index 0000000000..fb25bb5a17 --- /dev/null +++ b/deps/v8/tools/testrunner/local/android.py @@ -0,0 +1,207 @@ +# Copyright 2018 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Wrapper around the Android device abstraction from src/build/android. +""" + +import logging +import os +import sys + + +BASE_DIR = os.path.normpath( + os.path.join(os.path.dirname(__file__), '..', '..', '..')) +ANDROID_DIR = os.path.join(BASE_DIR, 'build', 'android') +DEVICE_DIR = '/data/local/tmp/v8/' + + +class TimeoutException(Exception): + def __init__(self, timeout): + self.timeout = timeout + + +class CommandFailedException(Exception): + def __init__(self, status, output): + self.status = status + self.output = output + + +class _Driver(object): + """Helper class to execute shell commands on an Android device.""" + def __init__(self, device=None): + assert os.path.exists(ANDROID_DIR) + sys.path.insert(0, ANDROID_DIR) + + # We import the dependencies only on demand, so that this file can be + # imported unconditionally. + import devil_chromium + from devil.android import device_errors # pylint: disable=import-error + from devil.android import device_utils # pylint: disable=import-error + from devil.android.perf import cache_control # pylint: disable=import-error + from devil.android.perf import perf_control # pylint: disable=import-error + from devil.android.sdk import adb_wrapper # pylint: disable=import-error + global cache_control + global device_errors + global perf_control + + devil_chromium.Initialize() + + if not device: + # Detect attached device if not specified. + devices = adb_wrapper.AdbWrapper.Devices() + assert devices, 'No devices detected' + assert len(devices) == 1, 'Multiple devices detected.' + device = str(devices[0]) + self.adb_wrapper = adb_wrapper.AdbWrapper(device) + self.device = device_utils.DeviceUtils(self.adb_wrapper) + + # This remembers what we have already pushed to the device. + self.pushed = set() + + def tear_down(self): + """Clean up files after running all tests.""" + self.device.RemovePath(DEVICE_DIR, force=True, recursive=True) + + def push_file(self, host_dir, file_name, target_rel='.', + skip_if_missing=False): + """Push a single file to the device (cached). + + Args: + host_dir: Absolute parent directory of the file to push. + file_name: Name of the file to push. + target_rel: Parent directory of the target location on the device + (relative to the device's base dir for testing). + skip_if_missing: Keeps silent about missing files when set. Otherwise logs + error. + """ + file_on_host = os.path.join(host_dir, file_name) + + # Only push files not yet pushed in one execution. + if file_on_host in self.pushed: + return + + file_on_device_tmp = os.path.join(DEVICE_DIR, '_tmp_', file_name) + file_on_device = os.path.join(DEVICE_DIR, target_rel, file_name) + folder_on_device = os.path.dirname(file_on_device) + + # Only attempt to push files that exist. + if not os.path.exists(file_on_host): + if not skip_if_missing: + logging.critical('Missing file on host: %s' % file_on_host) + return + + # Work-around for 'text file busy' errors. Push the files to a temporary + # location and then copy them with a shell command. + output = self.adb_wrapper.Push(file_on_host, file_on_device_tmp) + # Success looks like this: '3035 KB/s (12512056 bytes in 4.025s)'. + # Errors look like this: 'failed to copy ... '. + if output and not re.search('^[0-9]', output.splitlines()[-1]): + logging.critical('PUSH FAILED: ' + output) + self.adb_wrapper.Shell('mkdir -p %s' % folder_on_device) + self.adb_wrapper.Shell('cp %s %s' % (file_on_device_tmp, file_on_device)) + self.pushed.add(file_on_host) + + def push_executable(self, shell_dir, target_dir, binary): + """Push files required to run a V8 executable. + + Args: + shell_dir: Absolute parent directory of the executable on the host. + target_dir: Parent directory of the executable on the device (relative to + devices' base dir for testing). + binary: Name of the binary to push. + """ + self.push_file(shell_dir, binary, target_dir) + + # Push external startup data. Backwards compatible for revisions where + # these files didn't exist. Or for bots that don't produce these files. + self.push_file( + shell_dir, + 'natives_blob.bin', + target_dir, + skip_if_missing=True, + ) + self.push_file( + shell_dir, + 'snapshot_blob.bin', + target_dir, + skip_if_missing=True, + ) + self.push_file( + shell_dir, + 'snapshot_blob_trusted.bin', + target_dir, + skip_if_missing=True, + ) + self.push_file( + shell_dir, + 'icudtl.dat', + target_dir, + skip_if_missing=True, + ) + + def run(self, target_dir, binary, args, rel_path, timeout, env=None, + logcat_file=False): + """Execute a command on the device's shell. + + Args: + target_dir: Parent directory of the executable on the device (relative to + devices' base dir for testing). + binary: Name of the binary. + args: List of arguments to pass to the binary. + rel_path: Relative path on device to use as CWD. + timeout: Timeout in seconds. + env: The environment variables with which the command should be run. + logcat_file: File into which to stream adb logcat log. + """ + binary_on_device = os.path.join(DEVICE_DIR, target_dir, binary) + cmd = [binary_on_device] + args + def run_inner(): + try: + output = self.device.RunShellCommand( + cmd, + cwd=os.path.join(DEVICE_DIR, rel_path), + check_return=True, + env=env, + timeout=timeout, + retries=0, + ) + return '\n'.join(output) + except device_errors.AdbCommandFailedError as e: + raise CommandFailedException(e.status, e.output) + except device_errors.CommandTimeoutError: + raise TimeoutException(timeout) + + + if logcat_file: + with self.device.GetLogcatMonitor(output_file=logcat_file) as logmon: + result = run_inner() + logmon.Close() + return result + else: + return run_inner() + + def drop_ram_caches(self): + """Drop ran caches on device.""" + cache = cache_control.CacheControl(self.device) + cache.DropRamCaches() + + def set_high_perf_mode(self): + """Set device into high performance mode.""" + perf = perf_control.PerfControl(self.device) + perf.SetHighPerfMode() + + def set_default_perf_mode(self): + """Set device into default performance mode.""" + perf = perf_control.PerfControl(self.device) + perf.SetDefaultPerfMode() + + +_ANDROID_DRIVER = None +def android_driver(device=None): + """Singleton access method to the driver class.""" + global _ANDROID_DRIVER + if not _ANDROID_DRIVER: + _ANDROID_DRIVER = _Driver(device) + return _ANDROID_DRIVER diff --git a/deps/v8/tools/testrunner/local/command.py b/deps/v8/tools/testrunner/local/command.py index adc9c2e452..302d568e87 100644 --- a/deps/v8/tools/testrunner/local/command.py +++ b/deps/v8/tools/testrunner/local/command.py @@ -4,16 +4,22 @@ import os +import re import signal import subprocess import sys import threading import time +from ..local.android import ( + android_driver, CommandFailedException, TimeoutException) from ..local import utils from ..objects import output +BASE_DIR = os.path.normpath( + os.path.join(os.path.dirname(os.path.abspath(__file__)), '..' , '..', '..')) + SEM_INVALID_VALUE = -1 SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h @@ -33,7 +39,18 @@ class AbortException(Exception): class BaseCommand(object): def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None, - verbose=False): + verbose=False, resources_func=None): + """Initialize the command. + + Args: + shell: The name of the executable (e.g. d8). + args: List of args to pass to the executable. + cmd_prefix: Prefix of command (e.g. a wrapper script). + timeout: Timeout in seconds. + env: Environment dict for execution. + verbose: Print additional output. + resources_func: Callable, returning all test files needed by this command. + """ assert(timeout > 0) self.shell = shell @@ -43,11 +60,11 @@ class BaseCommand(object): self.env = env or {} self.verbose = verbose - def execute(self, **additional_popen_kwargs): + def execute(self): if self.verbose: print '# %s' % self - process = self._start_process(**additional_popen_kwargs) + process = self._start_process() # Variable to communicate with the signal handler. abort_occured = [False] @@ -79,14 +96,13 @@ class BaseCommand(object): duration ) - def _start_process(self, **additional_popen_kwargs): + def _start_process(self): try: return subprocess.Popen( args=self._get_popen_args(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._get_env(), - **additional_popen_kwargs ) except Exception as e: sys.stderr.write('Error executing: %s\n' % self) @@ -187,8 +203,85 @@ class WindowsCommand(BaseCommand): sys.stdout.flush() -# Set the Command class to the OS-specific version. -if utils.IsWindows(): - Command = WindowsCommand -else: - Command = PosixCommand +class AndroidCommand(BaseCommand): + def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None, + verbose=False, resources_func=None): + """Initialize the command and all files that need to be pushed to the + Android device. + """ + self.shell_name = os.path.basename(shell) + self.shell_dir = os.path.dirname(shell) + self.files_to_push = resources_func() + + # Make all paths in arguments relative and also prepare files from arguments + # for pushing to the device. + rel_args = [] + find_path_re = re.compile(r'.*(%s/[^\'"]+).*' % re.escape(BASE_DIR)) + for arg in (args or []): + match = find_path_re.match(arg) + if match: + self.files_to_push.append(match.group(1)) + rel_args.append( + re.sub(r'(.*)%s/(.*)' % re.escape(BASE_DIR), r'\1\2', arg)) + + super(AndroidCommand, self).__init__( + shell, args=rel_args, cmd_prefix=cmd_prefix, timeout=timeout, env=env, + verbose=verbose) + + def execute(self, **additional_popen_kwargs): + """Execute the command on the device. + + This pushes all required files to the device and then runs the command. + """ + if self.verbose: + print '# %s' % self + + android_driver().push_executable(self.shell_dir, 'bin', self.shell_name) + + for abs_file in self.files_to_push: + abs_dir = os.path.dirname(abs_file) + file_name = os.path.basename(abs_file) + rel_dir = os.path.relpath(abs_dir, BASE_DIR) + android_driver().push_file(abs_dir, file_name, rel_dir) + + start_time = time.time() + return_code = 0 + timed_out = False + try: + stdout = android_driver().run( + 'bin', self.shell_name, self.args, '.', self.timeout, self.env) + except CommandFailedException as e: + return_code = e.status + stdout = e.output + except TimeoutException as e: + return_code = 1 + timed_out = True + # Sadly the Android driver doesn't provide output on timeout. + stdout = '' + + duration = time.time() - start_time + return output.Output( + return_code, + timed_out, + stdout, + '', # No stderr available. + -1, # No pid available. + duration, + ) + + +Command = None +def setup(target_os): + """Set the Command class to the OS-specific version.""" + global Command + if target_os == 'android': + Command = AndroidCommand + elif target_os == 'windows': + Command = WindowsCommand + else: + Command = PosixCommand + +def tear_down(): + """Clean up after using commands.""" + if Command == AndroidCommand: + android_driver().tear_down() diff --git a/deps/v8/tools/testrunner/local/statusfile.py b/deps/v8/tools/testrunner/local/statusfile.py index ecfbf008a2..4742e84caf 100644 --- a/deps/v8/tools/testrunner/local/statusfile.py +++ b/deps/v8/tools/testrunner/local/statusfile.py @@ -55,7 +55,7 @@ for key in [SKIP, FAIL, PASS, CRASH, SLOW, FAIL_OK, NO_VARIANTS, FAIL_SLOPPY, # Support arches, modes to be written as keywords instead of strings. VARIABLES = {ALWAYS: True} -for var in ["debug", "release", "big", "little", +for var in ["debug", "release", "big", "little", "android", "android_arm", "android_arm64", "android_ia32", "android_x64", "arm", "arm64", "ia32", "mips", "mipsel", "mips64", "mips64el", "x64", "ppc", "ppc64", "s390", "s390x", "macos", "windows", |