Commit de80e94c authored by Michael Achenbach's avatar Michael Achenbach Committed by Commit Bot

[test] Refactoring - extract Android driver logic from perf runner

This prepares for reusing the driver logic for correctness testing.

NOTRY=true

Bug: chromium:866862
Change-Id: If901ca8552cf48f29e240ed5b52ea7e722e0e1e4
Reviewed-on: https://chromium-review.googlesource.com/1163608
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarSergiy Byelozyorov <sergiyb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54961}
parent daf71978
...@@ -52,6 +52,7 @@ group("v8_perf") { ...@@ -52,6 +52,7 @@ group("v8_perf") {
# TODO(machenbach): These files are referenced by the perf runner. # TODO(machenbach): These files are referenced by the perf runner.
# They should be transformed into a proper python module. # They should be transformed into a proper python module.
"../tools/testrunner/local/android.py",
"../tools/testrunner/local/command.py", "../tools/testrunner/local/command.py",
"../tools/testrunner/local/utils.py", "../tools/testrunner/local/utils.py",
"../tools/testrunner/objects/output.py", "../tools/testrunner/objects/output.py",
......
...@@ -109,6 +109,7 @@ import re ...@@ -109,6 +109,7 @@ import re
import subprocess import subprocess
import sys import sys
from testrunner.local import android
from testrunner.local import command from testrunner.local import command
from testrunner.local import utils from testrunner.local import utils
...@@ -124,27 +125,6 @@ GENERIC_RESULTS_RE = re.compile(r"^RESULT ([^:]+): ([^=]+)= ([^ ]+) ([^ ]*)$") ...@@ -124,27 +125,6 @@ GENERIC_RESULTS_RE = re.compile(r"^RESULT ([^:]+): ([^=]+)= ([^ ]+) ([^ ]*)$")
RESULT_STDDEV_RE = re.compile(r"^\{([^\}]+)\}$") RESULT_STDDEV_RE = re.compile(r"^\{([^\}]+)\}$")
RESULT_LIST_RE = re.compile(r"^\[([^\]]+)\]$") RESULT_LIST_RE = re.compile(r"^\[([^\]]+)\]$")
TOOLS_BASE = os.path.abspath(os.path.dirname(__file__)) TOOLS_BASE = os.path.abspath(os.path.dirname(__file__))
ANDROID_BUILD_TOOLS = os.path.join(
os.path.dirname(TOOLS_BASE), 'build', 'android')
def LoadAndroidBuildTools(path): # pragma: no cover
assert os.path.exists(path)
sys.path.insert(0, path)
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.sdk import adb_wrapper # 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
global adb_wrapper
global cache_control
global device_errors
global device_utils
global perf_control
devil_chromium.Initialize()
def GeometricMean(values): def GeometricMean(values):
...@@ -730,95 +710,17 @@ class DesktopPlatform(Platform): ...@@ -730,95 +710,17 @@ class DesktopPlatform(Platform):
class AndroidPlatform(Platform): # pragma: no cover class AndroidPlatform(Platform): # pragma: no cover
DEVICE_DIR = "/data/local/tmp/v8/"
def __init__(self, options): def __init__(self, options):
super(AndroidPlatform, self).__init__(options) super(AndroidPlatform, self).__init__(options)
LoadAndroidBuildTools(ANDROID_BUILD_TOOLS) self.driver = android.android_driver(options.device)
if not options.device:
# Detect attached device if not specified.
devices = adb_wrapper.AdbWrapper.Devices()
assert devices and len(devices) == 1, (
"None or multiple devices detected. Please specify the device on "
"the command-line with --device")
options.device = str(devices[0])
self.adb_wrapper = adb_wrapper.AdbWrapper(options.device)
self.device = device_utils.DeviceUtils(self.adb_wrapper)
def PreExecution(self): def PreExecution(self):
perf = perf_control.PerfControl(self.device) self.driver.set_high_perf_mode()
perf.SetHighPerfMode()
# Remember what we have already pushed to the device.
self.pushed = set()
def PostExecution(self): def PostExecution(self):
perf = perf_control.PerfControl(self.device) self.driver.set_default_perf_mode()
perf.SetDefaultPerfMode() self.driver.tear_down()
self.device.RemovePath(
AndroidPlatform.DEVICE_DIR, force=True, recursive=True)
def _PushFile(self, host_dir, file_name, target_rel=".",
skip_if_missing=False):
file_on_host = os.path.join(host_dir, file_name)
file_on_device_tmp = os.path.join(
AndroidPlatform.DEVICE_DIR, "_tmp_", file_name)
file_on_device = os.path.join(
AndroidPlatform.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.warning('Missing file on host: %s', file_on_host)
return
# Only push files not yet pushed in one execution.
if file_on_host in self.pushed:
return
else:
self.pushed.add(file_on_host)
# 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.warning('PUSH FAILED: %s', 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))
def _PushExecutable(self, shell_dir, target_dir, binary):
self._PushFile(shell_dir, binary, target_dir)
# Push external startup data. Backwards compatible for revisions where
# these files didn't exist.
self._PushFile(
shell_dir,
"natives_blob.bin",
target_dir,
skip_if_missing=True,
)
self._PushFile(
shell_dir,
"snapshot_blob.bin",
target_dir,
skip_if_missing=True,
)
self._PushFile(
shell_dir,
"snapshot_blob_trusted.bin",
target_dir,
skip_if_missing=True,
)
self._PushFile(
shell_dir,
"icudtl.dat",
target_dir,
skip_if_missing=True,
)
def PreTests(self, node, path): def PreTests(self, node, path):
if isinstance(node, RunnableConfig): if isinstance(node, RunnableConfig):
...@@ -831,25 +733,21 @@ class AndroidPlatform(Platform): # pragma: no cover ...@@ -831,25 +733,21 @@ class AndroidPlatform(Platform): # pragma: no cover
bench_rel = "." bench_rel = "."
bench_abs = suite_dir bench_abs = suite_dir
self._PushExecutable(self.shell_dir, "bin", node.binary) self.driver.push_executable(self.shell_dir, "bin", node.binary)
if self.shell_dir_secondary: if self.shell_dir_secondary:
self._PushExecutable( self.driver.push_executable(
self.shell_dir_secondary, "bin_secondary", node.binary) self.shell_dir_secondary, "bin_secondary", node.binary)
if isinstance(node, RunnableConfig): if isinstance(node, RunnableConfig):
self._PushFile(bench_abs, node.main, bench_rel) self.driver.push_file(bench_abs, node.main, bench_rel)
for resource in node.resources: for resource in node.resources:
self._PushFile(bench_abs, resource, bench_rel) self.driver.push_file(bench_abs, resource, bench_rel)
def _Run(self, runnable, count, secondary=False): def _Run(self, runnable, count, secondary=False):
suffix = ' - secondary' if secondary else '' suffix = ' - secondary' if secondary else ''
target_dir = "bin_secondary" if secondary else "bin" target_dir = "bin_secondary" if secondary else "bin"
title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) title = ">>> %%s (#%d)%s:" % ((count + 1), suffix)
cache = cache_control.CacheControl(self.device) self.driver.drop_ram_caches()
cache.DropRamCaches()
binary_on_device = os.path.join(
AndroidPlatform.DEVICE_DIR, target_dir, runnable.binary)
cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags)
# Relative path to benchmark directory. # Relative path to benchmark directory.
if runnable.path: if runnable.path:
...@@ -858,16 +756,15 @@ class AndroidPlatform(Platform): # pragma: no cover ...@@ -858,16 +756,15 @@ class AndroidPlatform(Platform): # pragma: no cover
bench_rel = "." bench_rel = "."
try: try:
output = self.device.RunShellCommand( stdout = self.driver.run(
cmd, target_dir=target_dir,
cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel), binary=runnable.binary,
check_return=True, args=runnable.GetCommandFlags(self.extra_flags),
rel_path=bench_rel,
timeout=runnable.timeout, timeout=runnable.timeout,
retries=0,
) )
stdout = "\n".join(output)
logging.info(title % "Stdout" + "\n%s", stdout) logging.info(title % "Stdout" + "\n%s", stdout)
except device_errors.CommandTimeoutError: except android.TimeoutException:
logging.warning(">>> Test timed out after %ss.", runnable.timeout) logging.warning(">>> Test timed out after %ss.", runnable.timeout)
stdout = "" stdout = ""
if runnable.process_size: if runnable.process_size:
......
# 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):
"""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.
"""
binary_on_device = os.path.join(DEVICE_DIR, target_dir, binary)
cmd = [binary_on_device] + args
try:
output = self.device.RunShellCommand(
cmd,
cwd=os.path.join(DEVICE_DIR, rel_path),
check_return=True,
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)
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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment