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 |