Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Unified Diff: build/android/emulator.py

Issue 8787010: Android buildbot reliability fixes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: apply bradnelson feedback Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | build/android/run_tests.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/emulator.py
diff --git a/build/android/emulator.py b/build/android/emulator.py
index 6aeb3fca3b9cbd138ce532004cdd20f84df1eaf3..b5e4436cd12097cebbc300e39d5a745e565259b5 100755
--- a/build/android/emulator.py
+++ b/build/android/emulator.py
@@ -13,8 +13,10 @@ Assumes system environment ANDROID_NDK_ROOT has been set.
import logging
import os
+import signal
import subprocess
import sys
+import time
import android_commands
@@ -22,7 +24,31 @@ import android_commands
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..',
'..', 'third_party', 'android', 'testrunner'))
import adb_interface
+import cmd_helper
+import errors
+import run_command
+class EmulatorLaunchException(Exception):
+ """Emulator failed to launch."""
+ pass
+
+def _KillAllEmulators():
+ """Kill all running emulators that look like ones we started.
+
+ There are odd 'sticky' cases where there can be no emulator process
+ running but a device slot is taken. A little bot trouble and and
+ we're out of room forever.
+ """
+ emulators = android_commands.GetEmulators()
+ if not emulators:
+ return
+ for emu_name in emulators:
+ cmd_helper.GetCmdOutput(['adb', '-s', emu_name, 'emu', 'kill'])
+ logging.info('Emulator killing is async; give a few seconds for all to die.')
+ for i in range(5):
+ if not android_commands.GetEmulators():
+ return
+ time.sleep(1)
def _GetAvailablePort():
"""Returns an available TCP port for the console."""
@@ -52,6 +78,16 @@ class Emulator(object):
device: Device name of this emulator.
"""
+ # Signals we listen for to kill the emulator on
+ _SIGNALS = (signal.SIGINT, signal.SIGHUP)
+
+ # Time to wait for an emulator launch, in seconds.
+ _EMULATOR_LAUNCH_TIMEOUT = 120
+
+ # Timeout interval of wait-for-device command before bouncing to a a
+ # process life check.
+ _EMULATOR_WFD_TIMEOUT = 5
+
def __init__(self):
try:
android_sdk_root = os.environ['ANDROID_SDK_ROOT']
@@ -63,22 +99,18 @@ class Emulator(object):
self.popen = None
self.device = None
- def Reset(self):
- """Kill a running emulator.
-
- May be needed if the test scripts stopped abnormally and an
- emulator is left around."""
- adb = adb_interface.AdbInterface()
- logging.info('Killing any existing emulator.')
- adb.SendCommand('emu kill')
+ def _DeviceName(self):
+ """Return our device name."""
+ port = _GetAvailablePort()
+ return ('emulator-%d' % port, port)
def Launch(self):
"""Launches the emulator and waits for package manager to startup.
If fails, an exception will be raised.
"""
- port = _GetAvailablePort()
- self.device = "emulator-%d" % port
+ _KillAllEmulators() # just to be sure
+ (self.device, port) = self._DeviceName()
self.popen = subprocess.Popen(args=[
self.emulator,
# Speed up emulator launch by 40%. Really.
@@ -88,15 +120,63 @@ class Emulator(object):
'-partition-size', '256',
# Use a familiar name and port.
'-avd', 'buildbot',
- '-port', str(port)])
- # This will not return until device's package manager starts up or an
- # exception is raised.
- android_commands.AndroidCommands(self.device, True)
+ '-port', str(port)],
+ stderr=subprocess.STDOUT)
+ self._InstallKillHandler()
+ self._ConfirmLaunch()
+
+ def _ConfirmLaunch(self):
+ """Confirm the emulator launched properly.
+
+ Loop on a wait-for-device with a very small timeout. On each
+ timeout, check the emulator process is still alive.
+ After confirming a wait-for-device can be successful, make sure
+ it returns the right answer.
+ """
+ a = android_commands.AndroidCommands(self.device, False)
+ seconds_waited = 0
+ number_of_waits = 2 # Make sure we can wfd twice
+ adb_cmd = "adb -s %s %s" % (self.device, 'wait-for-device')
+ while seconds_waited < self._EMULATOR_LAUNCH_TIMEOUT:
+ try:
+ run_command.RunCommand(adb_cmd, timeout_time=self._EMULATOR_WFD_TIMEOUT,
+ retry_count=1)
+ number_of_waits -= 1
+ if not number_of_waits:
+ break
+ except errors.WaitForResponseTimedOutError as e:
+ seconds_waited += self._EMULATOR_WFD_TIMEOUT
+ adb_cmd = "adb -s %s %s" % (self.device, 'kill-server')
+ run_command.RunCommand(adb_cmd)
+ self.popen.poll()
+ if self.popen.returncode != None:
+ raise EmulatorLaunchException('EMULATOR DIED')
+ if seconds_waited >= self._EMULATOR_LAUNCH_TIMEOUT:
+ raise EmulatorLaunchException('TIMEOUT with wait-for-device')
+ logging.info('Seconds waited on wait-for-device: %d', seconds_waited)
+ # Now that we checked for obvious problems, wait for a boot complete.
+ # Waiting for the package manager has been problematic.
+ a.Adb().WaitForBootComplete()
def Shutdown(self):
"""Shuts down the process started by launch."""
- self.popen.terminate()
-
+ if self.popen:
+ self.popen.poll()
+ if self.popen.returncode == None:
+ self.popen.kill()
+ self.popen = None
+
+ def _ShutdownOnSignal(self, signum, frame):
+ logging.critical('emulator _ShutdownOnSignal')
+ for sig in self._SIGNALS:
+ signal.signal(sig, signal.SIG_DFL)
+ self.Shutdown()
+ raise KeyboardInterrupt # print a stack
+
+ def _InstallKillHandler(self):
+ """Install a handler to kill the emulator when we exit unexpectedly."""
+ for sig in self._SIGNALS:
+ signal.signal(sig, self._ShutdownOnSignal)
def main(argv):
Emulator().launch()
« no previous file with comments | « no previous file | build/android/run_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698