Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Unified Diff: client/site_tests/logging_UserCrash/logging_UserCrash.py

Issue 3138009: Split logging_UserCrash out into two other files (Closed) Base URL: ssh://git@chromiumos-git//autotest.git
Patch Set: Clean up imports Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: client/site_tests/logging_UserCrash/logging_UserCrash.py
diff --git a/client/site_tests/logging_UserCrash/logging_UserCrash.py b/client/site_tests/logging_UserCrash/logging_UserCrash.py
index 6055e43ea57003c2da4f16124552ffb527e2587c..cda67d93e10ffc5ae981bf51f80e35aea5aec028 100644
--- a/client/site_tests/logging_UserCrash/logging_UserCrash.py
+++ b/client/site_tests/logging_UserCrash/logging_UserCrash.py
@@ -4,27 +4,14 @@
import grp, logging, os, pwd, re, stat, subprocess
from signal import SIGSEGV
-from autotest_lib.client.bin import site_log_reader, site_utils, test
+from autotest_lib.client.bin import site_crash_test, site_utils, test
from autotest_lib.client.common_lib import error, utils
-_CONSENT_FILE = '/home/chronos/Consent To Send Stats'
-_CRASH_REPORTER_PATH = '/sbin/crash_reporter'
-_CRASH_SENDER_PATH = '/sbin/crash_sender'
-_CRASH_SENDER_CRON_PATH = '/etc/cron.hourly/crash_sender.hourly'
-_CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender'
-_CRASH_SENDER_RUN_PATH = '/var/run/crash_sender.pid'
_CORE_PATTERN = '/proc/sys/kernel/core_pattern'
-_DAILY_RATE_LIMIT = 8
_LEAVE_CORE_PATH = '/etc/leave_core'
-_MIN_UNIQUE_TIMES = 4
-_MOCK_CRASH_SENDING = '/tmp/mock-crash-sending'
-_PAUSE_FILE = '/tmp/pause-crash-sending'
-_SECONDS_SEND_SPREAD = 3600
-_SYSTEM_CRASH_DIR = '/var/spool/crash'
-_USER_CRASH_DIR = '/home/chronos/user/crash'
-class logging_UserCrash(test.test):
+class logging_UserCrash(site_crash_test.CrashTest):
version = 1
@@ -33,213 +20,11 @@ class logging_UserCrash(test.test):
utils.system('make clean all')
- def _set_sending(self, is_enabled):
- if is_enabled:
- if os.path.exists(_PAUSE_FILE):
- os.remove(_PAUSE_FILE)
- else:
- utils.system('touch ' + _PAUSE_FILE)
-
-
- def _reset_rate_limiting(self):
- utils.system('rm -rf ' + _CRASH_SENDER_RATE_DIR)
-
-
- def _clear_spooled_crashes(self):
- utils.system('rm -rf ' + _SYSTEM_CRASH_DIR)
- utils.system('rm -rf ' + _USER_CRASH_DIR)
-
-
- def _kill_running_sender(self):
- if not os.path.exists(_CRASH_SENDER_RUN_PATH):
- return
- running_pid = int(utils.read_file(_CRASH_SENDER_RUN_PATH))
- logging.warning('Detected running crash sender (%d), killing' %
- running_pid)
- utils.system('kill -9 %d' % running_pid)
- os.remove(_CRASH_SENDER_RUN_PATH)
-
-
- def _set_sending_mock(self, mock_enabled, send_success=True):
- if mock_enabled:
- if send_success:
- data = ''
- else:
- data = '1'
- logging.info('Setting sending mock')
- utils.open_write_close(_MOCK_CRASH_SENDING, data)
- else:
- utils.system('rm -f ' + _MOCK_CRASH_SENDING)
-
-
- def _set_consent(self, has_consent):
- if has_consent:
- utils.open_write_close(_CONSENT_FILE, 'test-consent')
- logging.info('Created ' + _CONSENT_FILE)
- else:
- utils.system('rm -f "%s"' % (_CONSENT_FILE))
-
-
- def _get_pushed_consent_file_path(self):
- return os.path.join(self.bindir, 'pushed_consent')
-
-
- def _push_consent(self):
- if os.path.exists(_CONSENT_FILE):
- os.rename(_CONSENT_FILE, self._get_pushed_consent_file_path())
-
-
- def _pop_consent(self):
- self._set_consent(False)
- if os.path.exists(self._get_pushed_consent_file_path()):
- os.rename(self._get_pushed_consent_file_path(), _CONSENT_FILE)
-
-
- def _get_crash_dir(self, username):
- if username == 'chronos':
- return _USER_CRASH_DIR
- else:
- return _SYSTEM_CRASH_DIR
-
-
- def _initialize_crash_reporter(self):
- utils.system('%s --init --nounclean_check' % _CRASH_REPORTER_PATH)
-
-
- def initialize(self):
- test.test.initialize(self)
- self._log_reader = site_log_reader.LogReader()
-
-
- def cleanup(self):
- self._reset_rate_limiting()
- self._clear_spooled_crashes()
- test.test.cleanup(self)
-
-
- def _create_fake_crash_dir_entry(self, name):
- entry = os.path.join(_SYSTEM_CRASH_DIR, name)
- if not os.path.exists(_SYSTEM_CRASH_DIR):
- os.makedirs(_SYSTEM_CRASH_DIR)
- utils.system('touch ' + entry)
- return entry
-
-
- def _prepare_sender_one_crash(self,
- send_success,
- reports_enabled,
- username,
- minidump):
- self._set_sending_mock(mock_enabled=True, send_success=send_success)
- self._set_consent(reports_enabled)
- if minidump is None:
- minidump = self._create_fake_crash_dir_entry('fake.dmp')
- return minidump
-
-
- def _parse_sender_output(self, output):
- """Parse the log output from the crash_sender script.
-
- This script can run on the logs from either a mocked or true
- crash send.
-
- Args:
- output: output from the script
-
- Returns:
- A dictionary with these values:
- send_attempt: did the script attempt to send a crash.
- send_success: if it attempted, was the crash send successful.
- sleep_time: if it attempted, how long did it sleep before
- sending (if mocked, how long would it have slept)
- output: the output from the script, copied
- """
- sleep_match = re.search('Scheduled to send in (\d+)s', output)
- send_attempt = sleep_match is not None
- if send_attempt:
- sleep_time = int(sleep_match.group(1))
- else:
- sleep_time = None
- send_success = 'Mocking successful send' in output
- return {'send_attempt': send_attempt,
- 'send_success': send_success,
- 'sleep_time': sleep_time,
- 'output': output}
-
-
- def _call_sender_one_crash(self,
- send_success=True,
- reports_enabled=True,
- username='root',
- minidump=None):
- """Call the crash sender script to mock upload one crash.
-
- Args:
- send_success: Mock a successful send if true
- reports_enabled: Has the user consented to sending crash reports.
- username: user to emulate a crash from
- minidump: minidump to use for crash, if None we create one.
-
- Returns:
- Returns a dictionary describing the result with the keys
- from _parse_sender_output, as well as:
- minidump_exists: does the minidump still exist after calling
- send script
- rate_count: how many crashes have been uploaded in the past
- 24 hours.
- """
- minidump = self._prepare_sender_one_crash(send_success,
- reports_enabled,
- username,
- minidump)
- self._log_reader.set_start_by_current()
- script_output = utils.system_output(
- '/bin/sh -c "%s" 2>&1' % _CRASH_SENDER_PATH,
- ignore_status=True)
- # Wait for up to 2s for no crash_sender to be running,
- # otherwise me might get only part of the output.
- site_utils.poll_for_condition(
- lambda: utils.system('pgrep crash_sender',
- ignore_status=True) != 0,
- timeout=2,
- exception=error.TestError(
- 'Timeout waiting for crash_sender to finish: ' +
- self._log_reader.get_logs()))
-
- output = self._log_reader.get_logs()
- logging.debug('Crash sender message output:\n' + output)
- if script_output != '':
- raise error.TestFail(
- 'Unexpected crash_sender stdout/stderr: ' + script_output)
-
- if os.path.exists(minidump):
- minidump_exists = True
- os.remove(minidump)
- else:
- minidump_exists = False
- if os.path.exists(_CRASH_SENDER_RATE_DIR):
- rate_count = len(os.listdir(_CRASH_SENDER_RATE_DIR))
- else:
- rate_count = 0
-
- result = self._parse_sender_output(output)
- result['minidump_exists'] = minidump_exists
- result['rate_count'] = rate_count
-
- # Show the result for debugging but remove 'output' key
- # since it's large and earlier in debug output.
- debug_result = dict(result)
- del debug_result['output']
- logging.debug('Result of send (besides output): %s' % debug_result)
-
- return result
-
-
def _test_reporter_startup(self):
"""Test that the core_pattern is set up by crash reporter."""
output = utils.read_file(_CORE_PATTERN).rstrip()
expected_core_pattern = ('|%s --signal=%%s --pid=%%p' %
- _CRASH_REPORTER_PATH)
+ self._CRASH_REPORTER_PATH)
if output != expected_core_pattern:
raise error.TestFail('core pattern should have been %s, not %s' %
(expected_core_pattern, output))
@@ -254,160 +39,13 @@ class logging_UserCrash(test.test):
def _test_reporter_shutdown(self):
"""Test the crash_reporter shutdown code works."""
self._log_reader.set_start_by_current()
- utils.system('%s --clean_shutdown' % _CRASH_REPORTER_PATH)
+ utils.system('%s --clean_shutdown' % self._CRASH_REPORTER_PATH)
output = utils.read_file(_CORE_PATTERN).rstrip()
if output != 'core':
raise error.TestFail('core pattern should have been core, not %s' %
output)
- def _test_sender_simple(self):
- """Test sending a single crash."""
- self._set_sending(True)
- result = self._call_sender_one_crash()
- if (result['minidump_exists'] or
- result['rate_count'] != 1 or
- not result['send_attempt'] or
- not result['send_success'] or
- result['sleep_time'] < 0 or
- result['sleep_time'] >= _SECONDS_SEND_SPREAD):
- raise error.TestFail('Simple send failed')
-
-
- def _test_sender_pausing(self):
- """Test the sender returns immediately when the pause file is present.
-
- This is testing the sender's test functionality - if this regresses,
- other tests can become flaky because the cron-started sender may run
- asynchronously to these tests."""
- self._set_sending(False)
- result = self._call_sender_one_crash()
- if (not result['minidump_exists'] or
- not 'Exiting early due to' in result['output'] or
- result['send_attempt']):
- raise error.TestFail('Sender did not pause')
-
-
- def _test_sender_reports_disabled(self):
- """Test that when reporting is disabled, we don't send."""
- self._set_sending(True)
- result = self._call_sender_one_crash(reports_enabled=False)
- if (result['minidump_exists'] or
- not 'Uploading is disabled' in result['output'] or
- result['send_attempt']):
- raise error.TestFail('Sender did not handle reports disabled')
-
-
- def _test_sender_rate_limiting(self):
- """Test the sender properly rate limits and sends with delay."""
- self._set_sending(True)
- sleep_times = []
- for i in range(1, _DAILY_RATE_LIMIT + 1):
- result = self._call_sender_one_crash()
- if not result['send_attempt'] or not result['send_success']:
- raise error.TestFail('Crash uploader did not send on #%d' % i)
- if result['rate_count'] != i:
- raise error.TestFail('Did not properly persist rate on #%d' % i)
- sleep_times.append(result['sleep_time'])
- logging.debug('Sleeps between sending crashes were: %s' % sleep_times)
- unique_times = {}
- for i in range(0, _DAILY_RATE_LIMIT):
- unique_times[sleep_times[i]] = True
- if len(unique_times) < _MIN_UNIQUE_TIMES:
- raise error.TestFail('Expected at least %d unique times: %s' %
- _MIN_UNIQUE_TIMES, sleep_times)
- # Now the _DAILY_RATE_LIMIT ^ th send request should fail.
- result = self._call_sender_one_crash()
- if (not result['minidump_exists'] or
- not 'Cannot send more crashes' in result['output'] or
- result['rate_count'] != _DAILY_RATE_LIMIT):
- raise error.TestFail('Crash rate limiting did not take effect')
-
- # Set one rate file a day earlier and verify can send
- rate_files = os.listdir(_CRASH_SENDER_RATE_DIR)
- rate_path = os.path.join(_CRASH_SENDER_RATE_DIR, rate_files[0])
- statinfo = os.stat(rate_path)
- os.utime(rate_path, (statinfo.st_atime,
- statinfo.st_mtime - (60 * 60 * 25)))
- utils.system('ls -l ' + _CRASH_SENDER_RATE_DIR)
- result = self._call_sender_one_crash()
- if (not result['send_attempt'] or
- not result['send_success'] or
- result['rate_count'] != _DAILY_RATE_LIMIT):
- raise error.TestFail('Crash not sent even after 25hrs pass')
-
-
- def _test_sender_single_instance(self):
- """Test the sender fails to start when another instance is running.
-
- Here we rely on the sender not checking the other running pid
- is of the same instance.
- """
- self._set_sending(True)
- utils.open_write_close(_CRASH_SENDER_RUN_PATH, str(os.getpid()))
- result = self._call_sender_one_crash()
- if (not 'Already running.' in result['output'] or
- result['send_attempt'] or not result['minidump_exists']):
- raise error.TestFail('Allowed multiple instances to run')
- os.remove(_CRASH_SENDER_RUN_PATH)
-
-
- def _test_sender_send_fails(self):
- """Test that when the send fails we try again later."""
- self._set_sending(True)
- result = self._call_sender_one_crash(send_success=False)
- if not result['send_attempt'] or result['send_success']:
- raise error.TestError('Did not properly cause a send failure')
- if result['rate_count'] != 1:
- raise error.TestFail('Did not count a failed send against rate '
- 'limiting')
- if not result['minidump_exists']:
- raise error.TestFail('Expected minidump to be saved for later '
- 'sending')
-
-
- def _test_sender_leaves_core_files(self):
- """Test that a core file is left in the send directory.
-
- Core files will only persist for developer/testing images. We
- should never remove such a file."""
- self._set_sending(True)
- # Call prepare function to make sure the directory exists.
- core_name = 'something.ending.with.core'
- core_path = self._create_fake_crash_dir_entry(core_name)
- result = self._call_sender_one_crash()
- if not 'Ignoring core file.' in result['output']:
- raise error.TestFail('Expected ignoring core file message')
- if not os.path.exists(core_path):
- raise error.TestFail('Core file was removed')
-
-
- def _test_cron_runs(self):
- """Test sender runs successfully as part of the hourly cron job.
-
- Assuming we've run test_sender_simple which shows that a minidump
- gets removed as part of sending, we run the cron job (which is
- asynchronous) and wait for that file to be removed to just verify
- the job eventually runs the sender."""
- self._set_sending(True)
- minidump = self._prepare_sender_one_crash(send_success=True,
- reports_enabled=True,
- username='root',
- minidump=None)
- if not os.path.exists(minidump):
- raise error.TestError('minidump not created')
- utils.system(_CRASH_SENDER_CRON_PATH)
- self._log_reader.set_start_by_current()
- site_utils.poll_for_condition(
- lambda: not os.path.exists(minidump),
- desc='minidump to be removed')
- crash_sender_log = self._log_reader.get_logs()
- logging.debug('Contents of crash sender log: ' + crash_sender_log)
- result = self._parse_sender_output(crash_sender_log)
- if not result['send_attempt'] or not result['send_success']:
- raise error.TestFail('Cron simple run test failed')
-
-
def _prepare_crasher(self):
"""Extract the crasher and set its permissions.
@@ -762,17 +400,9 @@ class logging_UserCrash(test.test):
# non-root, non-chronos user.
def run_once(self):
- test_names = [
+ self.run_crash_tests([
'reporter_startup',
'reporter_shutdown',
- 'sender_simple',
- 'sender_pausing',
- 'sender_reports_disabled',
- 'sender_rate_limiting',
- 'sender_single_instance',
- 'sender_send_fails',
- 'sender_leaves_core_files',
- 'cron_runs',
'no_crash',
'chronos_breakpad_crasher',
'chronos_nobreakpad_crasher',
@@ -780,28 +410,4 @@ class logging_UserCrash(test.test):
'root_nobreakpad_crasher',
'core_file_removed_in_production',
'core_file_persists_in_debug',
- ]
-
- self._push_consent()
-
- # Sanity check test_names is complete
- for attr in dir(self):
- if attr.find('_test_') == 0:
- test_name = attr[6:]
- if not test_name in test_names:
- raise error.TestError('Test %s is missing' % test_name)
-
- for test_name in test_names:
- logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20))
- self._initialize_crash_reporter()
- self._kill_running_sender()
- self._reset_rate_limiting()
- self._clear_spooled_crashes()
- self._set_sending(False)
- getattr(self, '_test_' + test_name)()
-
-
- def cleanup(self):
- self._set_sending(True)
- self._set_sending_mock(mock_enabled=False)
- self._pop_consent()
+ ])

Powered by Google App Engine
This is Rietveld 408576698