diff options
28 files changed, 483 insertions, 596 deletions
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py index 967809f..8709d15 100644 --- a/build/android/pylib/device/device_utils.py +++ b/build/android/pylib/device/device_utils.py @@ -1382,22 +1382,6 @@ class DeviceUtils(object): % (property_name, value), str(self)) @decorators.WithTimeoutAndRetriesFromInstance() - def GetABI(self, timeout=None, retries=None): - """Gets the device main ABI. - - Args: - timeout: timeout in seconds - retries: number of retries - - Returns: - The device's main ABI name. - - Raises: - CommandTimeoutError on timeout. - """ - return self.GetProp('ro.product.cpu.abi') - - @decorators.WithTimeoutAndRetriesFromInstance() def GetPids(self, process_name, timeout=None, retries=None): """Returns the PIDs of processes with the given name. diff --git a/build/android/pylib/screenshot.py b/build/android/pylib/screenshot.py index 0fcc590..35edf3c 100644 --- a/build/android/pylib/screenshot.py +++ b/build/android/pylib/screenshot.py @@ -8,6 +8,7 @@ import tempfile import time from pylib import cmd_helper +from pylib import constants from pylib import device_signal from pylib.device import device_errors @@ -40,7 +41,7 @@ class VideoRecorder(object): self._recorder_stdout = None self._is_started = False - self._args = ['adb'] + self._args = [constants.GetAdbPath()] if str(self._device): self._args += ['-s', str(self._device)] self._args += ['shell', 'screenrecord', '--verbose'] diff --git a/tools/telemetry/telemetry/core/android_process.py b/tools/telemetry/telemetry/core/android_process.py index 62134c2..bfbd293 100644 --- a/tools/telemetry/telemetry/core/android_process.py +++ b/tools/telemetry/telemetry/core/android_process.py @@ -2,10 +2,17 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -from telemetry.core.backends import adb_commands from telemetry.core.backends.chrome_inspector import devtools_client_backend +from telemetry.core import util from telemetry.core import web_contents +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +try: + from pylib import ports # pylint: disable=import-error +except ImportError: + ports = None + + class WebViewNotFoundException(Exception): pass @@ -16,7 +23,7 @@ class AndroidProcess(object): self._app_backend = app_backend self._pid = pid self._name = name - self._local_port = adb_commands.AllocateTestServerPort() + self._local_port = ports.AllocateTestServerPort() self._devtools_client = None @property diff --git a/tools/telemetry/telemetry/core/backends/adb_commands.py b/tools/telemetry/telemetry/core/backends/adb_commands.py deleted file mode 100644 index 42eeb43..0000000 --- a/tools/telemetry/telemetry/core/backends/adb_commands.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Brings in Chrome Android's android_commands module, which itself is a -thin(ish) wrapper around adb.""" - -import logging -import os -import shutil -import stat - -from telemetry.core import platform -from telemetry.core import util -from telemetry.util import support_binaries - -# This is currently a thin wrapper around Chrome Android's -# build scripts, located in chrome/build/android. This file exists mainly to -# deal with locating the module. - -util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') -from pylib import android_commands # pylint: disable=F0401 -from pylib import constants # pylint: disable=F0401 -try: - from pylib import ports # pylint: disable=F0401 -except Exception: - ports = None -from pylib.device import device_utils # pylint: disable=F0401 -from pylib.utils import apk_helper # pylint: disable=F0401 - - -def IsAndroidSupported(): - return device_utils != None - -def GetPackageName(apk_path): - return apk_helper.GetPackageName(apk_path) - -def GetAttachedDevices(): - """Returns a list of attached, online android devices. - - If a preferred device has been set with ANDROID_SERIAL, it will be first in - the returned list.""" - return android_commands.GetAttachedDevices() - - -def AllocateTestServerPort(): - return ports.AllocateTestServerPort() - - -def ResetTestServerPortAllocation(): - return ports.ResetTestServerPortAllocation() - - -class AdbCommands(object): - """A thin wrapper around ADB""" - - def __init__(self, device): - self._device = device_utils.DeviceUtils(device) - self._device_serial = device - - def device_serial(self): - return self._device_serial - - def device(self): - return self._device - - def __getattr__(self, name): - """Delegate all unknown calls to the underlying AndroidCommands object.""" - return getattr(self._device.old_interface, name) - - def Forward(self, local, remote): - self._device.adb.Forward(local, remote) - - def IsUserBuild(self): - return self._device.GetProp('ro.build.type') == 'user' - - -def GetBuildTypeOfPath(path): - if not path: - return None - for build_dir, build_type in util.GetBuildDirectories(): - if os.path.join(build_dir, build_type) in path: - return build_type - return None - - -def SetupPrebuiltTools(adb): - """Some of the android pylib scripts we depend on are lame and expect - binaries to be in the out/ directory. So we copy any prebuilt binaries there - as a prereq.""" - - # TODO(bulach): Build the targets for x86/mips. - device_tools = [ - 'file_poller', - 'forwarder_dist/device_forwarder', - 'md5sum_dist/md5sum_bin', - 'purge_ashmem', - 'run_pie', - ] - - host_tools = [ - 'bitmaptools', - 'md5sum_bin_host', - ] - - if platform.GetHostPlatform().GetOSName() == 'linux': - host_tools.append('host_forwarder') - - arch_name = adb.device().GetABI() - has_device_prebuilt = (arch_name.startswith('armeabi') - or arch_name.startswith('arm64')) - if not has_device_prebuilt: - logging.warning('Unknown architecture type: %s' % arch_name) - return all([support_binaries.FindLocallyBuiltPath(t) for t in device_tools]) - - build_type = None - for t in device_tools + host_tools: - executable = os.path.basename(t) - locally_built_path = support_binaries.FindLocallyBuiltPath(t) - if not build_type: - build_type = GetBuildTypeOfPath(locally_built_path) or 'Release' - constants.SetBuildType(build_type) - dest = os.path.join(constants.GetOutDirectory(), t) - if not locally_built_path: - logging.info('Setting up prebuilt %s', dest) - if not os.path.exists(os.path.dirname(dest)): - os.makedirs(os.path.dirname(dest)) - platform_name = ('android' if t in device_tools else - platform.GetHostPlatform().GetOSName()) - bin_arch_name = (arch_name if t in device_tools else - platform.GetHostPlatform().GetArchName()) - prebuilt_path = support_binaries.FindPath( - executable, bin_arch_name, platform_name) - if not prebuilt_path or not os.path.exists(prebuilt_path): - raise NotImplementedError(""" -%s must be checked into cloud storage. -Instructions: -http://www.chromium.org/developers/telemetry/upload_to_cloud_storage -""" % t) - shutil.copyfile(prebuilt_path, dest) - os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - return True diff --git a/tools/telemetry/telemetry/core/backends/android_app_backend.py b/tools/telemetry/telemetry/core/backends/android_app_backend.py index b2c56f2..c0654eb 100644 --- a/tools/telemetry/telemetry/core/backends/android_app_backend.py +++ b/tools/telemetry/telemetry/core/backends/android_app_backend.py @@ -24,8 +24,8 @@ class AndroidAppBackend(app_backend.AppBackend): self._existing_processes_by_pid = {} @property - def _adb(self): - return self.platform_backend.adb + def _device(self): + return self.platform_backend.device def _IsAppReady(self): if self._is_app_ready_predicate is None: @@ -42,10 +42,10 @@ class AndroidAppBackend(app_backend.AppBackend): backend_settings = android_browser_backend_settings.WebviewBackendSettings( 'android-webview') with android_command_line_backend.SetUpCommandLineFlags( - self._adb, backend_settings, webview_startup_args): + self._device, backend_settings, webview_startup_args): # TODO(slamm): check if can use "blocking=True" instead of needing to # sleep. If "blocking=True" does not work, switch sleep to "ps" check. - self._adb.device().StartActivity(self._start_intent, blocking=False) + self._device.StartActivity(self._start_intent, blocking=False) util.WaitFor(self._IsAppReady, timeout=60) self._is_running = True diff --git a/tools/telemetry/telemetry/core/backends/android_command_line_backend.py b/tools/telemetry/telemetry/core/backends/android_command_line_backend.py index e05c0bb..2636fe6 100644 --- a/tools/telemetry/telemetry/core/backends/android_command_line_backend.py +++ b/tools/telemetry/telemetry/core/backends/android_command_line_backend.py @@ -35,12 +35,12 @@ class SetUpCommandLineFlags(object): Example usage: with android_command_line_backend.SetUpCommandLineFlags( - adb, backend_settings, startup_args): + device, backend_settings, startup_args): # Something to run while the command line flags are set appropriately. """ - def __init__(self, adb, backend_settings, startup_args): + def __init__(self, device, backend_settings, startup_args): self._android_command_line_backend = _AndroidCommandLineBackend( - adb, backend_settings, startup_args) + device, backend_settings, startup_args) def __enter__(self): self._android_command_line_backend.SetUpCommandLineFlags() @@ -59,15 +59,15 @@ class _AndroidCommandLineBackend(object): functionality. """ - def __init__(self, adb, backend_settings, startup_args): - self._adb = adb + def __init__(self, device, backend_settings, startup_args): + self._device = device self._backend_settings = backend_settings self._startup_args = startup_args self._saved_command_line_file_contents = None @property def command_line_file(self): - return self._backend_settings.GetCommandLineFile(self._adb.IsUserBuild()) + return self._backend_settings.GetCommandLineFile(self._device.IsUserBuild()) def SetUpCommandLineFlags(self): args = [self._backend_settings.pseudo_exec_name] @@ -100,11 +100,11 @@ class _AndroidCommandLineBackend(object): self._WriteFile(self._saved_command_line_file_contents) def _ReadFile(self): - return self._adb.device().ReadFile(self.command_line_file, as_root=True) + return self._device.ReadFile(self.command_line_file, as_root=True) def _WriteFile(self, contents): - self._adb.device().WriteFile(self.command_line_file, contents, as_root=True) + self._device.WriteFile(self.command_line_file, contents, as_root=True) def _RemoveFile(self): - self._adb.device().RunShellCommand(['rm', '-f', self.command_line_file], - as_root=True, check_return=True) + self._device.RunShellCommand(['rm', '-f', self.command_line_file], + as_root=True, check_return=True) diff --git a/tools/telemetry/telemetry/core/backends/android_command_line_backend_unittest.py b/tools/telemetry/telemetry/core/backends/android_command_line_backend_unittest.py index da57d09..c03b3c9 100644 --- a/tools/telemetry/telemetry/core/backends/android_command_line_backend_unittest.py +++ b/tools/telemetry/telemetry/core/backends/android_command_line_backend_unittest.py @@ -5,10 +5,15 @@ import unittest from telemetry import benchmark -from telemetry.core.backends import adb_commands +from telemetry.core import util from telemetry.core.backends import android_command_line_backend from telemetry.unittest_util import options_for_unittests +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib.device import device_errors # pylint: disable=import-error +from pylib.device import device_utils # pylint: disable=import-error + + class _MockBackendSettings(object): pseudo_exec_name = 'chrome' @@ -49,16 +54,19 @@ class AndroidCommandLineBackendTest(unittest.TestCase): Requires a device connected to the host. """ serial = options_for_unittests.GetCopy().device - if not serial: - serial = adb_commands.GetAttachedDevices()[0] + if serial: + device = device_utils.DeviceUtils(serial) + else: + devices = device_utils.DeviceUtils.HealthyDevices() + if not devices: + raise device_errors.NoDevicesError() + device = devices[0] cmd_file = '/data/local/tmp/test_cmd' - adb = adb_commands.AdbCommands(device=serial) backend_settings = _MockBackendSettings('/data/local/tmp/test_cmd') startup_args = ['--some', '--test', '--args'] - device = adb.device() device.WriteFile(cmd_file, 'chrome --args --to --save') with android_command_line_backend.SetUpCommandLineFlags( - adb, backend_settings, startup_args): + device, backend_settings, startup_args): self.assertEqual('chrome --some --test --args', device.ReadFile(cmd_file).strip()) self.assertEqual('chrome --args --to --save', @@ -72,16 +80,19 @@ class AndroidCommandLineBackendTest(unittest.TestCase): Requires a device connected to the host. """ serial = options_for_unittests.GetCopy().device - if not serial: - serial = adb_commands.GetAttachedDevices()[0] + if serial: + device = device_utils.DeviceUtils(serial) + else: + devices = device_utils.DeviceUtils.HealthyDevices() + if not devices: + raise device_errors.NoDevicesError() + device = devices[0] cmd_file = '/data/local/tmp/test_cmd' - adb = adb_commands.AdbCommands(device=serial) backend_settings = _MockBackendSettings('/data/local/tmp/test_cmd') startup_args = ['--some', '--test', '--args'] - device = adb.device() device.RunShellCommand(['rm', '-f', cmd_file], check_return=True) with android_command_line_backend.SetUpCommandLineFlags( - adb, backend_settings, startup_args): + device, backend_settings, startup_args): self.assertEqual('chrome --some --test --args', device.ReadFile(cmd_file).strip()) self.assertFalse(device.FileExists(cmd_file)) diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py index 1f914db..adf0997 100644 --- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py +++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py @@ -5,7 +5,6 @@ import logging import sys -from telemetry.core.backends import adb_commands from telemetry.core.backends import android_command_line_backend from telemetry.core.backends import browser_backend from telemetry.core.backends.chrome import chrome_browser_backend @@ -16,7 +15,11 @@ from telemetry.core.platform import android_platform_backend as \ from telemetry.core import util util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') -from pylib.device import intent # pylint: disable=F0401 +try: + from pylib import ports # pylint: disable=import-error +except ImportError: + ports = None +from pylib.device import intent # pylint: disable=import-error class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): @@ -43,7 +46,7 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): # TODO(tonyg): This is flaky because it doesn't reserve the port that it # allocates. Need to fix this. - self._port = adb_commands.AllocateTestServerPort() + self._port = ports.AllocateTestServerPort() # TODO(wuhu): Move to network controller backend. self.platform_backend.InstallTestCa() @@ -51,7 +54,7 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): # Kill old browser. self._KillBrowser() - if self._adb.device().old_interface.CanAccessProtectedFileContents(): + if self.device.old_interface.CanAccessProtectedFileContents(): if self.browser_options.profile_dir: self.platform_backend.PushProfile( self._backend_settings.package, @@ -73,17 +76,17 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): self.platform_backend.SetDebugApp(self._backend_settings.package) @property - def _adb(self): - return self.platform_backend.adb + def device(self): + return self.platform_backend.device def _KillBrowser(self): - if self._adb.device().IsUserBuild(): + if self.device.IsUserBuild(): self.platform_backend.StopApplication(self._backend_settings.package) else: self.platform_backend.KillApplication(self._backend_settings.package) def Start(self): - self._adb.device().RunShellCommand('logcat -c') + self.device.RunShellCommand('logcat -c') if self.browser_options.startup_url: url = self.browser_options.startup_url elif self.browser_options.profile_dir: @@ -97,15 +100,15 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): browser_startup_args = self.GetBrowserStartupArgs() with android_command_line_backend.SetUpCommandLineFlags( - self._adb, self._backend_settings, browser_startup_args): - self._adb.device().StartActivity( + self.device, self._backend_settings, browser_startup_args): + self.device.StartActivity( intent.Intent(package=self._backend_settings.package, activity=self._backend_settings.activity, action=None, data=url, category=None), blocking=True) remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort( - self._adb) + self.device) self.platform_backend.ForwardHostToDevice(self._port, remote_devtools_port) try: @@ -113,8 +116,7 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): self._InitDevtoolsClientBackend(remote_devtools_port) except exceptions.BrowserGoneException: logging.critical('Failed to connect to browser.') - device = self._adb.device() - if not device.old_interface.CanAccessProtectedFileContents(): + if not self.device.old_interface.CanAccessProtectedFileContents(): logging.critical( 'Resolve this by either: ' '(1) Flashing to a userdebug build OR ' @@ -135,15 +137,11 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): return args @property - def adb(self): - return self._adb - - @property def pid(self): - pids = self._adb.ExtractPid(self._backend_settings.package) - if not pids: + pids = self.device.GetPids(self._backend_settings.package) + if not pids or self._backend_settings.package not in pids: raise exceptions.BrowserGoneException(self.browser) - return int(pids[0]) + return int(pids[self._backend_settings.package]) @property def browser_directory(self): diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py index d4402a4..8091cd0 100644 --- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py +++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py @@ -7,7 +7,6 @@ import logging import os -from telemetry.core.backends import adb_commands from telemetry.core.backends import android_browser_backend_settings from telemetry.core.backends.chrome import android_browser_backend from telemetry.core import browser @@ -18,6 +17,9 @@ from telemetry.core import possible_browser from telemetry.core import util from telemetry import decorators +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib.utils import apk_helper # pylint: disable=import-error + CHROME_PACKAGE_NAMES = { 'android-content-shell': @@ -170,7 +172,7 @@ def _FindAllPossibleBrowsers(finder_options, android_platform): CanPossiblyHandlePath(finder_options.browser_executable)): normalized_path = os.path.expanduser(finder_options.browser_executable) - exact_package = adb_commands.GetPackageName(normalized_path) + exact_package = apk_helper.GetPackageName(normalized_path) if not exact_package: raise exceptions.PackageDetectionError( 'Unable to find package for %s specified by --browser-executable' % diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py index ae6660f..d85a298 100644 --- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py +++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py @@ -6,8 +6,12 @@ import unittest from telemetry.core.backends.chrome import android_browser_finder from telemetry.core import browser_options +from telemetry.core import util from telemetry.unittest_util import system_stub +util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock') +import mock # pylint: disable=import-error + class FakeAndroidPlatform(object): def __init__(self, can_launch): @@ -24,7 +28,7 @@ class AndroidBrowserFinderTest(unittest.TestCase): # Mock out what's needed for testing with exact APKs self._android_browser_finder_stub = system_stub.Override( - android_browser_finder, ['adb_commands', 'os']) + android_browser_finder, ['os']) def tearDown(self): self._android_browser_finder_stub.Restore() @@ -50,37 +54,39 @@ class AndroidBrowserFinderTest(unittest.TestCase): self._android_browser_finder_stub.os.path.files.append( '/foo/content-shell.apk') self.finder_options.browser_executable = '/foo/content-shell.apk' - self._android_browser_finder_stub.adb_commands.apk_package_name = \ - 'org.chromium.content_shell_apk' - fake_platform = FakeAndroidPlatform(can_launch=True) - expected_types = set( - android_browser_finder.FindAllBrowserTypes(self.finder_options)) - possible_browsers = android_browser_finder._FindAllPossibleBrowsers( - self.finder_options, fake_platform) - self.assertEqual( - expected_types, - set([b.browser_type for b in possible_browsers])) + with mock.patch('pylib.utils.apk_helper.GetPackageName', + return_value='org.chromium.content_shell_apk'): + fake_platform = FakeAndroidPlatform(can_launch=True) + expected_types = set( + android_browser_finder.FindAllBrowserTypes(self.finder_options)) + possible_browsers = android_browser_finder._FindAllPossibleBrowsers( + self.finder_options, fake_platform) + self.assertEqual( + expected_types, + set([b.browser_type for b in possible_browsers])) def testErrorWithUnknownExactApk(self): self._android_browser_finder_stub.os.path.files.append( '/foo/content-shell.apk') self.finder_options.browser_executable = '/foo/content-shell.apk' - self._android_browser_finder_stub.adb_commands.apk_package_name = \ - 'org.unknown.app' - fake_platform = FakeAndroidPlatform(can_launch=True) - self.assertRaises(Exception, - android_browser_finder._FindAllPossibleBrowsers, - self.finder_options, fake_platform) + with mock.patch('pylib.utils.apk_helper.GetPackageName', + return_value='org.unknown.app'): + fake_platform = FakeAndroidPlatform(can_launch=True) + self.assertRaises(Exception, + android_browser_finder._FindAllPossibleBrowsers, + self.finder_options, fake_platform) def testErrorWithNonExistantExactApk(self): self.finder_options.browser_executable = '/foo/content-shell.apk' - fake_platform = FakeAndroidPlatform(can_launch=True) - self.assertRaises(Exception, - android_browser_finder._FindAllPossibleBrowsers, - self.finder_options, fake_platform) + with mock.patch('pylib.utils.apk_helper.GetPackageName', + return_value='org.chromium.content_shell_apk'): + fake_platform = FakeAndroidPlatform(can_launch=True) + self.assertRaises(Exception, + android_browser_finder._FindAllPossibleBrowsers, + self.finder_options, fake_platform) class FakePossibleBrowser(object): diff --git a/tools/telemetry/telemetry/core/forwarders/android_forwarder.py b/tools/telemetry/telemetry/core/forwarders/android_forwarder.py index ed8ed7b..d0c9eda 100644 --- a/tools/telemetry/telemetry/core/forwarders/android_forwarder.py +++ b/tools/telemetry/telemetry/core/forwarders/android_forwarder.py @@ -9,7 +9,6 @@ import socket import struct import subprocess -from telemetry.core.backends import adb_commands from telemetry.core import forwarders from telemetry.core import platform from telemetry.core import util @@ -17,27 +16,28 @@ from telemetry.util import support_binaries util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') try: - from pylib import forwarder # pylint: disable=F0401 + from pylib import forwarder # pylint: disable=import-error except ImportError: forwarder = None -from pylib.device import device_errors # pylint: disable=F0401 +from pylib.device import device_errors # pylint: disable=import-error +from pylib.device import device_utils # pylint: disable=import-error class AndroidForwarderFactory(forwarders.ForwarderFactory): - def __init__(self, adb, use_rndis): + def __init__(self, device, use_rndis): super(AndroidForwarderFactory, self).__init__() - self._adb = adb + self._device = device self._rndis_configurator = None if use_rndis: - self._rndis_configurator = AndroidRndisConfigurator(self._adb) + self._rndis_configurator = AndroidRndisConfigurator(self._device) def Create(self, port_pairs): if self._rndis_configurator: - return AndroidRndisForwarder(self._adb, self._rndis_configurator, + return AndroidRndisForwarder(self._device, self._rndis_configurator, port_pairs) - return AndroidForwarder(self._adb, port_pairs) + return AndroidForwarder(self._device, port_pairs) @property def host_ip(self): @@ -52,9 +52,9 @@ class AndroidForwarderFactory(forwarders.ForwarderFactory): class AndroidForwarder(forwarders.Forwarder): - def __init__(self, adb, port_pairs): + def __init__(self, device, port_pairs): super(AndroidForwarder, self).__init__(port_pairs) - self._device = adb.device() + self._device = device forwarder.Forwarder.Map([(p.remote_port, p.local_port) for p in port_pairs if p], self._device) self._port_pairs = forwarders.PortPairs(*[ @@ -74,10 +74,10 @@ class AndroidForwarder(forwarders.Forwarder): class AndroidRndisForwarder(forwarders.Forwarder): """Forwards traffic using RNDIS. Assumes the device has root access.""" - def __init__(self, adb, rndis_configurator, port_pairs): + def __init__(self, device, rndis_configurator, port_pairs): super(AndroidRndisForwarder, self).__init__(port_pairs) - self._adb = adb + self._device = device self._rndis_configurator = rndis_configurator self._device_iface = rndis_configurator.device_iface self._host_ip = rndis_configurator.host_ip @@ -103,12 +103,13 @@ class AndroidRndisForwarder(forwarders.Forwarder): def _RedirectPorts(self, port_pairs): """Sets the local to remote pair mappings to use for RNDIS.""" - self._adb.RunShellCommand('iptables -F -t nat') # Flush any old nat rules. + # Flush any old nat rules. + self._device.RunShellCommand('iptables -F -t nat') for port_pair in port_pairs: if not port_pair or port_pair.local_port == port_pair.remote_port: continue protocol = 'udp' if port_pair.remote_port == 53 else 'tcp' - self._adb.RunShellCommand( + self._device.RunShellCommand( 'iptables -t nat -A OUTPUT -p %s --dport %d' ' -j DNAT --to-destination %s:%d' % (protocol, port_pair.remote_port, self.host_ip, port_pair.local_port)) @@ -129,27 +130,27 @@ class AndroidRndisForwarder(forwarders.Forwarder): return # If there is no route, then nobody cares about DNS. # DNS proxy in older versions of Android is configured via properties. # TODO(szym): run via su -c if necessary. - self._adb.device().SetProp('net.dns1', dns1) - self._adb.device().SetProp('net.dns2', dns2) - dnschange = self._adb.device().GetProp('net.dnschange') + self._device.SetProp('net.dns1', dns1) + self._device.SetProp('net.dns2', dns2) + dnschange = self._device.GetProp('net.dnschange') if dnschange: - self._adb.device().SetProp('net.dnschange', str(int(dnschange) + 1)) + self._device.SetProp('net.dnschange', str(int(dnschange) + 1)) # Since commit 8b47b3601f82f299bb8c135af0639b72b67230e6 to frameworks/base # the net.dns1 properties have been replaced with explicit commands for netd - self._adb.RunShellCommand('netd resolver setifdns %s %s %s' % - (iface, dns1, dns2)) + self._device.RunShellCommand('netd resolver setifdns %s %s %s' % + (iface, dns1, dns2)) # TODO(szym): if we know the package UID, we could setifaceforuidrange - self._adb.RunShellCommand('netd resolver setdefaultif %s' % iface) + self._device.RunShellCommand('netd resolver setdefaultif %s' % iface) def _GetCurrentDns(self): """Returns current gateway, dns1, and dns2.""" - routes = self._adb.RunShellCommand('cat /proc/net/route')[1:] + routes = self._device.RunShellCommand('cat /proc/net/route')[1:] routes = [route.split() for route in routes] default_routes = [route[0] for route in routes if route[1] == '00000000'] return ( default_routes[0] if default_routes else None, - self._adb.device().GetProp('net.dns1'), - self._adb.device().GetProp('net.dns2'), + self._device.GetProp('net.dns1'), + self._device.GetProp('net.dns2'), ) def _OverrideDefaultGateway(self): @@ -162,11 +163,11 @@ class AndroidRndisForwarder(forwarders.Forwarder): (e.g. Telemetry crashes). A power cycle or "adb reboot" is a simple workaround around in that case. """ - self._adb.RunShellCommand('route add default gw %s dev %s' % + self._device.RunShellCommand('route add default gw %s dev %s' % (self.host_ip, self._device_iface)) def _RestoreDefaultGateway(self): - self._adb.RunShellCommand('netcfg %s down' % self._device_iface) + self._device.RunShellCommand('netcfg %s down' % self._device_iface) class AndroidRndisConfigurator(object): @@ -182,8 +183,8 @@ class AndroidRndisConfigurator(object): _INTERFACES_INCLUDE = 'source /etc/network/interfaces.d/*.conf' _TELEMETRY_INTERFACE_FILE = '/etc/network/interfaces.d/telemetry-{}.conf' - def __init__(self, adb): - self._device = adb.device() + def __init__(self, device): + self._device = device try: self._device.EnableRoot() @@ -386,9 +387,8 @@ doit & """ my_device = str(self._device) addresses = [] - for device_serial in adb_commands.GetAttachedDevices(): - device = adb_commands.AdbCommands(device_serial).device() - if device_serial == my_device: + for device in device_utils.DeviceUtils.HealthyDevices(): + if device.adb.GetDeviceSerial() == my_device: excluded = excluded_iface else: excluded = 'no interfaces excluded on other devices' diff --git a/tools/telemetry/telemetry/core/platform/android_action_runner.py b/tools/telemetry/telemetry/core/platform/android_action_runner.py index c2f79b4..1e87445 100644 --- a/tools/telemetry/telemetry/core/platform/android_action_runner.py +++ b/tools/telemetry/telemetry/core/platform/android_action_runner.py @@ -67,7 +67,7 @@ class AndroidActionRunner(object): Args: string: The string to send to the device. """ - self._platform_backend.adb.RunShellCommand('input text %s' % string) + self._platform_backend.device.RunShellCommand('input text %s' % string) def InputKeyEvent(self, key): """Send a single key input to the device. @@ -75,7 +75,7 @@ class AndroidActionRunner(object): Args: key: A key code number or name that will be sent to the device """ - self._platform_backend.adb.RunShellCommand('input keyevent %s' % key) + self._platform_backend.device.SendKeyEvent(key) def InputTap(self, x_coord, y_coord): """Perform a tap input at the given coordinates. @@ -84,8 +84,8 @@ class AndroidActionRunner(object): x_coord: The x coordinate of the tap event. y_coord: The y coordinate of the tap event. """ - self._platform_backend.adb.RunShellCommand('input tap %s %s' % (x_coord, - y_coord)) + self._platform_backend.device.RunShellCommand( + 'input tap %s %s' % (x_coord, y_coord)) def InputSwipe(self, left_start_coord, top_start_coord, left_end_coord, top_end_coord, duration): @@ -98,14 +98,14 @@ class AndroidActionRunner(object): top_end_coord: The vertical ending coordinate of the gesture duration: The length of time of the swipe in milliseconds """ - self._platform_backend.adb.RunShellCommand( + self._platform_backend.device.RunShellCommand( 'input swipe %s %s %s %s %s' % (left_start_coord, top_start_coord, left_end_coord, top_end_coord, duration)) def InputPress(self): """Perform a press input.""" - self._platform_backend.adb.RunShellCommand('input press') + self._platform_backend.device.RunShellCommand('input press') def InputRoll(self, dx, dy): """Perform a roll input. This sends a simple zero-pressure move event. @@ -114,7 +114,7 @@ class AndroidActionRunner(object): dx: Change in the x coordinate due to move. dy: Change in the y coordinate due to move. """ - self._platform_backend.adb.RunShellCommand('input roll %s %s' % (dx, dy)) + self._platform_backend.device.RunShellCommand('input roll %s %s' % (dx, dy)) def EnsureScreenOn(self): """If device screen is off, turn screen on. @@ -173,7 +173,7 @@ class AndroidActionRunner(object): return not self._platform_backend.IsScreenLocked() if self._platform_backend.IsScreenLocked(): - self._platform_backend.adb.RunShellCommand('input keyevent 82') + self._platform_backend.device.SendKeyEvent(82) else: logging.warning('Screen not locked when expected.') return @@ -181,4 +181,4 @@ class AndroidActionRunner(object): util.WaitFor(is_screen_unlocked, 5) def _ToggleScreenOn(self): - self._platform_backend.adb.RunShellCommand('input keyevent 26') + self._platform_backend.device.SendKeyEvent(26) diff --git a/tools/telemetry/telemetry/core/platform/android_device.py b/tools/telemetry/telemetry/core/platform/android_device.py index c7dc378..1c5fac4 100644 --- a/tools/telemetry/telemetry/core/platform/android_device.py +++ b/tools/telemetry/telemetry/core/platform/android_device.py @@ -1,17 +1,19 @@ # Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. + import logging import os -import re -import subprocess -import sys -from telemetry.core.backends import adb_commands from telemetry.core.platform import device from telemetry.core.platform.profiler import monsoon from telemetry.core import util +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib import constants # pylint: disable=import-error +from pylib.device import device_errors # pylint: disable=import-error +from pylib.device import device_utils # pylint: disable=import-error + class AndroidDevice(device.Device): """ Class represents information for connecting to an android device. @@ -44,7 +46,8 @@ class AndroidDevice(device.Device): def GetDeviceSerials(): - device_serials = adb_commands.GetAttachedDevices() + device_serials = [d.adb.GetDeviceSerial() + for d in device_utils.DeviceUtils.HealthyDevices()] # The monsoon provides power for the device, so for devices with no # real battery, we need to turn them on after the monsoon enables voltage # output to the device. @@ -65,8 +68,9 @@ The Monsoon's power output has been enabled. Please now ensure that: Waiting for device... """) - util.WaitFor(adb_commands.GetAttachedDevices, 600) - device_serials = adb_commands.GetAttachedDevices() + util.WaitFor(device_utils.DeviceUtils.HealthyDevices, 600) + device_serials = [d.adb.GetDeviceSerial() + for d in device_utils.DeviceUtils.HealthyDevices()] except IOError: return [] return device_serials @@ -98,33 +102,16 @@ def GetDevice(finder_options): def CanDiscoverDevices(): """Returns true if devices are discoverable via adb.""" - if not adb_commands.IsAndroidSupported(): - logging.info( - 'Android build commands unavailable on this machine. ' - 'Have you installed Android build dependencies?') + adb_path = constants.GetAdbPath() + if os.path.isabs(adb_path) and not os.path.exists(adb_path): return False + try: - with open(os.devnull, 'w') as devnull: - adb_process = subprocess.Popen( - ['adb', 'devices'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stdin=devnull) - stdout = adb_process.communicate()[0] - if re.search(re.escape('????????????\tno permissions'), stdout) != None: - logging.warn('adb devices gave a permissions error. ' - 'Consider running adb as root:') - logging.warn(' adb kill-server') - logging.warn(' sudo `which adb` devices\n\n') - return True - except OSError: - pass - chromium_adb_path = os.path.join( - util.GetChromiumSrcDir(), 'third_party', 'android_tools', 'sdk', - 'platform-tools', 'adb') - if sys.platform.startswith('linux') and os.path.exists(chromium_adb_path): - os.environ['PATH'] = os.pathsep.join( - [os.path.dirname(chromium_adb_path), os.environ['PATH']]) + device_utils.DeviceUtils.HealthyDevices() return True - return False + except (device_errors.CommandFailedError, device_errors.CommandTimeoutError, + OSError): + return False def FindAllAvailableDevices(_): diff --git a/tools/telemetry/telemetry/core/platform/android_device_unittest.py b/tools/telemetry/telemetry/core/platform/android_device_unittest.py index b8a61dd..ffc09cf 100644 --- a/tools/telemetry/telemetry/core/platform/android_device_unittest.py +++ b/tools/telemetry/telemetry/core/platform/android_device_unittest.py @@ -5,135 +5,142 @@ import unittest from telemetry.core import browser_options +from telemetry.core import util from telemetry.core.platform import android_device -from telemetry.core.platform import android_platform_backend from telemetry.unittest_util import system_stub +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib.device import device_utils # pylint: disable=import-error + +util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock') +import mock # pylint: disable=import-error + class AndroidDeviceTest(unittest.TestCase): - def setUp(self): - self._android_device_stub = system_stub.Override( - android_device, ['adb_commands']) def testGetAllAttachedAndroidDevices(self): - self._android_device_stub.adb_commands.attached_devices = [ - '01', '02'] - self.assertEquals( - set(['01', '02']), - set(device.device_id for device in - android_device.AndroidDevice.GetAllConnectedDevices() - )) - - def tearDown(self): - self._android_device_stub.Restore() + with mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('01'), + device_utils.DeviceUtils('02')]): + self.assertEquals( + set(['01', '02']), + set(device.device_id for device in + android_device.AndroidDevice.GetAllConnectedDevices())) class GetDeviceTest(unittest.TestCase): def setUp(self): self._android_device_stub = system_stub.Override( - android_device, ['adb_commands', 'os', 'subprocess', 'logging']) - self._apb_stub = system_stub.Override( - android_platform_backend, ['adb_commands']) + android_device, ['subprocess', 'logging']) def tearDown(self): self._android_device_stub.Restore() - self._apb_stub.Restore() def testNoAdbReturnsNone(self): finder_options = browser_options.BrowserFinderOptions() - - def NoAdb(*_1, **_2): - raise OSError('not found') - self._android_device_stub.subprocess.Popen = NoAdb - - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertIsNone(android_device.GetDevice(finder_options)) + with ( + mock.patch('os.path.isabs', return_value=True)), ( + mock.patch('os.path.exists', return_value=False)): + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertIsNone(android_device.GetDevice(finder_options)) def testAdbNoDevicesReturnsNone(self): finder_options = browser_options.BrowserFinderOptions() - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertIsNone(android_device.GetDevice(finder_options)) - - def testAdbPermissionsErrorReturnsNone(self): - finder_options = browser_options.BrowserFinderOptions() - self._android_device_stub.subprocess.Popen.communicate_result = ( - 'List of devices attached\n????????????\tno permissions\n', - '* daemon not running. starting it now on port 5037 *\n' - '* daemon started successfully *\n') - device = android_device.GetDevice(finder_options) - self.assertEquals([ - 'adb devices gave a permissions error. Consider running adb as root:', - ' adb kill-server', - ' sudo `which adb` devices\n\n'], - self._android_device_stub.logging.warnings) - self.assertIsNone(device) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[])): + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertIsNone(android_device.GetDevice(finder_options)) def testAdbTwoDevicesReturnsNone(self): finder_options = browser_options.BrowserFinderOptions() - self._android_device_stub.adb_commands.attached_devices = [ - '015d14fec128220c', '015d14fec128220d'] - device = android_device.GetDevice(finder_options) - self.assertEquals([ - 'Multiple devices attached. Please specify one of the following:\n' - ' --device=015d14fec128220c\n' - ' --device=015d14fec128220d'], - self._android_device_stub.logging.warnings) - self.assertIsNone(device) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('015d14fec128220c'), + device_utils.DeviceUtils('015d14fec128220d')])): + device = android_device.GetDevice(finder_options) + self.assertEquals([ + 'Multiple devices attached. Please specify one of the following:\n' + ' --device=015d14fec128220c\n' + ' --device=015d14fec128220d'], + self._android_device_stub.logging.warnings) + self.assertIsNone(device) def testAdbPickOneDeviceReturnsDeviceInstance(self): finder_options = browser_options.BrowserFinderOptions() finder_options.device = '555d14fecddddddd' # pick one - self._android_device_stub.adb_commands.attached_devices = [ - '015d14fec128220c', '555d14fecddddddd'] - device = android_device.GetDevice(finder_options) - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertEquals('555d14fecddddddd', device.device_id) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('015d14fec128220c'), + device_utils.DeviceUtils('555d14fecddddddd')])): + device = android_device.GetDevice(finder_options) + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertEquals('555d14fecddddddd', device.device_id) def testAdbOneDeviceReturnsDeviceInstance(self): finder_options = browser_options.BrowserFinderOptions() - self._android_device_stub.adb_commands.attached_devices = ( - ['015d14fec128220c']) - device = android_device.GetDevice(finder_options) - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertEquals('015d14fec128220c', device.device_id) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('015d14fec128220c')])): + device = android_device.GetDevice(finder_options) + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertEquals('015d14fec128220c', device.device_id) class FindAllAvailableDevicesTest(unittest.TestCase): def setUp(self): self._android_device_stub = system_stub.Override( - android_device, ['adb_commands', 'os', 'subprocess', 'logging']) - self._apb_stub = system_stub.Override( - android_platform_backend, ['adb_commands']) + android_device, ['subprocess', 'logging']) def tearDown(self): self._android_device_stub.Restore() - self._apb_stub.Restore() def testAdbNoDeviceReturnsEmptyList(self): finder_options = browser_options.BrowserFinderOptions() - devices = android_device.FindAllAvailableDevices(finder_options) - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertIsNotNone(devices) - self.assertEquals(len(devices), 0) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch( + 'pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[])): + devices = android_device.FindAllAvailableDevices(finder_options) + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertIsNotNone(devices) + self.assertEquals(len(devices), 0) def testAdbOneDeviceReturnsListWithOneDeviceInstance(self): finder_options = browser_options.BrowserFinderOptions() - self._android_device_stub.adb_commands.attached_devices = ( - ['015d14fec128220c']) - devices = android_device.FindAllAvailableDevices(finder_options) - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertIsNotNone(devices) - self.assertEquals(len(devices), 1) - self.assertEquals('015d14fec128220c', devices[0].device_id) + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('015d14fec128220c')])): + devices = android_device.FindAllAvailableDevices(finder_options) + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertIsNotNone(devices) + self.assertEquals(len(devices), 1) + self.assertEquals('015d14fec128220c', devices[0].device_id) def testAdbMultipleDevicesReturnsListWithAllDeviceInstances(self): finder_options = browser_options.BrowserFinderOptions() - self._android_device_stub.adb_commands.attached_devices = [ - '015d14fec128220c', '015d14fec128220d', '015d14fec128220e'] - devices = android_device.FindAllAvailableDevices(finder_options) - self.assertEquals([], self._android_device_stub.logging.warnings) - self.assertIsNotNone(devices) - self.assertEquals(len(devices), 3) - self.assertEquals(devices[0].guid, '015d14fec128220c') - self.assertEquals(devices[1].guid, '015d14fec128220d') - self.assertEquals(devices[2].guid, '015d14fec128220e') + with ( + mock.patch('os.path.isabs', return_value=False)), ( + mock.patch('pylib.device.device_utils.DeviceUtils.HealthyDevices', + return_value=[ + device_utils.DeviceUtils('015d14fec128220c'), + device_utils.DeviceUtils('015d14fec128220d'), + device_utils.DeviceUtils('015d14fec128220e')])): + devices = android_device.FindAllAvailableDevices(finder_options) + self.assertEquals([], self._android_device_stub.logging.warnings) + self.assertIsNotNone(devices) + self.assertEquals(len(devices), 3) + self.assertEquals(devices[0].guid, '015d14fec128220c') + self.assertEquals(devices[1].guid, '015d14fec128220d') + self.assertEquals(devices[2].guid, '015d14fec128220e') diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py index 579f43d..67a8b29 100644 --- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py +++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py @@ -6,10 +6,10 @@ import logging import os import re import shutil +import stat import subprocess import tempfile -from telemetry.core.backends import adb_commands from telemetry.core import exceptions from telemetry.core.forwarders import android_forwarder from telemetry.core import platform @@ -27,6 +27,7 @@ from telemetry.core import video from telemetry import decorators from telemetry.util import exception_formatter from telemetry.util import external_modules +from telemetry.util import support_binaries psutil = external_modules.ImportOptionalModule('psutil') util.AddDirToPythonPath(util.GetChromiumSrcDir(), @@ -37,14 +38,15 @@ import platformsettings # pylint: disable=import-error # Get build/android scripts into our path. util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') -from pylib import constants # pylint: disable=import-error +from pylib import constants # pylint: disable=import-error from pylib import screenshot # pylint: disable=import-error -from pylib.device import battery_utils # pylint: disable=import-error +from pylib.device import battery_utils # pylint: disable=import-error from pylib.device import device_errors # pylint: disable=import-error -from pylib.perf import cache_control # pylint: disable=import-error -from pylib.perf import perf_control # pylint: disable=import-error +from pylib.device import device_utils # pylint: disable=import-error +from pylib.perf import cache_control # pylint: disable=import-error +from pylib.perf import perf_control # pylint: disable=import-error from pylib.perf import thermal_throttle # pylint: disable=import-error -from pylib.utils import device_temp_file # pylint: disable=import-error +from pylib.utils import device_temp_file # pylint: disable=import-error try: from pylib.perf import surface_stats_collector # pylint: disable=import-error @@ -59,14 +61,81 @@ _DEVICE_COPY_SCRIPT_LOCATION = ( '/data/local/tmp/efficient_android_directory_copy.sh') +def _SetupPrebuiltTools(device): + """Some of the android pylib scripts we depend on are lame and expect + binaries to be in the out/ directory. So we copy any prebuilt binaries there + as a prereq.""" + + # TODO(bulach): Build the targets for x86/mips. + device_tools = [ + 'file_poller', + 'forwarder_dist/device_forwarder', + 'md5sum_dist/md5sum_bin', + 'purge_ashmem', + 'run_pie', + ] + + host_tools = [ + 'bitmaptools', + 'md5sum_bin_host', + ] + + if platform.GetHostPlatform().GetOSName() == 'linux': + host_tools.append('host_forwarder') + + arch_name = device.product_cpu_abi + has_device_prebuilt = (arch_name.startswith('armeabi') + or arch_name.startswith('arm64')) + if not has_device_prebuilt: + logging.warning('Unknown architecture type: %s' % arch_name) + return all([support_binaries.FindLocallyBuiltPath(t) for t in device_tools]) + + build_type = None + for t in device_tools + host_tools: + executable = os.path.basename(t) + locally_built_path = support_binaries.FindLocallyBuiltPath(t) + if not build_type: + build_type = _GetBuildTypeOfPath(locally_built_path) or 'Release' + constants.SetBuildType(build_type) + dest = os.path.join(constants.GetOutDirectory(), t) + if not locally_built_path: + logging.info('Setting up prebuilt %s', dest) + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + platform_name = ('android' if t in device_tools else + platform.GetHostPlatform().GetOSName()) + bin_arch_name = (arch_name if t in device_tools else + platform.GetHostPlatform().GetArchName()) + prebuilt_path = support_binaries.FindPath( + executable, bin_arch_name, platform_name) + if not prebuilt_path or not os.path.exists(prebuilt_path): + raise NotImplementedError(""" +%s must be checked into cloud storage. +Instructions: +http://www.chromium.org/developers/telemetry/upload_to_cloud_storage +""" % t) + shutil.copyfile(prebuilt_path, dest) + os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + return True + + +def _GetBuildTypeOfPath(path): + if not path: + return None + for build_dir, build_type in util.GetBuildDirectories(): + if os.path.join(build_dir, build_type) in path: + return build_type + return None + + class AndroidPlatformBackend( linux_based_platform_backend.LinuxBasedPlatformBackend): def __init__(self, device, finder_options): assert device, ( 'AndroidPlatformBackend can only be initialized from remote device') super(AndroidPlatformBackend, self).__init__(device) - self._adb = adb_commands.AdbCommands(device=device.device_id) - installed_prebuilt_tools = adb_commands.SetupPrebuiltTools(self._adb) + self._device = device_utils.DeviceUtils(device.device_id) + installed_prebuilt_tools = _SetupPrebuiltTools(self._device) if not installed_prebuilt_tools: logging.error( '%s detected, however prebuilt android tools could not ' @@ -74,10 +143,9 @@ class AndroidPlatformBackend( ' $ ninja -C out/Release android_tools' % device.name) raise exceptions.PlatformError() # Trying to root the device, if possible. - if not self._adb.IsRootEnabled(): + if not self._device.HasRoot(): # Ignore result. - self._adb.EnableAdbRoot() - self._device = self._adb.device() + self._device.EnableRoot() self._battery = battery_utils.BatteryUtils(self._device) self._enable_performance_mode = device.enable_performance_mode self._surface_stats_collector = None @@ -122,7 +190,7 @@ class AndroidPlatformBackend( def forwarder_factory(self): if not self._forwarder_factory: self._forwarder_factory = android_forwarder.AndroidForwarderFactory( - self._adb, self._use_rndis_forwarder) + self._device, self._use_rndis_forwarder) return self._forwarder_factory @@ -131,8 +199,8 @@ class AndroidPlatformBackend( return self._use_rndis_forwarder @property - def adb(self): - return self._adb + def device(self): + return self._device def IsDisplayTracingSupported(self): return bool(self.GetOSVersionName() >= 'J') @@ -423,7 +491,7 @@ class AndroidPlatformBackend( return old_flag def ForwardHostToDevice(self, host_port, device_port): - self._adb.Forward('tcp:%d' % host_port, device_port) + self._device.adb.Forward('tcp:%d' % host_port, device_port) def DismissCrashDialogIfNeeded(self): """Dismiss any error dialogs. @@ -440,8 +508,7 @@ class AndroidPlatformBackend( Args: process_name: The full package name string of the process. """ - pids = self._adb.ExtractPid(process_name) - return len(pids) != 0 + return bool(self._device.GetPids(process_name)) @property def wpr_ca_cert_path(self): @@ -477,9 +544,9 @@ class AndroidPlatformBackend( certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(), cert_path=self._wpr_ca_cert_path) self._device_cert_util = adb_install_cert.AndroidCertInstaller( - self._adb.device_serial(), None, self._wpr_ca_cert_path) + self._device.adb.GetDeviceSerial(), None, self._wpr_ca_cert_path) logging.info('Installing test certificate authority on device: %s', - self._adb.device_serial()) + str(self._device)) self._device_cert_util.install_cert(overwrite_cert=True) self._is_test_ca_installed = True except Exception as e: @@ -488,7 +555,7 @@ class AndroidPlatformBackend( logging.warning( 'Unable to install test certificate authority on device: %s. ' 'Will fallback to ignoring certificate errors. Install error: %s', - self._adb.device_serial(), e) + str(self._device), e) @property def is_test_ca_installed(self): @@ -509,7 +576,7 @@ class AndroidPlatformBackend( # Best effort cleanup - show the error and continue. exception_formatter.PrintFormattedException( msg=('Error while trying to remove certificate authority: %s. ' - % self._adb.device_serial())) + % str(self._device))) self._is_test_ca_installed = False shutil.rmtree(os.path.dirname(self._wpr_ca_cert_path), ignore_errors=True) @@ -620,7 +687,7 @@ class AndroidPlatformBackend( Args: package: The full package name string of the application. """ - if self._adb.IsUserBuild(): + if self._device.IsUserBuild(): logging.debug('User build device, setting debug app') self._device.RunShellCommand('am set-debug-app --persistent %s' % package) @@ -664,7 +731,7 @@ class AndroidPlatformBackend( if os.path.exists(tombstones): ret += Decorate('Tombstones', subprocess.Popen([tombstones, '-w', '--device', - self._adb.device_serial()], + self._device.adb.GetDeviceSerial()], stdout=subprocess.PIPE).communicate()[0]) return ret diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py index 1db861b..c883e28 100644 --- a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py +++ b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py @@ -12,16 +12,17 @@ from telemetry.unittest_util import options_for_unittests from telemetry.unittest_util import system_stub util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock') -import mock # pylint: disable=F0401 +import mock # pylint: disable=import-error util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') -from pylib.device import battery_utils # pylint: disable=F0401 +from pylib.device import battery_utils # pylint: disable=import-error +from pylib.device import device_utils # pylint: disable=import-error class AndroidPlatformBackendTest(unittest.TestCase): def setUp(self): self._options = options_for_unittests.GetCopy() self._stubs = system_stub.Override( android_platform_backend, - ['perf_control', 'thermal_throttle', 'adb_commands', 'certutils', + ['perf_control', 'thermal_throttle', 'certutils', 'adb_install_cert', 'platformsettings']) # Skip _FixPossibleAdbInstability by setting psutil to None. @@ -30,9 +31,25 @@ class AndroidPlatformBackendTest(unittest.TestCase): self.battery_patcher = mock.patch.object(battery_utils, 'BatteryUtils') self.battery_patcher.start() + def get_prop(name, cache=None): + return {'ro.product.cpu.abi': 'armeabi-v7a'}.get(name) + + self.helper_patcher = mock.patch( + 'telemetry.core.platform.android_platform_backend._SetupPrebuiltTools', + return_value=True) + self.helper_patcher.start() + + self.device_patcher = mock.patch.multiple( + device_utils.DeviceUtils, + HasRoot=mock.MagicMock(return_value=True), + GetProp=mock.MagicMock(side_effect=get_prop)) + self.device_patcher.start() + def tearDown(self): self._stubs.Restore() android_platform_backend.psutil = self._actual_ps_util + self.device_patcher.stop() + self.helper_patcher.stop() self.battery_patcher.stop() @decorators.Disabled('chromeos') @@ -43,20 +60,23 @@ class AndroidPlatformBackendTest(unittest.TestCase): '4294967295 1074458624 1074463824 3197495984 3197494152 ' '1074767676 0 4612 0 38136 4294967295 0 0 17 0 0 0 0 0 0 ' '1074470376 1074470912 1102155776\n') - self._stubs.adb_commands.adb_device.mock_content = proc_stat_content - self._stubs.adb_commands.adb_device.has_root = True - backend = android_platform_backend.AndroidPlatformBackend( - android_device.AndroidDevice('12345'), self._options) - cpu_stats = backend.GetCpuStats('7702') - self.assertEquals(cpu_stats, {'CpuProcessTime': 0.05}) + + with mock.patch('pylib.device.device_utils.DeviceUtils.ReadFile', + return_value=proc_stat_content): + backend = android_platform_backend.AndroidPlatformBackend( + android_device.AndroidDevice('12345'), self._options) + cpu_stats = backend.GetCpuStats('7702') + self.assertEquals(cpu_stats, {'CpuProcessTime': 0.05}) @decorators.Disabled('chromeos') def testGetCpuStatsInvalidPID(self): # Mock an empty /proc/pid/stat. - backend = android_platform_backend.AndroidPlatformBackend( - android_device.AndroidDevice('1234'), self._options) - cpu_stats = backend.GetCpuStats('7702') - self.assertEquals(cpu_stats, {}) + with mock.patch('pylib.device.device_utils.DeviceUtils.ReadFile', + return_value=''): + backend = android_platform_backend.AndroidPlatformBackend( + android_device.AndroidDevice('1234'), self._options) + cpu_stats = backend.GetCpuStats('7702') + self.assertEquals(cpu_stats, {}) def testAndroidParseCpuStates(self): cstate = { @@ -162,15 +182,33 @@ class AndroidPlatformBackendPsutilTest(unittest.TestCase): def setUp(self): self._options = options_for_unittests.GetCopy() self._stubs = system_stub.Override( - android_platform_backend, - ['perf_control', 'adb_commands']) + android_platform_backend, ['perf_control']) + self.battery_patcher = mock.patch.object(battery_utils, 'BatteryUtils') self.battery_patcher.start() + + def get_prop(name, cache=None): + return {'ro.product.cpu.abi': 'armeabi-v7a'}.get(name) + + self.helper_patcher = mock.patch( + 'telemetry.core.platform.android_platform_backend._SetupPrebuiltTools', + return_value=True) + self.helper_patcher.start() + + self.device_patcher = mock.patch.multiple( + device_utils.DeviceUtils, + FileExists=mock.MagicMock(return_value=False), + GetProp=mock.MagicMock(side_effect=get_prop), + HasRoot=mock.MagicMock(return_value=True)) + self.device_patcher.start() + self._actual_ps_util = android_platform_backend.psutil def tearDown(self): self._stubs.Restore() android_platform_backend.psutil = self._actual_ps_util + self.device_patcher.stop() + self.helper_patcher.stop() self.battery_patcher.stop() @decorators.Disabled('chromeos') @@ -179,11 +217,13 @@ class AndroidPlatformBackendPsutilTest(unittest.TestCase): android_platform_backend.psutil = psutil # Mock an empty /proc/pid/stat. - backend = android_platform_backend.AndroidPlatformBackend( - android_device.AndroidDevice('1234'), self._options) - cpu_stats = backend.GetCpuStats('7702') - self.assertEquals({}, cpu_stats) - self.assertEquals([[0]], psutil.set_cpu_affinity_args) + with mock.patch('pylib.device.device_utils.DeviceUtils.ReadFile', + return_value=''): + backend = android_platform_backend.AndroidPlatformBackend( + android_device.AndroidDevice('1234'), self._options) + cpu_stats = backend.GetCpuStats('7702') + self.assertEquals({}, cpu_stats) + self.assertEquals([[0]], psutil.set_cpu_affinity_args) @decorators.Disabled('chromeos') def testPsutil2(self): @@ -191,8 +231,10 @@ class AndroidPlatformBackendPsutilTest(unittest.TestCase): android_platform_backend.psutil = psutil # Mock an empty /proc/pid/stat. - backend = android_platform_backend.AndroidPlatformBackend( - android_device.AndroidDevice('1234'), self._options) - cpu_stats = backend.GetCpuStats('7702') - self.assertEquals({}, cpu_stats) - self.assertEquals([[0]], psutil.set_cpu_affinity_args) + with mock.patch('pylib.device.device_utils.DeviceUtils.ReadFile', + return_value=''): + backend = android_platform_backend.AndroidPlatformBackend( + android_device.AndroidDevice('1234'), self._options) + cpu_stats = backend.GetCpuStats('7702') + self.assertEquals({}, cpu_stats) + self.assertEquals([[0]], psutil.set_cpu_affinity_args) diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_prebuilt_profiler_helper.py b/tools/telemetry/telemetry/core/platform/profiler/android_prebuilt_profiler_helper.py index 5974f1d..573df36 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/android_prebuilt_profiler_helper.py +++ b/tools/telemetry/telemetry/core/platform/profiler/android_prebuilt_profiler_helper.py @@ -19,7 +19,7 @@ def GetDevicePath(profiler_binary): @decorators.Cache def InstallOnDevice(device, profiler_binary): - arch_name = device.GetABI() + arch_name = device.product_cpu_abi host_path = support_binaries.FindPath(profiler_binary, arch_name, 'android') if not host_path: logging.error('Profiler binary "%s" not found. Could not be installed', diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py index 5e3aad8..6ea83ff 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/android_screen_recorder_profiler.py @@ -22,7 +22,7 @@ class AndroidScreenRecordingProfiler(profiler.Profiler): 'screenshot.py'), '--video', '--file', self._output_path, - '--device', browser_backend.adb.device_serial()], + '--device', browser_backend.device.adb.GetDeviceSerial()], stdin=subprocess.PIPE, stdout=subprocess.PIPE) @classmethod diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py index 3f7a080..5ed86b6 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/android_traceview_profiler.py @@ -24,21 +24,21 @@ class AndroidTraceviewProfiler(profiler.Profiler): super(AndroidTraceviewProfiler, self).__init__( browser_backend, platform_backend, output_path, state) - if self._browser_backend.adb.device().FileExists(self._DEFAULT_DEVICE_DIR): - self._browser_backend.adb.RunShellCommand( + if self._browser_backend.device.FileExists(self._DEFAULT_DEVICE_DIR): + self._browser_backend.device.RunShellCommand( 'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*')) else: - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'mkdir -p ' + self._DEFAULT_DEVICE_DIR) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'chmod 777 ' + self._DEFAULT_DEVICE_DIR) self._trace_files = [] for pid in self._GetProcessOutputFileMap().iterkeys(): device_dump_file = '%s/%s.trace' % (self._DEFAULT_DEVICE_DIR, pid) self._trace_files.append((pid, device_dump_file)) - self._browser_backend.adb.RunShellCommand('am profile %s start %s' % - (pid, device_dump_file)) + self._browser_backend.device.RunShellCommand( + 'am profile %s start %s' % (pid, device_dump_file)) @classmethod @@ -54,13 +54,13 @@ class AndroidTraceviewProfiler(profiler.Profiler): def CollectProfile(self): output_files = [] for pid, trace_file in self._trace_files: - self._browser_backend.adb.RunShellCommand('am profile %s stop' % pid) + self._browser_backend.device.RunShellCommand('am profile %s stop' % pid) # pylint: disable=cell-var-from-loop util.WaitFor(lambda: self._FileSize(trace_file) > 0, timeout=10) output_files.append(trace_file) - self._browser_backend.adb.device().PullFile( + self._browser_backend.device.PullFile( self._DEFAULT_DEVICE_DIR, self._output_path) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*')) print 'Traceview profiles available in ', self._output_path print 'Use third_party/android_tools/sdk/tools/monitor ' @@ -69,6 +69,6 @@ class AndroidTraceviewProfiler(profiler.Profiler): def _FileSize(self, file_name): try: - return self._browser_backend.adb.device().Stat(file_name).st_size + return self._browser_backend.device.Stat(file_name).st_size except device_errors.CommandFailedError: return 0 diff --git a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py index 8719406..fc78127 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py @@ -48,9 +48,9 @@ class JavaHeapProfiler(profiler.Profiler): def CollectProfile(self): self._timer.cancel() self._DumpJavaHeap(True) - self._browser_backend.adb.device().PullFile( + self._browser_backend.device.PullFile( self._DEFAULT_DEVICE_DIR, self._output_path) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*')) output_files = [] for f in os.listdir(self._output_path): @@ -67,25 +67,25 @@ class JavaHeapProfiler(profiler.Profiler): self._DumpJavaHeap(False) def _DumpJavaHeap(self, wait_for_completion): - if not self._browser_backend.adb.device().FileExists( + if not self._browser_backend.device.FileExists( self._DEFAULT_DEVICE_DIR): - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'mkdir -p ' + self._DEFAULT_DEVICE_DIR) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'chmod 777 ' + self._DEFAULT_DEVICE_DIR) device_dump_file = None for pid in self._GetProcessOutputFileMap().iterkeys(): device_dump_file = '%s/%s.%s.aprof' % (self._DEFAULT_DEVICE_DIR, pid, self._run_count) - self._browser_backend.adb.RunShellCommand('am dumpheap %s %s' % - (pid, device_dump_file)) + self._browser_backend.device.RunShellCommand( + 'am dumpheap %s %s' % (pid, device_dump_file)) if device_dump_file and wait_for_completion: util.WaitFor(lambda: self._FileSize(device_dump_file) > 0, timeout=2) self._run_count += 1 def _FileSize(self, file_name): try: - return self._browser_backend.adb.device().Stat(file_name).st_size + return self._browser_backend.device.Stat(file_name).st_size except device_errors.CommandFailedError: return 0 diff --git a/tools/telemetry/telemetry/core/platform/profiler/netlog_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/netlog_profiler.py index aa0070a..4cb7e0a 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/netlog_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/netlog_profiler.py @@ -36,9 +36,9 @@ class NetLogProfiler(profiler.Profiler): # On Android pull the output file to the host. if self._platform_backend.GetOSName() == 'android': host_output_file = '%s.json' % self._output_path - self._browser_backend.adb.device().PullFile(output_file, host_output_file) + self._browser_backend.device.PullFile(output_file, host_output_file) # Clean the device - self._browser_backend.adb.device().RunShellCommand('rm %s' % output_file) + self._browser_backend.device.RunShellCommand('rm %s' % output_file) output_file = host_output_file print 'Net-internals log saved as %s' % output_file print 'To view, open in chrome://net-internals' diff --git a/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py index be4643d..a3c2639 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/oomkiller_profiler.py @@ -32,7 +32,7 @@ class OOMKillerProfiler(profiler.Profiler): browser_backend, platform_backend, output_path, state) if not 'mem_consumer_launched' in state: state['mem_consumer_launched'] = True - arch_name = self._browser_backend.adb.device().GetABI() + arch_name = self._browser_backend.device.product_cpu_abi mem_consumer_path = support_binaries.FindPath( os.path.join('apks', 'MemConsumer.apk'), arch_name, 'android') assert mem_consumer_path, ('Could not find memconsumer app. Please build ' @@ -40,12 +40,12 @@ class OOMKillerProfiler(profiler.Profiler): if not self._platform_backend.CanLaunchApplication( 'org.chromium.memconsumerg'): self._platform_backend.InstallApplication(mem_consumer_path) - self._browser_backend.adb.device().GoHome() + self._browser_backend.device.GoHome() self._platform_backend.LaunchApplication( 'org.chromium.memconsumer/.MemConsumer', '--ei memory 20') # Bring the browser to the foreground after launching the mem consumer - self._browser_backend.adb.device().StartActivity( + self._browser_backend.device.StartActivity( intent.Intent(package=browser_backend.package, activity=browser_backend.activity), blocking=True) @@ -62,7 +62,7 @@ class OOMKillerProfiler(profiler.Profiler): @classmethod def WillCloseBrowser(cls, browser_backend, platform_backend): - browser_backend.adb.device().ForceStop('org.chromium.memconsumer') + browser_backend.device.ForceStop('org.chromium.memconsumer') def CollectProfile(self): missing_applications = self._MissingApplications() diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py index f0982e6..159bf7a 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py @@ -80,15 +80,16 @@ class _SingleProcessPerfProfiler(object): cmd_prefix = [] perf_args = ['record', '--pid', str(pid)] if self._is_android: - cmd_prefix = ['adb', '-s', browser_backend.adb.device_serial(), 'shell', - perf_binary] + cmd_prefix = ['adb', '-s', browser_backend.device.adb.GetDeviceSerial(), + 'shell', perf_binary] perf_args += _PERF_OPTIONS_ANDROID output_file = os.path.join('/sdcard', 'perf_profiles', os.path.basename(output_file)) self._device_output_file = output_file - browser_backend.adb.RunShellCommand( + browser_backend.device.RunShellCommand( 'mkdir -p ' + os.path.dirname(self._device_output_file)) - browser_backend.adb.RunShellCommand('rm -f ' + self._device_output_file) + browser_backend.device.RunShellCommand( + 'rm -f ' + self._device_output_file) else: cmd_prefix = [perf_binary] perf_args += ['--output', output_file] + _PERF_OPTIONS @@ -103,10 +104,10 @@ class _SingleProcessPerfProfiler(object): 'To collect a full profile rerun with ' '"--extra-browser-args=--single-process"') if self._is_android: - device = self._browser_backend.adb.device() try: binary_name = os.path.basename(self._perf_binary) - device.KillAll(binary_name, signum=signal.SIGINT, blocking=True) + self._browser_backend.device.KillAll( + binary_name, signum=signal.SIGINT, blocking=True) except device_errors.CommandFailedError: logging.warning('The perf process could not be killed on the device.') self._proc.send_signal(signal.SIGINT) @@ -127,16 +128,15 @@ Try rerunning this script under sudo or setting cmd = '%s report -n -i %s' % (_NicePath(self._perfhost_binary), self._output_file) if self._is_android: - device = self._browser_backend.adb.device() - device.PullFile(self._device_output_file, self._output_file) - required_libs = \ + self._browser_backend.device.PullFile( + self._device_output_file, self._output_file) + required_libs = ( android_profiling_helper.GetRequiredLibrariesForPerfProfile( - self._output_file) + self._output_file)) symfs_root = os.path.dirname(self._output_file) - kallsyms = android_profiling_helper.CreateSymFs(device, - symfs_root, - required_libs, - use_symlinks=True) + kallsyms = android_profiling_helper.CreateSymFs( + self._browser_backend.device, symfs_root, required_libs, + use_symlinks=True) cmd += ' --symfs %s --kallsyms %s' % (symfs_root, kallsyms) for lib in required_libs: lib = os.path.join(symfs_root, lib[1:]) @@ -173,9 +173,10 @@ class PerfProfiler(profiler.Profiler): perf_binary = perfhost_binary = _InstallPerfHost() try: if platform_backend.GetOSName() == 'android': - device = browser_backend.adb.device() - perf_binary = android_profiling_helper.PrepareDeviceForPerf(device) - self._perf_control = perf_control.PerfControl(device) + perf_binary = android_profiling_helper.PrepareDeviceForPerf( + browser_backend.device) + self._perf_control = perf_control.PerfControl( + browser_backend.device) self._perf_control.SetPerfProfilingMode() else: _PrepareHostForPerf() diff --git a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py index 4fc3982..8c67284 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py @@ -34,26 +34,26 @@ class _TCMallocHeapProfilerAndroid(object): def _SetDeviceProperties(self, properties): device_configured = False # This profiler requires adb root to set properties. - self._browser_backend.adb.device().EnableRoot() + self._browser_backend.device.EnableRoot() for values in properties.itervalues(): - device_property = self._browser_backend.adb.device().GetProp(values[0]) + device_property = self._browser_backend.device.GetProp(values[0]) if not device_property or not device_property.strip(): - self._browser_backend.adb.device().SetProp(values[0], values[1]) + self._browser_backend.device.SetProp(values[0], values[1]) device_configured = True - if not self._browser_backend.adb.device().FileExists( + if not self._browser_backend.device.FileExists( self._DEFAULT_DEVICE_DIR): - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'mkdir -p ' + self._DEFAULT_DEVICE_DIR) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'chmod 777 ' + self._DEFAULT_DEVICE_DIR) device_configured = True if device_configured: raise Exception('Device required special config, run again.') def CollectProfile(self): - self._browser_backend.adb.device().PullFile( + self._browser_backend.device.PullFile( self._DEFAULT_DEVICE_DIR, self._output_path) - self._browser_backend.adb.RunShellCommand( + self._browser_backend.device.RunShellCommand( 'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*')) if os.path.exists(self._output_path): logging.info('TCMalloc dumps pulled to %s', self._output_path) diff --git a/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py index 6ad22c4..16962c8 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py @@ -2,15 +2,21 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import logging import os import signal import subprocess import sys import tempfile +from telemetry.core import util from telemetry.core.platform import profiler from telemetry.core.platform.profiler import android_prebuilt_profiler_helper +util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') +from pylib import device_signal # pylint: disable=import-error +from pylib.device import device_errors # pylint: disable=import-error + _TCP_DUMP_BASE_OPTS = ['-i', 'any', '-p', '-s', '0', '-w'] @@ -23,28 +29,30 @@ class _TCPDumpProfilerAndroid(object): _DEVICE_DUMP_FILE = '/sdcard/tcpdump_profiles/capture.pcap' - def __init__(self, adb, output_path): - self._adb = adb + def __init__(self, device, output_path): + self._device = device self._output_path = output_path - self._adb.RunShellCommand('mkdir -p ' + - os.path.dirname(self._DEVICE_DUMP_FILE)) + self._device.RunShellCommand('mkdir -p ' + + os.path.dirname(self._DEVICE_DUMP_FILE)) self._proc = subprocess.Popen( - ['adb', '-s', self._adb.device_serial(), + ['adb', '-s', self._device.adb.GetDeviceSerial(), 'shell', android_prebuilt_profiler_helper.GetDevicePath('tcpdump')] + _TCP_DUMP_BASE_OPTS + [self._DEVICE_DUMP_FILE]) def CollectProfile(self): - tcpdump_pid = self._adb.ExtractPid('tcpdump') - if not tcpdump_pid or not tcpdump_pid[0]: + try: + self._device.KillAll('tcpdump', signum=device_signal.SIGTERM) + except device_errors.CommandFailedError: + logging.exception('Unable to kill TCPDump.') raise Exception('Unable to find TCPDump. Check your device is rooted ' 'and tcpdump is installed at ' + android_prebuilt_profiler_helper.GetDevicePath('tcpdump')) - self._adb.RunShellCommand('kill -term ' + tcpdump_pid[0]) + self._proc.terminate() host_dump = os.path.join(self._output_path, os.path.basename(self._DEVICE_DUMP_FILE)) - self._adb.device().PullFile(self._DEVICE_DUMP_FILE, host_dump) + self._device.PullFile(self._DEVICE_DUMP_FILE, host_dump) print 'TCP dump available at: %s ' % host_dump print 'Use Wireshark to open it.' return host_dump @@ -98,9 +106,9 @@ class TCPDumpProfiler(profiler.Profiler): browser_backend, platform_backend, output_path, state) if platform_backend.GetOSName() == 'android': android_prebuilt_profiler_helper.InstallOnDevice( - browser_backend.adb.device(), 'tcpdump') + browser_backend.device, 'tcpdump') self._platform_profiler = _TCPDumpProfilerAndroid( - browser_backend.adb, output_path) + browser_backend.device, output_path) else: self._platform_profiler = _TCPDumpProfilerLinux(output_path) diff --git a/tools/telemetry/telemetry/core/platform/profiler/v8_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/v8_profiler.py index 73e126e..ef065be 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/v8_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/v8_profiler.py @@ -38,9 +38,9 @@ class V8Profiler(profiler.Profiler): # On Android pull the output file to the host. if self._platform_backend.GetOSName() == 'android': host_output_file = '%s.log' % self._output_path - self._browser_backend.adb.device().PullFile(output_file, host_output_file) + self._browser_backend.device.PullFile(output_file, host_output_file) # Clean the device - self._browser_backend.adb.device().RunShellCommand('rm %s' % output_file) + self._browser_backend.device.RunShellCommand('rm %s' % output_file) output_file = host_output_file print 'V8 profile saved as %s' % output_file print 'To view, open in ' \ diff --git a/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler.py index 96f0878..11acb71 100644 --- a/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler.py +++ b/tools/telemetry/telemetry/core/platform/profiler/vtune_profiler.py @@ -57,16 +57,14 @@ class _SingleProcessVTuneProfiler(object): return self._output_file if self._is_android: - required_libs = \ + required_libs = ( android_profiling_helper.GetRequiredLibrariesForVTuneProfile( - self._output_file) + self._output_file)) - device = self._browser_backend.adb.device() symfs_root = os.path.dirname(self._output_file) - android_profiling_helper.CreateSymFs(device, - symfs_root, - required_libs, - use_symlinks=True) + android_profiling_helper.CreateSymFs( + self._browser_backend.device, symfs_root, required_libs, + use_symlinks=True) logging.info('Resolving symbols in profile.') subprocess.call(['amplxe-cl', '-finalize', '-r', self._output_file, '-search-dir', symfs_root]) @@ -130,6 +128,8 @@ class VTuneProfiler(profiler.Profiler): if browser_type.startswith('android'): # VTune checks if 'su' is available on the device. + # TODO(jbudorick): Replace with DeviceUtils.HasRoot, + # DeviceUtils.NeedsSU, or some combination thereof. proc = subprocess.Popen(['adb', 'shell', 'su', '-c', 'id'], stderr=subprocess.STDOUT, stdout=subprocess.PIPE) diff --git a/tools/telemetry/telemetry/unittest_util/system_stub.py b/tools/telemetry/telemetry/unittest_util/system_stub.py index cb7c20d..27f3956 100644 --- a/tools/telemetry/telemetry/unittest_util/system_stub.py +++ b/tools/telemetry/telemetry/unittest_util/system_stub.py @@ -9,14 +9,12 @@ This test allows one to test code that itself uses os, sys, and subprocess. import os import re -import shlex import sys class Override(object): def __init__(self, base_module, module_list): - stubs = {'adb_commands': AdbCommandsModuleStub, - 'cloud_storage': CloudStorageModuleStub, + stubs = {'cloud_storage': CloudStorageModuleStub, 'open': OpenFunctionStub, 'os': OsModuleStub, 'perf_control': PerfControlModuleStub, @@ -60,96 +58,6 @@ class Override(object): self._overrides = {} -class AdbDevice(object): - - def __init__(self): - self.has_root = False - self.needs_su = False - self.shell_command_handlers = {} - self.mock_content = [] - self.system_properties = {} - if self.system_properties.get('ro.product.cpu.abi') == None: - self.system_properties['ro.product.cpu.abi'] = 'armeabi-v7a' - - def HasRoot(self): - return self.has_root - - def NeedsSU(self): - return self.needs_su - - def RunShellCommand(self, args, **_kwargs): - if isinstance(args, basestring): - args = shlex.split(args) - handler = self.shell_command_handlers[args[0]] - return handler(args) - - def FileExists(self, _): - return False - - def ReadFile(self, device_path, as_root=False): # pylint: disable=W0613 - return self.mock_content - - def GetProp(self, property_name): - return self.system_properties[property_name] - - def SetProp(self, property_name, property_value): - self.system_properties[property_name] = property_value - - -class AdbCommandsModuleStub(object): - - class AdbCommandsStub(object): - - def __init__(self, module, device): - self._module = module - self._device = device - self.is_root_enabled = True - self._adb_device = module.adb_device - - def IsRootEnabled(self): - return self.is_root_enabled - - def RestartAdbdOnDevice(self): - pass - - def IsUserBuild(self): - return False - - def WaitForDevicePm(self): - pass - - def device(self): - return self._adb_device - - def device_serial(self): - return self._device - - def __init__(self): - self.attached_devices = [] - self.apk_package_name = None - self.adb_device = AdbDevice() - - def AdbCommandsStubConstructor(device=None): - return AdbCommandsModuleStub.AdbCommandsStub(self, device) - self.AdbCommands = AdbCommandsStubConstructor - - @staticmethod - def IsAndroidSupported(): - return True - - def GetPackageName(self, _): - return self.apk_package_name - - def GetAttachedDevices(self): - return self.attached_devices - - def SetupPrebuiltTools(self, _): - return True - - def CleanupLeftoverProcesses(self): - pass - - class CloudStorageModuleStub(object): PUBLIC_BUCKET = 'chromium-telemetry' PARTNER_BUCKET = 'chrome-partner-telemetry' |