# Copyright (c) 2010 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 from autotest_lib.client.common_lib import error from autotest_lib.client.cros import cros_logging from autotest_lib.client.cros.crash_test import CrashTest as CrashTestDefs from autotest_lib.server import autotest, site_host_attributes, test _CONSENT_FILE = '/home/chronos/Consent To Send Stats' _STOWED_CONSENT_FILE = '/var/lib/kernel-crash-server.consent' class logging_KernelCrashServer(test.test): """ Prepares a system for generating a kernel crash report, then crashes the system and call logging_KernelCrash client autotest to validate the resulting report. """ version = 1 def _exact_copy(self, source, dest): """Copy remote source to dest, where dest removed if src not present.""" self._host.run('rm -f "%s"; cp "%s" "%s" 2>/dev/null; true' % (dest, source, dest)) def _exists_on_client(self, f): return self._host.run('ls "%s"' % f, ignore_status=True).exit_status == 0 # Taken from KernelErrorPaths, which duplicates it, but is up to date def _enable_consent(self): """ Enable consent so that crashes get stored in /var/spool/crash. """ self._consent_files = [ (CrashTestDefs._PAUSE_FILE, None, 'chronos'), (CrashTestDefs._CONSENT_FILE, None, 'chronos'), (CrashTestDefs._POLICY_FILE, 'mock_metrics_on.policy', 'root'), (CrashTestDefs._OWNER_KEY_FILE, 'mock_metrics_owner.key', 'root'), ] for dst, src, owner in self._consent_files: if self._exists_on_client(dst): self._host.run('mv "%s" "%s.autotest_backup"' % (dst, dst)) if src: full_src = os.path.join(self.autodir, 'client/cros', src) self._host.send_file(full_src, dst) else: self._host.run('touch "%s"' % dst) self._host.run('chown "%s" "%s"' % (owner, dst)) def _restore_consent_files(self): """ Restore consent files to their previous values. """ for f, _, _ in self._consent_files: self._host.run('rm -f "%s"' % f) if self._exists_on_client('%s.autotest_backup' % f): self._host.run('mv "%s.autotest_backup" "%s"' % (f, f)) def cleanup(self): self._exact_copy(_STOWED_CONSENT_FILE, _CONSENT_FILE) test.test.cleanup(self) def _can_disable_consent(self): """Returns whether or not host can have consent disabled. Presence of /etc/send_metrics causes ui.conf job (which starts after chromeos_startup) to regenerate a consent file if one does not exist. Therefore, we cannot guarantee that crash-reporter.conf will start with the file gone if we removed it before causing a crash. Presence of /root/.leave_core causes crash_reporter to always handle crashes, so consent cannot be disabled in this case too. """ always_regen = self._host.run('[ -r /etc/send_metrics ]', ignore_status=True).exit_status == 0 is_devimg = self._host.run('[ -r /root/.leave_core ]', ignore_status=True).exit_status == 0 logging.info('always_regen: %d', always_regen) logging.info('is_devimg: %d', is_devimg) return not (always_regen or is_devimg) def _crash_it(self, consent): """Crash the host after setting the consent as given.""" if consent: self._enable_consent() else: self._restore_consent_files() logging.info('KernelCrashServer: crashing %s', self._host.hostname) lkdtm = "/sys/kernel/debug/provoke-crash/DIRECT" if self._exists_on_client(lkdtm): cmd = "echo BUG > %s" % (lkdtm) else: cmd = "echo bug > /proc/breakme" logging.info("Falling back to using /proc/breakme") boot_id = self._host.get_boot_id() self._host.run('sh -c "sync; sleep 1; %s" >/dev/null 2>&1 &' % (cmd)) self._host.wait_for_restart(old_boot_id=boot_id) def _run_while_paused(self, host): self._host = host client_at = autotest.Autotest(host) self._exact_copy(_CONSENT_FILE, _STOWED_CONSENT_FILE) client_at.run_test('logging_KernelCrash', tag='before-crash', is_before=True, consent=True) client_attributes = site_host_attributes.HostAttributes(host.hostname) if not client_attributes.has_chromeos_firmware: raise error.TestNAError( 'This device is unable to report kernel crashes') self._crash_it(True) # Check for crash handling with consent. client_at.run_test('logging_KernelCrash', tag='after-crash-consent', is_before=False, consent=True) if not self._can_disable_consent(): logging.info('This device always has metrics enabled, ' 'skipping test of metrics disabled mode.') else: self._crash_it(False) # Check for crash handling without consent. client_at.run_test('logging_KernelCrash', tag='after-crash-no-consent', is_before=False, consent=False) def run_once(self, host=None): # For the entire duration of this server test (across crashes # and boots after crashes) we want to disable log rotation. log_pauser = cros_logging.LogRotationPauser(host) try: log_pauser.begin() self._run_while_paused(host) finally: log_pauser.end()