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