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 grp, logging, os, pwd, re, stat, subprocess | 5 import grp, logging, os, pwd, re, stat, subprocess |
6 from signal import SIGSEGV | 6 from signal import SIGSEGV |
7 from autotest_lib.client.bin import site_crash_test, site_utils, test | 7 from autotest_lib.client.bin import site_crash_test, site_utils, test |
8 from autotest_lib.client.common_lib import error, utils | 8 from autotest_lib.client.common_lib import error, utils |
9 | 9 |
10 _COLLECTION_ERROR_SIGNATURE = 'crash_reporter-user-collection' | |
11 _CORE2MD_PATH = '/usr/bin/core2md' | |
10 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' | 12 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' |
11 _LEAVE_CORE_PATH = '/root/.leave_core' | 13 _LEAVE_CORE_PATH = '/root/.leave_core' |
12 _MAX_CRASH_DIRECTORY_SIZE = 32 | 14 _MAX_CRASH_DIRECTORY_SIZE = 32 |
13 | 15 |
14 | 16 |
15 class logging_UserCrash(site_crash_test.CrashTest): | 17 class logging_UserCrash(site_crash_test.CrashTest): |
16 version = 1 | 18 version = 1 |
17 | 19 |
18 | 20 |
19 def setup(self): | 21 def setup(self): |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 crasher_command.append(self._crasher_path) | 150 crasher_command.append(self._crasher_path) |
149 basename = os.path.basename(self._crasher_path) | 151 basename = os.path.basename(self._crasher_path) |
150 if not cause_crash: | 152 if not cause_crash: |
151 crasher_command.append('--nocrash') | 153 crasher_command.append('--nocrash') |
152 self._set_consent(consent) | 154 self._set_consent(consent) |
153 crasher = subprocess.Popen(crasher_command, | 155 crasher = subprocess.Popen(crasher_command, |
154 stdout=subprocess.PIPE, | 156 stdout=subprocess.PIPE, |
155 stderr=subprocess.PIPE) | 157 stderr=subprocess.PIPE) |
156 output = crasher.communicate()[1] | 158 output = crasher.communicate()[1] |
157 logging.debug('Output from %s: %s' % | 159 logging.debug('Output from %s: %s' % |
158 (self._crasher_path, output)) | 160 (crasher_command, output)) |
159 | 161 |
160 # Grab the pid from the process output. We can't just use | 162 # Grab the pid from the process output. We can't just use |
161 # crasher.pid unfortunately because that may be the PID of su. | 163 # crasher.pid unfortunately because that may be the PID of su. |
162 match = re.search(r'pid=(\d+)', output) | 164 match = re.search(r'pid=(\d+)', output) |
163 if not match: | 165 if not match: |
164 raise error.TestFail('Could not find pid output from crasher: %s' % | 166 raise error.TestFail('Could not find pid output from crasher: %s' % |
165 output) | 167 output) |
166 pid = int(match.group(1)) | 168 pid = int(match.group(1)) |
167 | 169 |
168 if consent: | 170 if consent: |
169 handled_string = 'handling' | 171 handled_string = 'handling' |
170 else: | 172 else: |
171 handled_string = 'ignoring' | 173 handled_string = 'ignoring - no consent' |
172 expected_message = ( | 174 expected_message = ( |
173 'Received crash notification for %s[%d] sig 11 (%s)' % | 175 'Received crash notification for %s[%d] sig 11 (%s)' % |
174 (basename, pid, handled_string)) | 176 (basename, pid, handled_string)) |
175 | 177 |
176 # Wait until no crash_reporter is running. | 178 # Wait until no crash_reporter is running. |
177 site_utils.poll_for_condition( | 179 site_utils.poll_for_condition( |
178 lambda: utils.system('pgrep crash_reporter', | 180 lambda: utils.system('pgrep crash_reporter', |
179 ignore_status=True) != 0, | 181 ignore_status=True) != 0, |
180 timeout=10, | 182 timeout=10, |
181 exception=error.TestError( | 183 exception=error.TestError( |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 | 221 |
220 | 222 |
221 def _check_minidump_stackwalk(self, minidump_path, basename, | 223 def _check_minidump_stackwalk(self, minidump_path, basename, |
222 from_crash_reporter): | 224 from_crash_reporter): |
223 # Now stackwalk the minidump | 225 # Now stackwalk the minidump |
224 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' % | 226 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' % |
225 (minidump_path, self._symbol_dir)) | 227 (minidump_path, self._symbol_dir)) |
226 self._verify_stack(stack, basename, from_crash_reporter) | 228 self._verify_stack(stack, basename, from_crash_reporter) |
227 | 229 |
228 | 230 |
229 def _check_generated_minidump_sending(self, meta_path, minidump_path, | 231 def _check_generated_report_sending(self, meta_path, payload_path, |
230 username, crasher_basename, | 232 username, exec_name, report_kind, |
231 will_syslog_give_name): | 233 expected_sig=None): |
232 # Now check that the sending works | 234 # Now check that the sending works |
233 result = self._call_sender_one_crash( | 235 result = self._call_sender_one_crash( |
234 username=username, | 236 username=username, |
235 report=os.path.basename(minidump_path)) | 237 report=os.path.basename(payload_path)) |
236 if (not result['send_attempt'] or not result['send_success'] or | 238 if (not result['send_attempt'] or not result['send_success'] or |
237 result['report_exists']): | 239 result['report_exists']): |
238 raise error.TestFail('Minidump not sent properly') | 240 raise error.TestFail('Report not sent properly') |
239 if will_syslog_give_name: | 241 if result['exec_name'] != exec_name: |
240 if result['exec_name'] != crasher_basename: | 242 raise error.TestFail('Executable name incorrect') |
241 raise error.TestFail('Executable name incorrect') | 243 if result['report_kind'] != report_kind: |
242 if result['report_kind'] != 'minidump': | |
243 raise error.TestFail('Expected a minidump report') | 244 raise error.TestFail('Expected a minidump report') |
244 if result['report_payload'] != minidump_path: | 245 if result['report_payload'] != payload_path: |
245 raise error.TestFail('Sent the wrong minidump payload') | 246 raise error.TestFail('Sent the wrong minidump payload') |
246 if result['meta_path'] != meta_path: | 247 if result['meta_path'] != meta_path: |
247 raise error.TestFail('Used the wrong meta file') | 248 raise error.TestFail('Used the wrong meta file') |
248 if result['sig'] is not None: | 249 if expected_sig is None: |
249 raise error.TestFail('User crash should not have signature') | 250 if result['sig'] is not None: |
251 raise error.TestFail('Report should not have signature') | |
252 else: | |
253 if not 'sig' in result or result['sig'] != expected_sig: | |
254 raise error.TestFail('Report signature mismatch: %s vs %s' % | |
255 (result['sig'], expected_sig)) | |
250 | 256 |
251 # Check version matches. | 257 # Check version matches. |
252 lsb_release = utils.read_file('/etc/lsb-release') | 258 lsb_release = utils.read_file('/etc/lsb-release') |
253 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) | 259 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) |
254 if not ('Version: %s' % version_match.group(1)) in result['output']: | 260 if not ('Version: %s' % version_match.group(1)) in result['output']: |
255 raise error.TestFail('Did not find version %s in log output' % | 261 raise error.TestFail('Did not find version %s in log output' % |
256 version_match.group(1)) | 262 version_match.group(1)) |
257 | 263 |
258 | 264 |
259 def _check_crashing_process(self, username, consent=True): | 265 def _run_crasher_process_and_analyze(self, username, |
266 cause_crash=True, consent=True): | |
260 self._log_reader.set_start_by_current() | 267 self._log_reader.set_start_by_current() |
261 | 268 |
262 result = self._run_crasher_process(username, consent=consent) | 269 result = self._run_crasher_process(username, cause_crash=cause_crash, |
270 consent=consent) | |
263 | 271 |
264 if not result['crashed']: | 272 if not result['crashed'] or not result['crash_reporter_caught']: |
265 raise error.TestFail('crasher did not do its job of crashing: %d' % | 273 return result; |
266 result['returncode']) | |
267 | |
268 if not result['crash_reporter_caught']: | |
269 logging.debug('Messages that should have included segv: %s' % | |
270 self._log_reader.get_logs()) | |
271 raise error.TestFail('Did not find segv message') | |
272 | 274 |
273 crash_dir = self._get_crash_dir(username) | 275 crash_dir = self._get_crash_dir(username) |
274 | 276 |
275 if not consent: | 277 if not consent: |
276 if os.path.exists(crash_dir): | 278 if os.path.exists(crash_dir): |
277 raise error.TestFail('Crash directory should not exist') | 279 raise error.TestFail('Crash directory should not exist') |
278 return | 280 return result |
279 | 281 |
280 crash_contents = os.listdir(crash_dir) | 282 crash_contents = os.listdir(crash_dir) |
281 basename = os.path.basename(self._crasher_path) | 283 basename = os.path.basename(self._crasher_path) |
282 | 284 |
283 breakpad_minidump = None | 285 breakpad_minidump = None |
284 crash_reporter_minidump = None | 286 crash_reporter_minidump = None |
285 crash_reporter_meta = None | 287 crash_reporter_meta = None |
288 crash_reporter_log = None | |
286 | 289 |
287 self._check_crash_directory_permissions(crash_dir) | 290 self._check_crash_directory_permissions(crash_dir) |
288 | 291 |
289 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) | 292 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) |
290 | 293 |
291 for filename in crash_contents: | 294 for filename in crash_contents: |
292 if filename.endswith('.core'): | 295 if filename.endswith('.core'): |
293 # Ignore core files. We'll test them later. | 296 # Ignore core files. We'll test them later. |
294 pass | 297 pass |
295 elif (filename.startswith(basename) and | 298 elif (filename.startswith(basename) and |
296 filename.endswith('.dmp')): | 299 filename.endswith('.dmp')): |
297 # This appears to be a minidump created by the crash reporter. | 300 # This appears to be a minidump created by the crash reporter. |
298 if not crash_reporter_minidump is None: | 301 if not crash_reporter_minidump is None: |
299 raise error.TestFail('Crash reporter wrote multiple ' | 302 raise error.TestFail('Crash reporter wrote multiple ' |
300 'minidumps') | 303 'minidumps') |
301 crash_reporter_minidump = os.path.join(crash_dir, filename) | 304 crash_reporter_minidump = os.path.join(crash_dir, filename) |
302 elif (filename.startswith(basename) and | 305 elif (filename.startswith(basename) and |
303 filename.endswith('.meta')): | 306 filename.endswith('.meta')): |
304 if not crash_reporter_meta is None: | 307 if not crash_reporter_meta is None: |
305 raise error.TestFail('Crash reported wrote multiple ' | 308 raise error.TestFail('Crash reporter wrote multiple ' |
306 'meta files') | 309 'meta files') |
307 crash_reporter_meta = os.path.join(crash_dir, filename) | 310 crash_reporter_meta = os.path.join(crash_dir, filename) |
311 elif (filename.startswith(basename) and | |
312 filename.endswith('.log')): | |
313 if not crash_reporter_log is None: | |
314 raise error.TestFail('Crash reporter wrote multiple ' | |
315 'log files') | |
316 crash_reporter_log = os.path.join(crash_dir, filename) | |
308 else: | 317 else: |
309 # This appears to be a breakpad created minidump. | 318 # This appears to be a breakpad created minidump. |
310 if not breakpad_minidump is None: | 319 if not breakpad_minidump is None: |
311 raise error.TestFail('Breakpad wrote multimpe minidumps') | 320 raise error.TestFail('Breakpad wrote multimpe minidumps') |
312 breakpad_minidump = os.path.join(crash_dir, filename) | 321 breakpad_minidump = os.path.join(crash_dir, filename) |
313 | 322 |
314 if breakpad_minidump: | 323 if breakpad_minidump: |
315 raise error.TestFail('%s did generate breakpad minidump' % basename) | 324 raise error.TestFail('%s did generate breakpad minidump' % basename) |
316 | 325 |
317 if not crash_reporter_minidump: | |
318 raise error.TestFail('crash reporter did not generate minidump') | |
319 | |
320 if not crash_reporter_meta: | 326 if not crash_reporter_meta: |
321 raise error.TestFail('crash reporter did not generate meta') | 327 raise error.TestFail('crash reporter did not generate meta') |
322 | 328 |
329 result['minidump'] = crash_reporter_minidump | |
330 result['basename'] = basename | |
331 result['meta'] = crash_reporter_meta | |
332 result['log'] = crash_reporter_log | |
333 return result | |
334 | |
335 | |
336 def _check_crashed_and_caught(self, result): | |
337 if not result['crashed']: | |
338 raise error.TestFail('crasher did not do its job of crashing: %d' % | |
339 result['returncode']) | |
340 | |
341 if not result['crash_reporter_caught']: | |
342 logging.debug('Messages that should have included segv: %s' % | |
343 self._log_reader.get_logs()) | |
344 raise error.TestFail('Did not find segv message') | |
345 | |
346 | |
347 def _check_crashing_process(self, username, consent=True): | |
348 result = self._run_crasher_process_and_analyze(username, | |
349 consent=consent) | |
350 | |
351 self._check_crashed_and_caught(result) | |
352 | |
353 if not consent: | |
354 return | |
355 | |
356 if not result['minidump']: | |
357 raise error.TestFail('crash reporter did not generate minidump') | |
358 | |
323 if not self._log_reader.can_find('Stored minidump to ' + | 359 if not self._log_reader.can_find('Stored minidump to ' + |
324 crash_reporter_minidump): | 360 result['minidump']): |
325 raise error.TestFail('crash reporter did not announce minidump') | 361 raise error.TestFail('crash reporter did not announce minidump') |
326 | 362 |
327 if crash_reporter_minidump: | 363 self._check_minidump_stackwalk(result['minidump'], |
328 self._check_minidump_stackwalk(crash_reporter_minidump, | 364 result['basename'], |
329 basename, | 365 from_crash_reporter=True) |
330 from_crash_reporter=True) | 366 self._check_generated_report_sending(result['meta'], |
331 will_syslog_give_name = True | 367 result['minidump'], |
332 | 368 username, |
333 self._check_generated_minidump_sending(crash_reporter_meta, | 369 result['basename'], |
334 crash_reporter_minidump, | 370 'minidump') |
335 username, | |
336 basename, | |
337 will_syslog_give_name) | |
338 | 371 |
339 def _test_no_crash(self): | 372 def _test_no_crash(self): |
340 """Test a program linked against libcrash_dumper can exit normally.""" | 373 """Test a program linked against libcrash_dumper can exit normally.""" |
341 self._log_reader.set_start_by_current() | 374 self._log_reader.set_start_by_current() |
342 result = self._run_crasher_process(username='root', | 375 result = self._run_crasher_process_and_analyze(username='root', |
343 cause_crash=False) | 376 cause_crash=False) |
344 if (result['crashed'] or | 377 if (result['crashed'] or |
345 result['crash_reporter_caught'] or | 378 result['crash_reporter_caught'] or |
346 result['returncode'] != 0): | 379 result['returncode'] != 0): |
347 raise error.TestFail('Normal exit of program with dumper failed') | 380 raise error.TestFail('Normal exit of program with dumper failed') |
348 | 381 |
349 | 382 |
350 def _test_chronos_crasher(self): | 383 def _test_chronos_crasher(self): |
351 """Test a user space crash when running as chronos is handled.""" | 384 """Test a user space crash when running as chronos is handled.""" |
352 self._check_crashing_process('chronos') | 385 self._check_crashing_process('chronos') |
353 | 386 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
402 if not self._log_reader.can_find(full_message): | 435 if not self._log_reader.can_find(full_message): |
403 raise error.TestFail('expected full message: ' + full_message) | 436 raise error.TestFail('expected full message: ' + full_message) |
404 | 437 |
405 if crash_dir_size != len(os.listdir(crash_dir)): | 438 if crash_dir_size != len(os.listdir(crash_dir)): |
406 utils.system('ls -l %s' % crash_dir) | 439 utils.system('ls -l %s' % crash_dir) |
407 raise error.TestFail('expected no new files (now %d were %d)', | 440 raise error.TestFail('expected no new files (now %d were %d)', |
408 len(os.listdir(crash_dir)), | 441 len(os.listdir(crash_dir)), |
409 crash_dir_size) | 442 crash_dir_size) |
410 | 443 |
411 | 444 |
445 def _check_collection_failure(self, test_option, failure_string): | |
446 # Add parameter to core_pattern. | |
447 old_core_pattern = utils.read_file(_CORE_PATTERN)[:-1] | |
448 try: | |
449 utils.system('echo "%s %s" > %s' % (old_core_pattern, test_option, | |
petkov
2010/10/28 22:30:50
You could use utils.write_one_line here and below.
| |
450 _CORE_PATTERN)) | |
451 result = self._run_crasher_process_and_analyze('root', | |
452 consent=True) | |
453 self._check_crashed_and_caught(result) | |
454 if not self._log_reader.can_find(failure_string): | |
455 raise error.TestFail('Did not find fail string in log %s' % | |
456 failure_string) | |
457 if result['minidump']: | |
458 raise error.TestFail('failed collection resulted in minidump') | |
459 if not result['log']: | |
460 raise error.TestFail('failed collection had no log') | |
461 log_contents = utils.read_file(result['log']) | |
462 if not log_contents.startswith(failure_string): | |
463 raise error.TestFail('Expected logged error ' | |
464 '\"%s\" was \"%s\"' % | |
465 (failure_string, log_contents)) | |
466 self._check_generated_report_sending(result['meta'], | |
467 result['log'], | |
468 'root', | |
469 result['basename'], | |
470 'log', | |
471 _COLLECTION_ERROR_SIGNATURE) | |
472 finally: | |
473 utils.system('echo "%s" > %s' % (old_core_pattern, _CORE_PATTERN)) | |
474 | |
475 | |
476 def _test_core2md_failure(self): | |
477 self._check_collection_failure('--core2md_failure_test', | |
478 'Problem during %s [result=1]: Usage:' % | |
479 _CORE2MD_PATH) | |
480 | |
481 | |
482 def _test_internal_directory_failure(self): | |
483 self._check_collection_failure('--directory_failure_test', | |
484 'Purposefully failing to create') | |
485 | |
486 | |
412 def _check_core_file_persisting(self, expect_persist): | 487 def _check_core_file_persisting(self, expect_persist): |
413 self._log_reader.set_start_by_current() | 488 self._log_reader.set_start_by_current() |
414 | 489 |
415 result = self._run_crasher_process('root') | 490 result = self._run_crasher_process('root') |
416 | 491 |
417 if not result['crashed']: | 492 if not result['crashed']: |
418 raise error.TestFail('crasher did not crash') | 493 raise error.TestFail('crasher did not crash') |
419 | 494 |
420 crash_contents = os.listdir(self._get_crash_dir('root')) | 495 crash_contents = os.listdir(self._get_crash_dir('root')) |
421 | 496 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
474 | 549 |
475 def run_once(self): | 550 def run_once(self): |
476 self.run_crash_tests(['reporter_startup', | 551 self.run_crash_tests(['reporter_startup', |
477 'reporter_shutdown', | 552 'reporter_shutdown', |
478 'no_crash', | 553 'no_crash', |
479 'chronos_crasher', | 554 'chronos_crasher', |
480 'chronos_crasher_no_consent', | 555 'chronos_crasher_no_consent', |
481 'root_crasher', | 556 'root_crasher', |
482 'root_crasher_no_consent', | 557 'root_crasher_no_consent', |
483 'max_enqueued_crashes', | 558 'max_enqueued_crashes', |
559 'core2md_failure', | |
560 'internal_directory_failure', | |
484 'core_file_persists_in_debug', | 561 'core_file_persists_in_debug', |
485 'core_file_removed_in_production'], | 562 'core_file_removed_in_production'], |
486 initialize_crash_reporter = True) | 563 initialize_crash_reporter = True) |
OLD | NEW |