| 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' | 10 _COLLECTION_ERROR_SIGNATURE = 'crash_reporter-user-collection' |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 if output != 'core': | 49 if output != 'core': |
| 50 raise error.TestFail('core pattern should have been core, not %s' % | 50 raise error.TestFail('core pattern should have been core, not %s' % |
| 51 output) | 51 output) |
| 52 | 52 |
| 53 | 53 |
| 54 def _prepare_crasher(self): | 54 def _prepare_crasher(self): |
| 55 """Extract the crasher and set its permissions. | 55 """Extract the crasher and set its permissions. |
| 56 | 56 |
| 57 crasher is only gzipped to subvert Portage stripping. | 57 crasher is only gzipped to subvert Portage stripping. |
| 58 """ | 58 """ |
| 59 self.enable_crash_filtering('crasher_nobreakpad') | |
| 60 self._crasher_path = os.path.join(self.srcdir, 'crasher_nobreakpad') | 59 self._crasher_path = os.path.join(self.srcdir, 'crasher_nobreakpad') |
| 61 utils.system('cd %s; tar xzf crasher.tgz-unmasked' % | 60 utils.system('cd %s; tar xzf crasher.tgz-unmasked' % |
| 62 self.srcdir) | 61 self.srcdir) |
| 63 # Make sure all users (specifically chronos) have access to | 62 # Make sure all users (specifically chronos) have access to |
| 64 # this directory and its decendents in order to run crasher | 63 # this directory and its decendents in order to run crasher |
| 65 # executable as different users. | 64 # executable as different users. |
| 66 utils.system('chmod -R a+rx ' + self.bindir) | 65 utils.system('chmod -R a+rx ' + self.bindir) |
| 67 | 66 |
| 68 | 67 |
| 69 def _populate_symbols(self): | 68 def _populate_symbols(self): |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 if not self._is_frame_in_stack(15, basename, 'recbomb', | 157 if not self._is_frame_in_stack(15, basename, 'recbomb', |
| 159 'bomb.cc', 12, stack): | 158 'bomb.cc', 12, stack): |
| 160 raise error.TestFail('Did not show recursion line on stack') | 159 raise error.TestFail('Did not show recursion line on stack') |
| 161 | 160 |
| 162 # Should identify main line | 161 # Should identify main line |
| 163 if not self._is_frame_in_stack(16, basename, 'main', | 162 if not self._is_frame_in_stack(16, basename, 'main', |
| 164 'crasher.cc', 21, stack): | 163 'crasher.cc', 21, stack): |
| 165 raise error.TestFail('Did not show main on stack') | 164 raise error.TestFail('Did not show main on stack') |
| 166 | 165 |
| 167 | 166 |
| 168 def _run_crasher_process(self, username, cause_crash=True, consent=True): | 167 def _run_crasher_process(self, username, cause_crash=True, consent=True, |
| 168 crasher_path=None): |
| 169 """Runs the crasher process. | 169 """Runs the crasher process. |
| 170 | 170 |
| 171 Args: | 171 Args: |
| 172 username: runs as given user | 172 username: runs as given user |
| 173 extra_args: additional parameters to pass to crasher process | 173 extra_args: additional parameters to pass to crasher process |
| 174 | 174 |
| 175 Returns: | 175 Returns: |
| 176 A dictionary with keys: | 176 A dictionary with keys: |
| 177 returncode: return code of the crasher | 177 returncode: return code of the crasher |
| 178 crashed: did the crasher return segv error code | 178 crashed: did the crasher return segv error code |
| 179 crash_reporter_caught: did crash_reporter catch a segv | 179 crash_reporter_caught: did crash_reporter catch a segv |
| 180 output: stderr/stdout output of the crasher process | 180 output: stderr/stdout output of the crasher process |
| 181 """ | 181 """ |
| 182 self._prepare_crasher() | 182 if crasher_path is None: crasher_path=self._crasher_path |
| 183 self._populate_symbols() | 183 self.enable_crash_filtering(os.path.basename(crasher_path)) |
| 184 | 184 |
| 185 if username != 'root': | 185 if username != 'root': |
| 186 crasher_command = ['su', username, '-c'] | 186 crasher_command = ['su', username, '-c'] |
| 187 expected_result = 128 + SIGSEGV | 187 expected_result = 128 + SIGSEGV |
| 188 else: | 188 else: |
| 189 crasher_command = [] | 189 crasher_command = [] |
| 190 expected_result = -SIGSEGV | 190 expected_result = -SIGSEGV |
| 191 | 191 |
| 192 crasher_command.append(self._crasher_path) | 192 crasher_command.append(crasher_path) |
| 193 basename = os.path.basename(self._crasher_path) | 193 basename = os.path.basename(crasher_path) |
| 194 if not cause_crash: | 194 if not cause_crash: |
| 195 crasher_command.append('--nocrash') | 195 crasher_command.append('--nocrash') |
| 196 self._set_consent(consent) | 196 self._set_consent(consent) |
| 197 crasher = subprocess.Popen(crasher_command, | 197 crasher = subprocess.Popen(crasher_command, |
| 198 stdout=subprocess.PIPE, | 198 stdout=subprocess.PIPE, |
| 199 stderr=subprocess.PIPE) | 199 stderr=subprocess.PIPE) |
| 200 output = crasher.communicate()[1] | 200 output = crasher.communicate()[1] |
| 201 logging.debug('Output from %s: %s' % | 201 logging.debug('Output from %s: %s' % |
| 202 (crasher_command, output)) | 202 (crasher_command, output)) |
| 203 | 203 |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 | 298 |
| 299 # Check version matches. | 299 # Check version matches. |
| 300 lsb_release = utils.read_file('/etc/lsb-release') | 300 lsb_release = utils.read_file('/etc/lsb-release') |
| 301 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) | 301 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) |
| 302 if not ('Version: %s' % version_match.group(1)) in result['output']: | 302 if not ('Version: %s' % version_match.group(1)) in result['output']: |
| 303 raise error.TestFail('Did not find version %s in log output' % | 303 raise error.TestFail('Did not find version %s in log output' % |
| 304 version_match.group(1)) | 304 version_match.group(1)) |
| 305 | 305 |
| 306 | 306 |
| 307 def _run_crasher_process_and_analyze(self, username, | 307 def _run_crasher_process_and_analyze(self, username, |
| 308 cause_crash=True, consent=True): | 308 cause_crash=True, consent=True, |
| 309 crasher_path=None): |
| 309 self._log_reader.set_start_by_current() | 310 self._log_reader.set_start_by_current() |
| 310 | 311 |
| 312 if crasher_path is None: crasher_path=self._crasher_path |
| 311 result = self._run_crasher_process(username, cause_crash=cause_crash, | 313 result = self._run_crasher_process(username, cause_crash=cause_crash, |
| 312 consent=consent) | 314 consent=consent, |
| 315 crasher_path=crasher_path) |
| 313 | 316 |
| 314 if not result['crashed'] or not result['crash_reporter_caught']: | 317 if not result['crashed'] or not result['crash_reporter_caught']: |
| 315 return result; | 318 return result; |
| 316 | 319 |
| 317 crash_dir = self._get_crash_dir(username) | 320 crash_dir = self._get_crash_dir(username) |
| 318 | 321 |
| 319 if not consent: | 322 if not consent: |
| 320 if os.path.exists(crash_dir): | 323 if os.path.exists(crash_dir): |
| 321 raise error.TestFail('Crash directory should not exist') | 324 raise error.TestFail('Crash directory should not exist') |
| 322 return result | 325 return result |
| 323 | 326 |
| 324 crash_contents = os.listdir(crash_dir) | 327 crash_contents = os.listdir(crash_dir) |
| 325 basename = os.path.basename(self._crasher_path) | 328 basename = os.path.basename(crasher_path) |
| 326 | 329 |
| 327 breakpad_minidump = None | 330 breakpad_minidump = None |
| 328 crash_reporter_minidump = None | 331 crash_reporter_minidump = None |
| 329 crash_reporter_meta = None | 332 crash_reporter_meta = None |
| 330 crash_reporter_log = None | 333 crash_reporter_log = None |
| 331 | 334 |
| 332 self._check_crash_directory_permissions(crash_dir) | 335 self._check_crash_directory_permissions(crash_dir) |
| 333 | 336 |
| 334 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) | 337 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) |
| 335 | 338 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 site_utils.poll_for_condition( | 456 site_utils.poll_for_condition( |
| 454 lambda: self._log_reader.can_find(to_find), | 457 lambda: self._log_reader.can_find(to_find), |
| 455 timeout=10, | 458 timeout=10, |
| 456 exception=error.TestError( | 459 exception=error.TestError( |
| 457 'Timeout waiting for: ' + to_find + ' in ' + | 460 'Timeout waiting for: ' + to_find + ' in ' + |
| 458 self._log_reader.get_logs())) | 461 self._log_reader.get_logs())) |
| 459 | 462 |
| 460 | 463 |
| 461 def _test_crash_filtering(self): | 464 def _test_crash_filtering(self): |
| 462 """Test that crash filtering (a feature needed for testing) works.""" | 465 """Test that crash filtering (a feature needed for testing) works.""" |
| 463 self._prepare_crasher() | |
| 464 crasher_basename = os.path.basename(self._crasher_path) | 466 crasher_basename = os.path.basename(self._crasher_path) |
| 465 self._log_reader.set_start_by_current() | 467 self._log_reader.set_start_by_current() |
| 466 | 468 |
| 467 self.enable_crash_filtering('none') | 469 self.enable_crash_filtering('none') |
| 468 self._check_filter_crasher(False) | 470 self._check_filter_crasher(False) |
| 469 | 471 |
| 470 self.enable_crash_filtering('sleep') | 472 self.enable_crash_filtering('sleep') |
| 471 self._check_filter_crasher(False) | 473 self._check_filter_crasher(False) |
| 472 | 474 |
| 473 self.disable_crash_filtering() | 475 self.disable_crash_filtering() |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 self._check_collection_failure('--core2md_failure_test', | 554 self._check_collection_failure('--core2md_failure_test', |
| 553 'Problem during %s [result=1]: Usage:' % | 555 'Problem during %s [result=1]: Usage:' % |
| 554 _CORE2MD_PATH) | 556 _CORE2MD_PATH) |
| 555 | 557 |
| 556 | 558 |
| 557 def _test_internal_directory_failure(self): | 559 def _test_internal_directory_failure(self): |
| 558 self._check_collection_failure('--directory_failure_test', | 560 self._check_collection_failure('--directory_failure_test', |
| 559 'Purposefully failing to create') | 561 'Purposefully failing to create') |
| 560 | 562 |
| 561 | 563 |
| 564 def _test_crash_logs_creation(self): |
| 565 logs_triggering_crasher = os.path.join(os.path.dirname(self.bindir), |
| 566 'crash_log_test') |
| 567 # Copy crasher_path to a test location with correct mode and a |
| 568 # special name to trigger crash log creation. |
| 569 utils.system('cp -a "%s" "%s"' % (self._crasher_path, |
| 570 logs_triggering_crasher)) |
| 571 result = self._run_crasher_process_and_analyze( |
| 572 'root', crasher_path=logs_triggering_crasher) |
| 573 self._check_crashed_and_caught(result) |
| 574 contents = utils.read_file(result['log']) |
| 575 if contents != 'hello world\n': |
| 576 raise error.TestFail('Crash log contents unexpected: %s' % contents) |
| 577 if not ('log=' + result['log']) in utils.read_file(result['meta']): |
| 578 raise error.TestFail('Meta file does not reference log') |
| 579 |
| 580 |
| 581 def _test_crash_log_infinite_recursion(self): |
| 582 recursion_triggering_crasher = os.path.join( |
| 583 os.path.dirname(self.bindir), 'crash_log_recursion_test') |
| 584 # The configuration file hardcodes this path, so make sure it's still |
| 585 # the same. |
| 586 if (recursion_triggering_crasher != |
| 587 '/home/autotest/tests/crash_log_recursion_test'): |
| 588 raise error.TestError('Path to recursion test changed') |
| 589 # Copy crasher_path to a test location with correct mode and a |
| 590 # special name to trigger crash log creation. |
| 591 utils.system('cp -a "%s" "%s"' % (self._crasher_path, |
| 592 recursion_triggering_crasher)) |
| 593 # Simply completing this command means that we avoided |
| 594 # infinite recursion. |
| 595 result = self._run_crasher_process( |
| 596 'root', crasher_path=recursion_triggering_crasher) |
| 597 |
| 598 |
| 562 def _check_core_file_persisting(self, expect_persist): | 599 def _check_core_file_persisting(self, expect_persist): |
| 563 self._log_reader.set_start_by_current() | 600 self._log_reader.set_start_by_current() |
| 564 | 601 |
| 565 result = self._run_crasher_process('root') | 602 result = self._run_crasher_process('root') |
| 566 | 603 |
| 567 if not result['crashed']: | 604 if not result['crashed']: |
| 568 raise error.TestFail('crasher did not crash') | 605 raise error.TestFail('crasher did not crash') |
| 569 | 606 |
| 570 crash_contents = os.listdir(self._get_crash_dir('root')) | 607 crash_contents = os.listdir(self._get_crash_dir('root')) |
| 571 | 608 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 616 raise error.TestFail('.leave_core file did not disappear') | 653 raise error.TestFail('.leave_core file did not disappear') |
| 617 self._check_core_file_persisting(False) | 654 self._check_core_file_persisting(False) |
| 618 finally: | 655 finally: |
| 619 os.system('umount /root') | 656 os.system('umount /root') |
| 620 | 657 |
| 621 | 658 |
| 622 # TODO(kmixter): Test crashing a process as ntp or some other | 659 # TODO(kmixter): Test crashing a process as ntp or some other |
| 623 # non-root, non-chronos user. | 660 # non-root, non-chronos user. |
| 624 | 661 |
| 625 def run_once(self): | 662 def run_once(self): |
| 663 self._prepare_crasher() |
| 664 self._populate_symbols() |
| 626 self.run_crash_tests(['reporter_startup', | 665 self.run_crash_tests(['reporter_startup', |
| 627 'reporter_shutdown', | 666 'reporter_shutdown', |
| 628 'no_crash', | 667 'no_crash', |
| 629 'chronos_crasher', | 668 'chronos_crasher', |
| 630 'chronos_crasher_no_consent', | 669 'chronos_crasher_no_consent', |
| 631 'root_crasher', | 670 'root_crasher', |
| 632 'root_crasher_no_consent', | 671 'root_crasher_no_consent', |
| 633 'crash_filtering', | 672 'crash_filtering', |
| 634 'max_enqueued_crashes', | 673 'max_enqueued_crashes', |
| 635 'core2md_failure', | 674 'core2md_failure', |
| 636 'internal_directory_failure', | 675 'internal_directory_failure', |
| 676 'crash_logs_creation', |
| 677 'crash_log_infinite_recursion', |
| 637 'core_file_persists_in_debug', | 678 'core_file_persists_in_debug', |
| 638 'core_file_removed_in_production'], | 679 'core_file_removed_in_production'], |
| 639 initialize_crash_reporter = True) | 680 initialize_crash_reporter = True) |
| OLD | NEW |