| Index: tools/telemetry/telemetry/internal/backends/mandoline/android.py
|
| diff --git a/tools/telemetry/telemetry/internal/backends/mandoline/android.py b/tools/telemetry/telemetry/internal/backends/mandoline/android.py
|
| deleted file mode 100644
|
| index 57fd4507812d501e1820205e3dd121a2a150cb70..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/telemetry/internal/backends/mandoline/android.py
|
| +++ /dev/null
|
| @@ -1,278 +0,0 @@
|
| -# 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.
|
| -
|
| -# TODO(yzshen): Once the dep manager is ready, remove this file and use the one
|
| -# from src/mojo/tools directly.
|
| -
|
| -
|
| -import atexit
|
| -import logging
|
| -import os
|
| -import signal
|
| -import subprocess
|
| -import sys
|
| -import threading
|
| -import time
|
| -
|
| -from .paths import Paths
|
| -
|
| -from telemetry.internal.util import binary_manager
|
| -
|
| -from devil import base_error
|
| -from devil.android import apk_helper
|
| -from devil.android import device_errors
|
| -from devil.android import device_utils
|
| -from devil.android.sdk import adb_wrapper
|
| -from devil.constants import exit_codes
|
| -
|
| -
|
| -# Tags used by the mojo shell application logs.
|
| -LOGCAT_TAGS = [
|
| - 'AndroidHandler',
|
| - 'MojoFileHelper',
|
| - 'MojoMain',
|
| - 'MojoShellActivity',
|
| - 'MojoShellApplication',
|
| - 'chromium',
|
| -]
|
| -
|
| -MAPPING_PREFIX = '--map-origin='
|
| -
|
| -
|
| -def _ExitIfNeeded(process):
|
| - '''Exits |process| if it is still alive.'''
|
| - if process.poll() is None:
|
| - process.kill()
|
| -
|
| -
|
| -class AndroidShell(object):
|
| - '''
|
| - Used to set up and run a given mojo shell binary on an Android device.
|
| - |config| is the mopy.config.Config for the build.
|
| - '''
|
| - def __init__(self, config, chrome_root):
|
| - self.adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
|
| - self.config = config
|
| - self.paths = Paths(config, chrome_root)
|
| - self.device = None
|
| - self.shell_args = []
|
| - self.target_package = apk_helper.GetPackageName(self.paths.apk_path)
|
| - self.temp_gdb_dir = None
|
| -
|
| - # TODO(msw): Use devil's adb_wrapper and device_utils instead.
|
| - def _CreateADBCommand(self, args):
|
| - adb_command = [self.adb_path, '-s', self.device.adb.GetDeviceSerial()]
|
| - adb_command.extend(args)
|
| - logging.getLogger().debug('Command: %s', ' '.join(adb_command))
|
| - return adb_command
|
| -
|
| - def _ReadFifo(self, path, pipe, on_fifo_closed, max_attempts=5):
|
| - '''
|
| - Reads the fifo at |path| on the device and write the contents to |pipe|.
|
| - Calls |on_fifo_closed| when the fifo is closed. This method will try to find
|
| - the path up to |max_attempts|, waiting 1 second between each attempt. If it
|
| - cannot find |path|, a exception will be raised.
|
| - '''
|
| - def Run():
|
| - def _WaitForFifo():
|
| - for _ in xrange(max_attempts):
|
| - if self.device.FileExists(path):
|
| - return
|
| - time.sleep(1)
|
| - on_fifo_closed()
|
| - raise Exception('Unable to find fifo: %s' % path)
|
| - _WaitForFifo()
|
| - stdout_cat = subprocess.Popen(self._CreateADBCommand([
|
| - 'shell',
|
| - 'cat',
|
| - path]),
|
| - stdout=pipe)
|
| - atexit.register(_ExitIfNeeded, stdout_cat)
|
| - stdout_cat.wait()
|
| - on_fifo_closed()
|
| -
|
| - thread = threading.Thread(target=Run, name='StdoutRedirector')
|
| - thread.start()
|
| -
|
| -
|
| - def InitShell(self, device=None):
|
| - '''
|
| - Runs adb as root, and installs the apk as needed. |device| is the target
|
| - device to run on, if multiple devices are connected. Returns 0 on success or
|
| - a non-zero exit code on a terminal failure.
|
| - '''
|
| - try:
|
| - devices = device_utils.DeviceUtils.HealthyDevices()
|
| - if device:
|
| - self.device = next((d for d in devices if d == device), None)
|
| - if not self.device:
|
| - raise device_errors.DeviceUnreachableError(device)
|
| - elif devices:
|
| - self.device = devices[0]
|
| - else:
|
| - raise device_errors.NoDevicesError()
|
| -
|
| - logging.getLogger().debug('Using device: %s', self.device)
|
| - # Clean the logs on the device to avoid displaying prior activity.
|
| - self.device.adb.Logcat(clear=True)
|
| - self.device.EnableRoot()
|
| - self.device.Install(self.paths.apk_path)
|
| - except base_error.BaseError as e:
|
| - # Report 'device not found' as infra failures. See http://crbug.com/493900
|
| - print 'Exception in AndroidShell.InitShell:\n%s' % str(e)
|
| - if e.is_infra_error or 'error: device not found' in str(e):
|
| - return exit_codes.INFRA
|
| - return exit_codes.ERROR
|
| -
|
| - return 0
|
| -
|
| - def _GetProcessId(self, process):
|
| - '''Returns the process id of the process on the remote device.'''
|
| - while True:
|
| - line = process.stdout.readline()
|
| - pid_command = 'launcher waiting for GDB. pid: '
|
| - index = line.find(pid_command)
|
| - if index != -1:
|
| - return line[index + len(pid_command):].strip()
|
| - return 0
|
| -
|
| - def _GetLocalGdbPath(self):
|
| - '''Returns the path to the android gdb.'''
|
| - if self.config.target_cpu not in ('arm', 'x86', 'x64'):
|
| - raise Exception('Unknown target_cpu: %s' % self.config.target_cpu)
|
| - return binary_manager.FetchPath('gdb', self.config.target_cpu, 'android')
|
| -
|
| - def _WaitForProcessIdAndStartGdb(self, process):
|
| - '''
|
| - Waits until we see the process id from the remote device, starts up
|
| - gdbserver on the remote device, and gdb on the local device.
|
| - '''
|
| - # Wait until we see 'PID'
|
| - pid = self._GetProcessId(process)
|
| - assert pid != 0
|
| - # No longer need the logcat process.
|
| - process.kill()
|
| - # Disable python's processing of SIGINT while running gdb. Otherwise
|
| - # control-c doesn't work well in gdb.
|
| - signal.signal(signal.SIGINT, signal.SIG_IGN)
|
| - gdbserver_process = subprocess.Popen(self._CreateADBCommand(['shell',
|
| - 'gdbserver',
|
| - '--attach',
|
| - ':5039',
|
| - pid]))
|
| - atexit.register(_ExitIfNeeded, gdbserver_process)
|
| -
|
| - gdbinit_path = os.path.join(self.temp_gdb_dir, 'gdbinit')
|
| - _CreateGdbInit(self.temp_gdb_dir, gdbinit_path, self.paths.build_dir)
|
| -
|
| - # Wait a second for gdb to start up on the device. Without this the local
|
| - # gdb starts before the remote side has registered the port.
|
| - # TODO(sky): maybe we should try a couple of times and then give up?
|
| - time.sleep(1)
|
| -
|
| - local_gdb_process = subprocess.Popen([self._GetLocalGdbPath(),
|
| - '-x',
|
| - gdbinit_path],
|
| - cwd=self.temp_gdb_dir)
|
| - atexit.register(_ExitIfNeeded, local_gdb_process)
|
| - local_gdb_process.wait()
|
| - signal.signal(signal.SIGINT, signal.SIG_DFL)
|
| -
|
| - def StartActivity(self,
|
| - activity_name,
|
| - arguments,
|
| - stdout,
|
| - on_fifo_closed,
|
| - temp_gdb_dir=None):
|
| - '''
|
| - Starts the shell with the given |arguments|, directing output to |stdout|.
|
| - |on_fifo_closed| will be run if the FIFO can't be found or when it's closed.
|
| - |temp_gdb_dir| is set to a location with appropriate symlinks for gdb to
|
| - find when attached to the device's remote process on startup.
|
| - '''
|
| - assert self.device
|
| - arguments += self.shell_args
|
| -
|
| - cmd = self._CreateADBCommand([
|
| - 'shell',
|
| - 'am',
|
| - 'start',
|
| - '-S',
|
| - '-a', 'android.intent.action.VIEW',
|
| - '-n', '%s/%s.%s' % (self.target_package,
|
| - self.target_package,
|
| - activity_name)])
|
| -
|
| - logcat_process = None
|
| - if temp_gdb_dir:
|
| - self.temp_gdb_dir = temp_gdb_dir
|
| - arguments.append('--wait-for-debugger')
|
| - # Remote debugging needs a port forwarded.
|
| - self.device.adb.Forward('tcp:5039', 'tcp:5039')
|
| - logcat_process = self.ShowLogs(stdout=subprocess.PIPE)
|
| -
|
| - fifo_path = '/data/data/%s/stdout.fifo' % self.target_package
|
| - subprocess.check_call(self._CreateADBCommand(
|
| - ['shell', 'rm', '-f', fifo_path]))
|
| - arguments.append('--fifo-path=%s' % fifo_path)
|
| - max_attempts = 200 if '--wait-for-debugger' in arguments else 5
|
| - self._ReadFifo(fifo_path, stdout, on_fifo_closed, max_attempts)
|
| -
|
| - # Extract map-origin args and add the extras array with commas escaped.
|
| - parameters = [a for a in arguments if not a.startswith(MAPPING_PREFIX)]
|
| - parameters = [p.replace(',', r'\,') for p in parameters]
|
| - cmd += ['--esa', '%s.extras' % self.target_package, ','.join(parameters)]
|
| -
|
| - atexit.register(self.kill)
|
| - with open(os.devnull, 'w') as devnull:
|
| - cmd_process = subprocess.Popen(cmd, stdout=devnull)
|
| - if logcat_process:
|
| - self._WaitForProcessIdAndStartGdb(logcat_process)
|
| - cmd_process.wait()
|
| -
|
| - def kill(self):
|
| - '''Stops the mojo shell; matches the Popen.kill method signature.'''
|
| - self.device.ForceStop(self.target_package)
|
| -
|
| - def ShowLogs(self, stdout=sys.stdout):
|
| - '''Displays the mojo shell logs and returns the process reading the logs.'''
|
| - logcat = subprocess.Popen(self._CreateADBCommand([
|
| - 'logcat',
|
| - '-s',
|
| - ' '.join(LOGCAT_TAGS)]),
|
| - stdout=stdout)
|
| - atexit.register(_ExitIfNeeded, logcat)
|
| - return logcat
|
| -
|
| -
|
| -def _CreateGdbInit(tmp_dir, gdb_init_path, build_dir):
|
| - '''
|
| - Creates the gdbinit file.
|
| -
|
| - Args:
|
| - tmp_dir: the directory where the gdbinit and other files lives.
|
| - gdb_init_path: path to gdbinit
|
| - build_dir: path where build files are located.
|
| - '''
|
| - gdbinit = ('target remote localhost:5039\n'
|
| - 'def reload-symbols\n'
|
| - ' set solib-search-path %s:%s\n'
|
| - 'end\n'
|
| - 'def info-symbols\n'
|
| - ' info sharedlibrary\n'
|
| - 'end\n'
|
| - 'reload-symbols\n'
|
| - 'echo \\n\\n'
|
| - 'You are now in gdb and need to type continue (or c) to continue '
|
| - 'execution.\\n'
|
| - 'gdb is in the directory %s\\n'
|
| - 'The following functions have been defined:\\n'
|
| - 'reload-symbols: forces reloading symbols. If after a crash you\\n'
|
| - 'still do not see symbols you likely need to create a link in\\n'
|
| - 'the directory you are in.\\n'
|
| - 'info-symbols: shows status of current shared libraries.\\n'
|
| - 'NOTE: you may need to type reload-symbols again after a '
|
| - 'crash.\\n\\n' % (tmp_dir, build_dir, tmp_dir))
|
| - with open(gdb_init_path, 'w') as f:
|
| - f.write(gdbinit)
|
|
|