164 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
| # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
 | |
| # Use of this source code is governed by a BSD-style license that can be
 | |
| # found in the LICENSE file.
 | |
| 
 | |
| import logging, os, time
 | |
| from autotest_lib.client.bin import utils
 | |
| from autotest_lib.client.common_lib import error
 | |
| from autotest_lib.client.cros import cros_ui, upstart
 | |
| from autotest_lib.client.cros.crash import user_crash_test
 | |
| 
 | |
| 
 | |
| _COLLECTION_ERROR_SIGNATURE = 'crash_reporter-user-collection'
 | |
| _MAX_CRASH_DIRECTORY_SIZE = 32
 | |
| _CRASH_REPORTER_ENABLED_PATH = '/var/lib/crash_reporter/crash-handling-enabled'
 | |
| 
 | |
| 
 | |
| class logging_UserCrash(user_crash_test.UserCrashTest):
 | |
|     """Verifies crash reporting for user processes."""
 | |
|     version = 1
 | |
| 
 | |
| 
 | |
|     def _get_uptime(self):
 | |
|         with open('/proc/uptime', 'r') as f:
 | |
|             uptime_seconds = float(f.readline().split()[0])
 | |
| 
 | |
|         return uptime_seconds
 | |
| 
 | |
| 
 | |
|     # This test has a critical tast counterpart, but the tast version only
 | |
|     # performs one of the two functions that this test does. In particular,
 | |
|     # the tast variant does not verify that crash reporter state is valid before
 | |
|     # any tests run and re-initialize crash reporter.
 | |
|     # TODO(https://crbug.com/1085194): Write a tast test to verify that crash
 | |
|     # reporter's state is good on a "clean" system.
 | |
|     def _test_reporter_startup(self):
 | |
|         """Test that the core_pattern is set up by crash reporter."""
 | |
|         # Turn off crash filtering so we see the original setting.
 | |
|         self.disable_crash_filtering()
 | |
|         output = utils.read_file(self._CORE_PATTERN).rstrip()
 | |
|         expected_core_pattern = ('|%s --user=%%P:%%s:%%u:%%g:%%f' %
 | |
|                                  self._CRASH_REPORTER_PATH)
 | |
|         if output != expected_core_pattern:
 | |
|             raise error.TestFail('core pattern should have been %s, not %s' %
 | |
|                                  (expected_core_pattern, output))
 | |
| 
 | |
|         # Check that we wrote out the file indicating that crash_reporter is
 | |
|         # enabled AFTER the system was booted. This replaces the old technique
 | |
|         # of looking for the log message which was flakey when the logs got
 | |
|         # flooded.
 | |
|         # NOTE: This technique doesn't need to be highly accurate, we are only
 | |
|         # verifying that the flag was written after boot and there are multiple
 | |
|         # seconds between those steps, and a file from a prior boot will almost
 | |
|         # always have been written out much further back in time than our
 | |
|         # current boot time.
 | |
|         if not os.path.isfile(_CRASH_REPORTER_ENABLED_PATH):
 | |
|             raise error.TestFail(
 | |
|                 'crash reporter enabled file flag is not present at %s' %
 | |
|                 _CRASH_REPORTER_ENABLED_PATH)
 | |
|         flag_time = time.time() - os.path.getmtime(_CRASH_REPORTER_ENABLED_PATH)
 | |
|         uptime = self._get_uptime()
 | |
|         if (flag_time > uptime):
 | |
|             raise error.TestFail(
 | |
|                 'user space crash handling was not started during last boot')
 | |
| 
 | |
| 
 | |
|     def _test_chronos_crasher(self):
 | |
|         """Test a user space crash when running as chronos is handled."""
 | |
|         self._check_crashing_process(
 | |
|                 'chronos',
 | |
|                 extra_meta_contents='upload_var_in_progress_integration_test='
 | |
|                 'logging_UserCrash')
 | |
| 
 | |
| 
 | |
|     def _test_chronos_crasher_no_consent(self):
 | |
|         """Test that without consent no files are stored."""
 | |
|         results = self._check_crashing_process('chronos', consent=False)
 | |
| 
 | |
| 
 | |
|     def _test_root_crasher(self):
 | |
|         """Test a user space crash when running as root is handled."""
 | |
|         self._check_crashing_process('root')
 | |
| 
 | |
| 
 | |
|     def _test_root_crasher_no_consent(self):
 | |
|         """Test that without consent no files are stored."""
 | |
|         results = self._check_crashing_process('root', consent=False)
 | |
| 
 | |
| 
 | |
|     def _test_max_enqueued_crashes(self):
 | |
|         """Test that _MAX_CRASH_DIRECTORY_SIZE is enforced."""
 | |
|         self._log_reader.set_start_by_current()
 | |
|         username = 'root'
 | |
| 
 | |
|         crash_dir = self._get_crash_dir(username)
 | |
|         full_message = ('Crash directory %s already full with %d pending '
 | |
|                         'reports' % (crash_dir, _MAX_CRASH_DIRECTORY_SIZE))
 | |
| 
 | |
|         # Fill up the queue.
 | |
|         for i in range(0, _MAX_CRASH_DIRECTORY_SIZE):
 | |
|             result = self._run_crasher_process(username)
 | |
|             if not result['crashed']:
 | |
|                 raise error.TestFail('failure while setting up queue: %d' %
 | |
|                                      result['returncode'])
 | |
|             if self._log_reader.can_find(full_message):
 | |
|                 raise error.TestFail('unexpected full message: ' +
 | |
|                                      full_message)
 | |
| 
 | |
|         crash_dir_size = len(os.listdir(crash_dir))
 | |
|         # For debugging
 | |
|         utils.system('ls -l %s' % crash_dir)
 | |
|         logging.info('Crash directory had %d entries', crash_dir_size)
 | |
| 
 | |
|         # Crash a bunch more times, but make sure no new reports
 | |
|         # are enqueued.
 | |
|         for i in range(0, 10):
 | |
|             self._log_reader.set_start_by_current()
 | |
|             result = self._run_crasher_process(username)
 | |
|             logging.info('New log messages: %s', self._log_reader.get_logs())
 | |
|             if not result['crashed']:
 | |
|                 raise error.TestFail('failure after setting up queue: %d' %
 | |
|                                      result['returncode'])
 | |
|             utils.poll_for_condition(
 | |
|                     lambda: self._log_reader.can_find(full_message),
 | |
|                     timeout=20,
 | |
|                     exception=error.TestFail('expected full message: ' +
 | |
|                                              full_message))
 | |
|             if crash_dir_size != len(os.listdir(crash_dir)):
 | |
|                 utils.system('ls -l %s' % crash_dir)
 | |
|                 raise error.TestFail('expected no new files (now %d were %d)',
 | |
|                                      len(os.listdir(crash_dir)),
 | |
|                                      crash_dir_size)
 | |
| 
 | |
| 
 | |
|     def initialize(self):
 | |
|         user_crash_test.UserCrashTest.initialize(self)
 | |
| 
 | |
|         # If the device has a GUI, return the device to the sign-in screen, as
 | |
|         # some tests will fail inside a user session.
 | |
|         if upstart.has_service('ui'):
 | |
|             cros_ui.restart()
 | |
| 
 | |
| 
 | |
|     # TODO(kmixter): Test crashing a process as ntp or some other
 | |
|     # non-root, non-chronos user.
 | |
| 
 | |
|     def run_once(self):
 | |
|         """ Run all tests once """
 | |
|         self._prepare_crasher()
 | |
|         self._populate_symbols()
 | |
| 
 | |
|         # Run the test once without re-initializing
 | |
|         # to catch problems with the default crash reporting setup
 | |
|         self.run_crash_tests(['reporter_startup'],
 | |
|                               initialize_crash_reporter=False,
 | |
|                               must_run_all=False)
 | |
| 
 | |
|         self.run_crash_tests(['reporter_startup',
 | |
|                               'chronos_crasher',
 | |
|                               'chronos_crasher_no_consent',
 | |
|                               'root_crasher',
 | |
|                               'root_crasher_no_consent',
 | |
|                               'max_enqueued_crashes'],
 | |
|                               initialize_crash_reporter=True)
 |