| # Copyright (c) 2012 The Chromium OS 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, os, signal, time |
| |
| import common |
| from autotest_lib.client.bin import test, utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros import constants, cros_logging, cros_ui, login |
| |
| class desktopui_HangDetector(test.test): |
| """ |
| This class enables browser process hang detection, simulates a hang |
| by sending a SIGSTOP to the browser, and then checks to see that it |
| got killed and restarted successfully -- without the UI getting bounced. |
| """ |
| version = 1 |
| |
| |
| def initialize(self): |
| self._pauser = cros_logging.LogRotationPauser() |
| self._pauser.begin() |
| |
| |
| def _get_oldest_pid_by_name(self, name): |
| try: |
| pid = utils.get_oldest_pid_by_name(name) |
| logging.debug('Found %d for %s', pid, name) |
| except error.CmdError as e: |
| raise error.TestError('Could not find pid of %s: %r' % (name, e)) |
| except ValueError as e: |
| raise error.TestError('Got bad pid looking up %s: %r' % (name, e)) |
| if not pid: |
| raise error.TestError('Got no pid looking up %s' % name) |
| return pid |
| |
| |
| def run_once(self): |
| # Create magic file to enable browser liveness checking and |
| # bounce the session manager to pick up the flag file. |
| cros_ui.stop() |
| os.mknod(constants.ENABLE_BROWSER_HANG_DETECTION_FILE) |
| cros_ui.start() |
| |
| browser_pid = self._get_oldest_pid_by_name(constants.BROWSER) |
| sm_pid = self._get_oldest_pid_by_name(constants.SESSION_MANAGER) |
| |
| # Reading the log is the best way to watch for the hang detector. |
| reader = cros_logging.LogReader() |
| reader.set_start_by_current() |
| |
| # To simulate a hang, STOP the browser and wait for it to get |
| # hit by the session manager. It won't actually exit until it gets |
| # a SIGCONT, though. |
| try: |
| os.kill(browser_pid, signal.SIGSTOP) # Simulate hang. |
| except OSError as e: |
| raise error.TestError('Cannot STOP browser: %r' % e) |
| |
| # Watch for hang detection. |
| utils.poll_for_condition( |
| condition=lambda: reader.can_find('Aborting browser process.'), |
| exception=utils.TimeoutError('Waiting for hang detector.'), |
| sleep_interval=5, |
| timeout=60) |
| |
| try: |
| os.kill(browser_pid, signal.SIGCONT) # Allow browser to die. |
| except OSError as e: |
| raise error.TestError('Cannot CONT browser: %r' % e) |
| |
| # Wait for old browser process to be gone. |
| utils.poll_for_condition( |
| condition= lambda: utils.pid_is_alive(browser_pid), |
| exception=utils.TimeoutError( |
| 'Browser does not seem to have restarted!'), |
| timeout=60) |
| |
| # Wait for new browser to come up. |
| login.wait_for_browser() |
| if sm_pid != self._get_oldest_pid_by_name(constants.SESSION_MANAGER): |
| raise error.TestFail('session_manager seems to have restarted') |
| |
| |
| def cleanup(self): |
| if os.path.exists(constants.ENABLE_BROWSER_HANG_DETECTION_FILE): |
| os.remove(constants.ENABLE_BROWSER_HANG_DETECTION_FILE) |
| self._pauser.end() |