| OLD | NEW |
| 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging, os | 5 import logging, os, re |
| 6 from autotest_lib.client.bin import site_crash_test, site_utils, test | 6 from autotest_lib.client.bin import site_crash_test, site_utils, test |
| 7 from autotest_lib.client.common_lib import error, utils | 7 from autotest_lib.client.common_lib import error, utils |
| 8 | 8 |
| 9 _25_HOURS_AGO = -25 * 60 * 60 |
| 9 _CRASH_SENDER_CRON_PATH = '/etc/cron.hourly/crash_sender.hourly' | 10 _CRASH_SENDER_CRON_PATH = '/etc/cron.hourly/crash_sender.hourly' |
| 10 _DAILY_RATE_LIMIT = 32 | 11 _DAILY_RATE_LIMIT = 32 |
| 11 _MIN_UNIQUE_TIMES = 4 | 12 _MIN_UNIQUE_TIMES = 4 |
| 13 _HWCLASS_PATH = '/sys/devices/platform/chromeos_acpi/HWID' |
| 12 _SECONDS_SEND_SPREAD = 3600 | 14 _SECONDS_SEND_SPREAD = 3600 |
| 13 | 15 |
| 14 | |
| 15 class logging_CrashSender(site_crash_test.CrashTest): | 16 class logging_CrashSender(site_crash_test.CrashTest): |
| 16 version = 1 | 17 version = 1 |
| 17 | 18 |
| 18 | 19 |
| 19 def _test_sender_simple_minidump(self): | 20 def _check_hardware_info(self, result): |
| 20 """Test sending a single minidump crash report.""" | 21 # Get board name |
| 22 lsb_release = utils.read_file('/etc/lsb-release') |
| 23 board_match = re.search(r'CHROMEOS_RELEASE_BOARD=(.*)', lsb_release) |
| 24 if not ('Board: %s' % board_match.group(1)) in result['output']: |
| 25 raise error.TestFail('Missing board name %s in output' % |
| 26 board_match.group(1)) |
| 27 # Get hwid |
| 28 hwclass = 'unknown' |
| 29 if os.path.exists(_HWCLASS_PATH): |
| 30 hwclass = utils.read_file(_HWCLASS_PATH) |
| 31 if not ('HWClass: %s' % hwclass) in result['output']: |
| 32 raise error.TestFail('Missing hwclass %s in output' % hwclass) |
| 33 |
| 34 |
| 35 def _check_simple_minidump_send(self, report): |
| 21 self._set_sending(True) | 36 self._set_sending(True) |
| 22 result = self._call_sender_one_crash() | 37 result = self._call_sender_one_crash(report=report) |
| 23 if (result['report_exists'] or | 38 if (result['report_exists'] or |
| 24 result['rate_count'] != 1 or | 39 result['rate_count'] != 1 or |
| 25 not result['send_attempt'] or | 40 not result['send_attempt'] or |
| 26 not result['send_success'] or | 41 not result['send_success'] or |
| 27 result['sleep_time'] < 0 or | 42 result['sleep_time'] < 0 or |
| 28 result['sleep_time'] >= _SECONDS_SEND_SPREAD or | 43 result['sleep_time'] >= _SECONDS_SEND_SPREAD or |
| 29 result['report_kind'] != 'minidump' or | 44 result['report_kind'] != 'minidump' or |
| 30 result['exec_name'] != 'fake'): | 45 result['report_payload'] != '/var/spool/crash/fake.dmp' or |
| 46 result['exec_name'] != 'fake' or |
| 47 not 'Version: my_ver' in result['output']): |
| 31 raise error.TestFail('Simple minidump send failed') | 48 raise error.TestFail('Simple minidump send failed') |
| 49 self._check_hardware_info(result) |
| 50 |
| 51 |
| 52 def _test_sender_simple_minidump(self): |
| 53 """Test sending a single minidump crash report.""" |
| 54 self._check_simple_minidump_send(None) |
| 55 |
| 56 |
| 57 def _shift_file_mtime(self, path, delta): |
| 58 statinfo = os.stat(path) |
| 59 os.utime(path, (statinfo.st_atime, |
| 60 statinfo.st_mtime + delta)) |
| 61 |
| 62 |
| 63 def _test_sender_simple_old_minidump(self): |
| 64 """Test that old minidumps and metadata are sent.""" |
| 65 self._set_sending(True) |
| 66 dmp_path = self.write_crash_dir_entry('fake.dmp', '') |
| 67 meta_path = self.write_fake_meta('fake.meta', 'fake') |
| 68 self._shift_file_mtime(dmp_path, _25_HOURS_AGO) |
| 69 self._shift_file_mtime(meta_path, _25_HOURS_AGO) |
| 70 self._check_simple_minidump_send(meta_path) |
| 32 | 71 |
| 33 | 72 |
| 34 def _test_sender_simple_kernel_crash(self): | 73 def _test_sender_simple_kernel_crash(self): |
| 35 """Test sending a single kcrash report.""" | 74 """Test sending a single kcrash report.""" |
| 36 self._set_sending(True) | 75 self._set_sending(True) |
| 37 kcrash_fake_report = self.create_fake_crash_dir_entry( | 76 kcrash_fake_report = self.write_crash_dir_entry( |
| 38 'kernel.today.kcrash') | 77 'kernel.today.kcrash', '') |
| 78 self.write_fake_meta('kernel.today.meta', 'kernel') |
| 39 result = self._call_sender_one_crash(report=kcrash_fake_report) | 79 result = self._call_sender_one_crash(report=kcrash_fake_report) |
| 40 if (result['report_exists'] or | 80 if (result['report_exists'] or |
| 41 result['rate_count'] != 1 or | 81 result['rate_count'] != 1 or |
| 42 not result['send_attempt'] or | 82 not result['send_attempt'] or |
| 43 not result['send_success'] or | 83 not result['send_success'] or |
| 44 result['sleep_time'] < 0 or | 84 result['sleep_time'] < 0 or |
| 45 result['sleep_time'] >= _SECONDS_SEND_SPREAD or | 85 result['sleep_time'] >= _SECONDS_SEND_SPREAD or |
| 46 result['report_kind'] != 'kcrash' or | 86 result['report_kind'] != 'kcrash' or |
| 87 (result['report_payload'] != |
| 88 '/var/spool/crash/kernel.today.kcrash') or |
| 47 result['exec_name'] != 'kernel'): | 89 result['exec_name'] != 'kernel'): |
| 48 raise error.TestFail('Simple kcrash send failed') | 90 raise error.TestFail('Simple kcrash send failed') |
| 91 self._check_hardware_info(result) |
| 49 | 92 |
| 50 | 93 |
| 51 def _test_sender_pausing(self): | 94 def _test_sender_pausing(self): |
| 52 """Test the sender returns immediately when the pause file is present. | 95 """Test the sender returns immediately when the pause file is present. |
| 53 | 96 |
| 54 This is testing the sender's test functionality - if this regresses, | 97 This is testing the sender's test functionality - if this regresses, |
| 55 other tests can become flaky because the cron-started sender may run | 98 other tests can become flaky because the cron-started sender may run |
| 56 asynchronously to these tests.""" | 99 asynchronously to these tests.""" |
| 57 self._set_sending(False) | 100 self._set_sending(False) |
| 58 result = self._call_sender_one_crash() | 101 result = self._call_sender_one_crash() |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 # Now the _DAILY_RATE_LIMIT ^ th send request should fail. | 136 # Now the _DAILY_RATE_LIMIT ^ th send request should fail. |
| 94 result = self._call_sender_one_crash() | 137 result = self._call_sender_one_crash() |
| 95 if (not result['report_exists'] or | 138 if (not result['report_exists'] or |
| 96 not 'Cannot send more crashes' in result['output'] or | 139 not 'Cannot send more crashes' in result['output'] or |
| 97 result['rate_count'] != _DAILY_RATE_LIMIT): | 140 result['rate_count'] != _DAILY_RATE_LIMIT): |
| 98 raise error.TestFail('Crash rate limiting did not take effect') | 141 raise error.TestFail('Crash rate limiting did not take effect') |
| 99 | 142 |
| 100 # Set one rate file a day earlier and verify can send | 143 # Set one rate file a day earlier and verify can send |
| 101 rate_files = os.listdir(self._CRASH_SENDER_RATE_DIR) | 144 rate_files = os.listdir(self._CRASH_SENDER_RATE_DIR) |
| 102 rate_path = os.path.join(self._CRASH_SENDER_RATE_DIR, rate_files[0]) | 145 rate_path = os.path.join(self._CRASH_SENDER_RATE_DIR, rate_files[0]) |
| 103 statinfo = os.stat(rate_path) | 146 self._shift_file_mtime(rate_path, _25_HOURS_AGO) |
| 104 os.utime(rate_path, (statinfo.st_atime, | |
| 105 statinfo.st_mtime - (60 * 60 * 25))) | |
| 106 utils.system('ls -l ' + self._CRASH_SENDER_RATE_DIR) | 147 utils.system('ls -l ' + self._CRASH_SENDER_RATE_DIR) |
| 107 result = self._call_sender_one_crash() | 148 result = self._call_sender_one_crash() |
| 108 if (not result['send_attempt'] or | 149 if (not result['send_attempt'] or |
| 109 not result['send_success'] or | 150 not result['send_success'] or |
| 110 result['rate_count'] != _DAILY_RATE_LIMIT): | 151 result['rate_count'] != _DAILY_RATE_LIMIT): |
| 111 raise error.TestFail('Crash not sent even after 25hrs pass') | 152 raise error.TestFail('Crash not sent even after 25hrs pass') |
| 112 | 153 |
| 113 | 154 |
| 114 def _test_sender_single_instance(self): | 155 def _test_sender_single_instance(self): |
| 115 """Test the sender fails to start when another instance is running. | 156 """Test the sender fails to start when another instance is running. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 133 if not result['send_attempt'] or result['send_success']: | 174 if not result['send_attempt'] or result['send_success']: |
| 134 raise error.TestError('Did not properly cause a send failure') | 175 raise error.TestError('Did not properly cause a send failure') |
| 135 if result['rate_count'] != 1: | 176 if result['rate_count'] != 1: |
| 136 raise error.TestFail('Did not count a failed send against rate ' | 177 raise error.TestFail('Did not count a failed send against rate ' |
| 137 'limiting') | 178 'limiting') |
| 138 if not result['report_exists']: | 179 if not result['report_exists']: |
| 139 raise error.TestFail('Expected minidump to be saved for later ' | 180 raise error.TestFail('Expected minidump to be saved for later ' |
| 140 'sending') | 181 'sending') |
| 141 | 182 |
| 142 | 183 |
| 143 def _test_sender_leaves_core_files(self): | 184 def _test_sender_orphaned_files(self): |
| 144 """Test that a core file is left in the send directory. | 185 """Test that payload and unknown files that are old are removed.""" |
| 145 | 186 core_file = self.write_crash_dir_entry('random1.core', '') |
| 146 Core files will only persist for developer/testing images. We | 187 unknown_file = self.write_crash_dir_entry('.unknown', '') |
| 147 should never remove such a file.""" | 188 # As new files, we expect crash_sender to leave these alone. |
| 148 self._set_sending(True) | 189 self._set_sending(True) |
| 149 # Call prepare function to make sure the directory exists. | 190 results = self._call_sender_one_crash() |
| 150 core_name = 'something.ending.with.core' | 191 if ('Removing old orphaned file' in results['output'] or |
| 151 core_path = self.create_fake_crash_dir_entry(core_name) | 192 not os.path.exists(core_file) or |
| 152 result = self._call_sender_one_crash() | 193 not os.path.exists(unknown_file)): |
| 153 if not 'Ignoring core file.' in result['output']: | 194 raise error.TestFail('New orphaned files were removed') |
| 154 raise error.TestFail('Expected ignoring core file message') | 195 self._shift_file_mtime(core_file, _25_HOURS_AGO) |
| 155 if not os.path.exists(core_path): | 196 self._shift_file_mtime(unknown_file, _25_HOURS_AGO) |
| 156 raise error.TestFail('Core file was removed') | 197 results = self._call_sender_one_crash() |
| 198 if (not 'Removing old orphaned file' in results['output'] or |
| 199 os.path.exists(core_file) or os.path.exists(unknown_file)): |
| 200 raise error.TestFail( |
| 201 'Old orphaned files were not removed') |
| 157 | 202 |
| 158 | 203 |
| 159 def _test_sender_unknown_report_kind(self): | 204 def _test_sender_incomplete_metadata(self): |
| 205 """Test that incomplete metadata file is removed once old.""" |
| 206 meta_file = self.write_crash_dir_entry('incomplete.meta', 'half=1') |
| 207 dmp_file = self.write_crash_dir_entry('incomplete.dmp', '') |
| 208 # As new files, we expect crash_sender to leave these alone. |
| 160 self._set_sending(True) | 209 self._set_sending(True) |
| 161 bad_report = self.create_fake_crash_dir_entry('fake.bad') | 210 results = self._call_sender_one_crash() |
| 162 result = self._call_sender_one_crash(report=bad_report) | 211 if ('Removing recent incomplete report' in results['output'] or |
| 163 if (result['report_exists'] or | 212 not os.path.exists(meta_file) or |
| 164 result['rate_count'] != 0 or | 213 not os.path.exists(dmp_file)): |
| 165 result['send_attempt'] or | 214 raise error.TestFail('New unknown files were removed') |
| 166 result['send_success'] or | 215 self._shift_file_mtime(meta_file, _25_HOURS_AGO) |
| 167 not 'Unknown report' in result['output']): | 216 results = self._call_sender_one_crash() |
| 168 raise error.TestFail('Error handling of unknown report kind failed') | 217 if (not 'Removing old incomplete metadata' in results['output'] or |
| 218 os.path.exists(meta_file) or os.path.exists(dmp_file)): |
| 219 raise error.TestFail( |
| 220 'Old unknown/incomplete files were not removed') |
| 169 | 221 |
| 170 | 222 |
| 171 def _test_cron_runs(self): | 223 def _test_cron_runs(self): |
| 172 """Test sender runs successfully as part of the hourly cron job. | 224 """Test sender runs successfully as part of the hourly cron job. |
| 173 | 225 |
| 174 Assuming we've run test_sender_simple which shows that a minidump | 226 Assuming we've run test_sender_simple which shows that a minidump |
| 175 gets removed as part of sending, we run the cron job (which is | 227 gets removed as part of sending, we run the cron job (which is |
| 176 asynchronous) and wait for that file to be removed to just verify | 228 asynchronous) and wait for that file to be removed to just verify |
| 177 the job eventually runs the sender.""" | 229 the job eventually runs the sender.""" |
| 178 self._set_sending(True) | 230 self._set_sending(True) |
| (...skipping 11 matching lines...) Expand all Loading... |
| 190 crash_sender_log = self._log_reader.get_logs() | 242 crash_sender_log = self._log_reader.get_logs() |
| 191 logging.debug('Contents of crash sender log: ' + crash_sender_log) | 243 logging.debug('Contents of crash sender log: ' + crash_sender_log) |
| 192 result = self._parse_sender_output(crash_sender_log) | 244 result = self._parse_sender_output(crash_sender_log) |
| 193 if not result['send_attempt'] or not result['send_success']: | 245 if not result['send_attempt'] or not result['send_success']: |
| 194 raise error.TestFail('Cron simple run test failed') | 246 raise error.TestFail('Cron simple run test failed') |
| 195 | 247 |
| 196 | 248 |
| 197 def run_once(self): | 249 def run_once(self): |
| 198 self.run_crash_tests([ | 250 self.run_crash_tests([ |
| 199 'sender_simple_minidump', | 251 'sender_simple_minidump', |
| 252 'sender_simple_old_minidump', |
| 200 'sender_simple_kernel_crash', | 253 'sender_simple_kernel_crash', |
| 201 'sender_pausing', | 254 'sender_pausing', |
| 202 'sender_reports_disabled', | 255 'sender_reports_disabled', |
| 203 'sender_rate_limiting', | 256 'sender_rate_limiting', |
| 204 'sender_single_instance', | 257 'sender_single_instance', |
| 205 'sender_send_fails', | 258 'sender_send_fails', |
| 206 'sender_leaves_core_files', | 259 'sender_orphaned_files', |
| 207 'sender_unknown_report_kind', | 260 'sender_incomplete_metadata', |
| 208 'cron_runs']) | 261 'cron_runs']) |
| OLD | NEW |