Home | History | Annotate | Download | only in logging_KernelCrashServer
      1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging, os
      6 from autotest_lib.client.common_lib import error
      7 from autotest_lib.client.cros import cros_logging
      8 from autotest_lib.client.cros.crash_test import CrashTest as CrashTestDefs
      9 from autotest_lib.server import autotest, site_host_attributes, test
     10 
     11 _CONSENT_FILE = '/home/chronos/Consent To Send Stats'
     12 _STOWED_CONSENT_FILE = '/var/lib/kernel-crash-server.consent'
     13 
     14 
     15 class logging_KernelCrashServer(test.test):
     16     """
     17     Prepares a system for generating a kernel crash report, then crashes
     18     the system and call logging_KernelCrash client autotest to validate
     19     the resulting report.
     20     """
     21     version = 1
     22 
     23     def _exact_copy(self, source, dest):
     24         """Copy remote source to dest, where dest removed if src not present."""
     25         self._host.run('rm -f "%s"; cp "%s" "%s" 2>/dev/null; true' %
     26                        (dest, source, dest))
     27 
     28     def _exists_on_client(self, f):
     29         return self._host.run('ls "%s"' % f,
     30                                ignore_status=True).exit_status == 0
     31 
     32     # Taken from KernelErrorPaths, which duplicates it, but is up to date
     33     def _enable_consent(self):
     34         """ Enable consent so that crashes get stored in /var/spool/crash. """
     35         self._consent_files = [
     36             (CrashTestDefs._PAUSE_FILE, None, 'chronos'),
     37             (CrashTestDefs._CONSENT_FILE, None, 'chronos'),
     38             (CrashTestDefs._POLICY_FILE, 'mock_metrics_on.policy', 'root'),
     39             (CrashTestDefs._OWNER_KEY_FILE, 'mock_metrics_owner.key', 'root'),
     40             ]
     41         for dst, src, owner in self._consent_files:
     42             if self._exists_on_client(dst):
     43                 self._host.run('mv "%s" "%s.autotest_backup"' % (dst, dst))
     44             if src:
     45                 full_src = os.path.join(self.autodir, 'client/cros', src)
     46                 self._host.send_file(full_src, dst)
     47             else:
     48                 self._host.run('touch "%s"' % dst)
     49             self._host.run('chown "%s" "%s"' % (owner, dst))
     50 
     51     def _restore_consent_files(self):
     52         """ Restore consent files to their previous values. """
     53         for f, _, _ in self._consent_files:
     54             self._host.run('rm -f "%s"' % f)
     55             if self._exists_on_client('%s.autotest_backup' % f):
     56                 self._host.run('mv "%s.autotest_backup" "%s"' % (f, f))
     57 
     58     def cleanup(self):
     59         self._exact_copy(_STOWED_CONSENT_FILE, _CONSENT_FILE)
     60         test.test.cleanup(self)
     61 
     62 
     63     def _can_disable_consent(self):
     64         """Returns whether or not host can have consent disabled.
     65 
     66         Presence of /etc/send_metrics causes ui.conf job (which starts
     67         after chromeos_startup) to regenerate a consent file if one
     68         does not exist.  Therefore, we cannot guarantee that
     69         crash-reporter.conf will start with the file gone if we
     70         removed it before causing a crash.
     71 
     72         Presence of /root/.leave_core causes crash_reporter to always
     73         handle crashes, so consent cannot be disabled in this case too.
     74         """
     75         always_regen = self._host.run('[ -r /etc/send_metrics ]',
     76                                       ignore_status=True).exit_status == 0
     77         is_devimg = self._host.run('[ -r /root/.leave_core ]',
     78                                    ignore_status=True).exit_status == 0
     79         logging.info('always_regen: %d', always_regen)
     80         logging.info('is_devimg: %d', is_devimg)
     81         return not (always_regen or is_devimg)
     82 
     83 
     84     def _crash_it(self, consent):
     85         """Crash the host after setting the consent as given."""
     86         if consent:
     87             self._enable_consent()
     88         else:
     89             self._restore_consent_files()
     90         logging.info('KernelCrashServer: crashing %s', self._host.hostname)
     91         lkdtm = "/sys/kernel/debug/provoke-crash/DIRECT"
     92         if self._exists_on_client(lkdtm):
     93             cmd = "echo BUG > %s" % (lkdtm)
     94         else:
     95             cmd = "echo bug > /proc/breakme"
     96             logging.info("Falling back to using /proc/breakme")
     97         boot_id = self._host.get_boot_id()
     98         self._host.run('sh -c "sync; sleep 1; %s" >/dev/null 2>&1 &' % (cmd))
     99         self._host.wait_for_restart(old_boot_id=boot_id)
    100 
    101 
    102     def _run_while_paused(self, host):
    103         self._host = host
    104         client_at = autotest.Autotest(host)
    105         self._exact_copy(_CONSENT_FILE, _STOWED_CONSENT_FILE)
    106 
    107         client_at.run_test('logging_KernelCrash',
    108                            tag='before-crash',
    109                            is_before=True,
    110                            consent=True)
    111 
    112         client_attributes = site_host_attributes.HostAttributes(host.hostname)
    113         if not client_attributes.has_chromeos_firmware:
    114             raise error.TestNAError(
    115                 'This device is unable to report kernel crashes')
    116 
    117         self._crash_it(True)
    118 
    119         # Check for crash handling with consent.
    120         client_at.run_test('logging_KernelCrash',
    121                            tag='after-crash-consent',
    122                            is_before=False,
    123                            consent=True)
    124 
    125         if not self._can_disable_consent():
    126             logging.info('This device always has metrics enabled, '
    127                          'skipping test of metrics disabled mode.')
    128         else:
    129             self._crash_it(False)
    130 
    131             # Check for crash handling without consent.
    132             client_at.run_test('logging_KernelCrash',
    133                                tag='after-crash-no-consent',
    134                                is_before=False,
    135                                consent=False)
    136 
    137     def run_once(self, host=None):
    138         # For the entire duration of this server test (across crashes
    139         # and boots after crashes) we want to disable log rotation.
    140         log_pauser = cros_logging.LogRotationPauser(host)
    141         try:
    142             log_pauser.begin()
    143             self._run_while_paused(host)
    144         finally:
    145             log_pauser.end()
    146