Home | History | Annotate | Download | only in crash
      1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import contextlib
      6 import fcntl
      7 import glob
      8 import logging
      9 import os
     10 import re
     11 import shutil
     12 
     13 import common
     14 from autotest_lib.client.bin import test, utils
     15 from autotest_lib.client.common_lib import error
     16 from autotest_lib.client.cros import constants, cros_logging
     17 
     18 
     19 class CrashTest(test.test):
     20     """
     21     This class deals with running crash tests, which are tests which crash a
     22     user-space program (or the whole machine) and generate a core dump. We
     23     want to check that the correct crash dump is available and can be
     24     retrieved.
     25 
     26     Chromium OS has a crash sender which checks for new crash data and sends
     27     it to a server. This crash data is used to track software quality and find
     28     bugs. The system crash sender normally is always running, but can be paused
     29     by creating _PAUSE_FILE. When crash sender sees this, it pauses operation.
     30 
     31     The pid of the system crash sender is stored in _CRASH_SENDER_RUN_PATH so
     32     we can use this to kill the system crash sender for when we want to run
     33     our own.
     34 
     35     For testing purposes we sometimes want to run the crash sender manually.
     36     In this case we can set 'OVERRIDE_PAUSE_SENDING=1' in the environment and
     37     run the crash sender manually (as a child process).
     38 
     39     Also for testing we sometimes want to mock out the crash sender, and just
     40     have it pretend to succeed or fail. The _MOCK_CRASH_SENDING file is used
     41     for this. If it doesn't exist, then the crash sender runs normally. If
     42     it exists but is empty, the crash sender will succeed (but actually do
     43     nothing). If the file contains something, then the crash sender will fail.
     44 
     45     If the user consents to sending crash tests, then the _CONSENT_FILE will
     46     exist in the home directory. This test needs to create this file for the
     47     crash sending to work.
     48 
     49     Crash reports are rate limited to a certain number of reports each 24
     50     hours. If the maximum number has already been sent then reports are held
     51     until later. This is administered by a directory _CRASH_SENDER_RATE_DIR
     52     which contains one temporary file for each time a report is sent.
     53 
     54     The class provides the ability to push a consent file. This disables
     55     consent for this test but allows it to be popped back at later. This
     56     makes nested tests easier. If _automatic_consent_saving is True (the
     57     default) then consent will be pushed at the start and popped at the end.
     58 
     59     Interesting variables:
     60         _log_reader: the log reader used for reading log files
     61         _leave_crash_sending: True to enable crash sending on exit from the
     62             test, False to disable it. (Default True).
     63         _automatic_consent_saving: True to push the consent at the start of
     64             the test and pop it afterwards. (Default True).
     65 
     66     Useful places to look for more information are:
     67 
     68     chromeos/src/platform/crash-reporter/crash_sender
     69         - sender script which crash crash reporter to create reports, then
     70 
     71     chromeos/src/platform/crash-reporter/
     72         - crash reporter program
     73     """
     74 
     75 
     76     _CONSENT_FILE = '/home/chronos/Consent To Send Stats'
     77     _CORE_PATTERN = '/proc/sys/kernel/core_pattern'
     78     _CRASH_REPORTER_PATH = '/sbin/crash_reporter'
     79     _CRASH_SENDER_PATH = '/sbin/crash_sender'
     80     _CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender'
     81     _CRASH_SENDER_RUN_PATH = '/run/crash_sender.pid'
     82     _CRASH_SENDER_LOCK_PATH = '/run/lock/crash_sender'
     83     _CRASH_RUN_STATE_DIR = '/run/crash_reporter'
     84     _CRASH_TEST_IN_PROGRESS = _CRASH_RUN_STATE_DIR + '/crash-test-in-progress'
     85     _MOCK_CRASH_SENDING = _CRASH_RUN_STATE_DIR + '/mock-crash-sending'
     86     _PAUSE_FILE = '/var/lib/crash_sender_paused'
     87     _SYSTEM_CRASH_DIR = '/var/spool/crash'
     88     _FALLBACK_USER_CRASH_DIR = '/home/chronos/crash'
     89     _USER_CRASH_DIRS = '/home/chronos/u-*/crash'
     90     _USER_CRASH_DIR_REGEX = re.compile('/home/chronos/u-([a-f0-9]+)/crash')
     91 
     92     # Use the same file format as crash does normally:
     93     # <basename>.#.#.#.meta
     94     _FAKE_TEST_BASENAME = 'fake.1.2.3'
     95 
     96     def _set_system_sending(self, is_enabled):
     97         """Sets whether or not the system crash_sender is allowed to run.
     98 
     99         This is done by creating or removing _PAUSE_FILE.
    100 
    101         crash_sender may still be allowed to run if _set_child_sending is
    102         called with True and it is run as a child process.
    103 
    104         @param is_enabled: True to enable crash_sender, False to disable it.
    105         """
    106         if is_enabled:
    107             if os.path.exists(self._PAUSE_FILE):
    108                 os.remove(self._PAUSE_FILE)
    109         else:
    110             utils.system('touch ' + self._PAUSE_FILE)
    111 
    112 
    113     def _set_child_sending(self, is_enabled):
    114         """Overrides crash sending enabling for child processes.
    115 
    116         When the system crash sender is disabled this test can manually run
    117         the crash sender as a child process. Normally this would do nothing,
    118         but this function sets up crash_sender to ignore its disabled status
    119         and do its job.
    120 
    121         @param is_enabled: True to enable crash sending for child processes.
    122         """
    123         if is_enabled:
    124             os.environ['OVERRIDE_PAUSE_SENDING'] = "1"
    125         else:
    126             del os.environ['OVERRIDE_PAUSE_SENDING']
    127 
    128 
    129     def _set_force_official(self, is_enabled):
    130         """Sets whether or not reports will upload for unofficial versions.
    131 
    132         Normally, crash reports are only uploaded for official build
    133         versions.  If the override is set, however, they will also be
    134         uploaded for unofficial versions.
    135 
    136         @param is_enabled: True to enable uploading for unofficial versions.
    137         """
    138         if is_enabled:
    139             os.environ['FORCE_OFFICIAL'] = "1"
    140         elif os.environ.get('FORCE_OFFICIAL'):
    141             del os.environ['FORCE_OFFICIAL']
    142 
    143 
    144     def _set_mock_developer_mode(self, is_enabled):
    145         """Sets whether or not we should pretend we booted in developer mode.
    146 
    147         @param is_enabled: True to pretend we are in developer mode.
    148         """
    149         if is_enabled:
    150             os.environ['MOCK_DEVELOPER_MODE'] = "1"
    151         elif os.environ.get('MOCK_DEVELOPER_MODE'):
    152             del os.environ['MOCK_DEVELOPER_MODE']
    153 
    154 
    155     def _reset_rate_limiting(self):
    156         """Reset the count of crash reports sent today.
    157 
    158         This clears the contents of the rate limiting directory which has
    159         the effect of reseting our count of crash reports sent.
    160         """
    161         utils.system('rm -rf ' + self._CRASH_SENDER_RATE_DIR)
    162 
    163 
    164     def _clear_spooled_crashes(self):
    165         """Clears system and user crash directories.
    166 
    167         This will remove all crash reports which are waiting to be sent.
    168         """
    169         utils.system('rm -rf ' + self._SYSTEM_CRASH_DIR)
    170         utils.system('rm -rf %s %s' % (self._USER_CRASH_DIRS,
    171                                        self._FALLBACK_USER_CRASH_DIR))
    172 
    173 
    174     def _kill_running_sender(self):
    175         """Kill the the crash_sender process if running.
    176 
    177         We use the PID file to find the process ID, then kill it with signal 9.
    178         """
    179         if not os.path.exists(self._CRASH_SENDER_RUN_PATH):
    180             return
    181         running_pid = int(utils.read_file(self._CRASH_SENDER_RUN_PATH))
    182         logging.warning('Detected running crash sender (%d), killing',
    183                         running_pid)
    184         utils.system('kill -9 %d' % running_pid)
    185         os.remove(self._CRASH_SENDER_RUN_PATH)
    186 
    187 
    188     def _set_sending_mock(self, mock_enabled, send_success=True):
    189         """Enables / disables mocking of the sending process.
    190 
    191         This uses the _MOCK_CRASH_SENDING file to achieve its aims. See notes
    192         at the top.
    193 
    194         @param mock_enabled: If True, mocking is enabled, else it is disabled.
    195         @param send_success: If mock_enabled this is True for the mocking to
    196                 indicate success, False to indicate failure.
    197         """
    198         if mock_enabled:
    199             if send_success:
    200                 data = ''
    201             else:
    202                 data = '1'
    203             logging.info('Setting sending mock')
    204             utils.open_write_close(self._MOCK_CRASH_SENDING, data)
    205         else:
    206             utils.system('rm -f ' + self._MOCK_CRASH_SENDING)
    207 
    208 
    209     def _set_consent(self, has_consent):
    210         """Sets whether or not we have consent to send crash reports.
    211 
    212         This creates or deletes the _CONSENT_FILE to control whether
    213         crash_sender will consider that it has consent to send crash reports.
    214         It also copies a policy blob with the proper policy setting.
    215 
    216         @param has_consent: True to indicate consent, False otherwise
    217         """
    218         autotest_cros_dir = os.path.join(os.path.dirname(__file__), '..')
    219         if has_consent:
    220             if os.path.isdir(constants.WHITELIST_DIR):
    221                 # Create policy file that enables metrics/consent.
    222                 shutil.copy('%s/mock_metrics_on.policy' % autotest_cros_dir,
    223                             constants.SIGNED_POLICY_FILE)
    224                 shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir,
    225                             constants.OWNER_KEY_FILE)
    226             # Create deprecated consent file.  This is created *after* the
    227             # policy file in order to avoid a race condition where chrome
    228             # might remove the consent file if the policy's not set yet.
    229             # We create it as a temp file first in order to make the creation
    230             # of the consent file, owned by chronos, atomic.
    231             # See crosbug.com/18413.
    232             temp_file = self._CONSENT_FILE + '.tmp';
    233             utils.open_write_close(temp_file, 'test-consent')
    234             utils.system('chown chronos:chronos "%s"' % (temp_file))
    235             shutil.move(temp_file, self._CONSENT_FILE)
    236             logging.info('Created ' + self._CONSENT_FILE)
    237         else:
    238             if os.path.isdir(constants.WHITELIST_DIR):
    239                 # Create policy file that disables metrics/consent.
    240                 shutil.copy('%s/mock_metrics_off.policy' % autotest_cros_dir,
    241                             constants.SIGNED_POLICY_FILE)
    242                 shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir,
    243                             constants.OWNER_KEY_FILE)
    244             # Remove deprecated consent file.
    245             utils.system('rm -f "%s"' % (self._CONSENT_FILE))
    246 
    247 
    248     def _set_crash_test_in_progress(self, in_progress):
    249         if in_progress:
    250             utils.open_write_close(self._CRASH_TEST_IN_PROGRESS, 'in-progress')
    251             logging.info('Created ' + self._CRASH_TEST_IN_PROGRESS)
    252         else:
    253             utils.system('rm -f "%s"' % (self._CRASH_TEST_IN_PROGRESS))
    254 
    255 
    256     def _get_pushed_consent_file_path(self):
    257         """Returns filename of the pushed consent file."""
    258         return os.path.join(self.bindir, 'pushed_consent')
    259 
    260 
    261     def _get_pushed_policy_file_path(self):
    262         """Returns filename of the pushed policy file."""
    263         return os.path.join(self.bindir, 'pushed_policy')
    264 
    265 
    266     def _get_pushed_owner_key_file_path(self):
    267         """Returns filename of the pushed owner.key file."""
    268         return os.path.join(self.bindir, 'pushed_owner_key')
    269 
    270 
    271     def _push_consent(self):
    272         """Push the consent file, thus disabling consent.
    273 
    274         The consent files can be created in the new test if required. Call
    275         _pop_consent() to restore the original state.
    276         """
    277         if os.path.exists(self._CONSENT_FILE):
    278             shutil.move(self._CONSENT_FILE,
    279                         self._get_pushed_consent_file_path())
    280         if os.path.exists(constants.SIGNED_POLICY_FILE):
    281             shutil.move(constants.SIGNED_POLICY_FILE,
    282                         self._get_pushed_policy_file_path())
    283         if os.path.exists(constants.OWNER_KEY_FILE):
    284             shutil.move(constants.OWNER_KEY_FILE,
    285                         self._get_pushed_owner_key_file_path())
    286 
    287 
    288     def _pop_consent(self):
    289         """Pop the consent files, enabling/disabling consent as it was before
    290         we pushed the consent."""
    291         if os.path.exists(self._get_pushed_consent_file_path()):
    292             shutil.move(self._get_pushed_consent_file_path(),
    293                         self._CONSENT_FILE)
    294         else:
    295             utils.system('rm -f "%s"' % self._CONSENT_FILE)
    296         if os.path.exists(self._get_pushed_policy_file_path()):
    297             shutil.move(self._get_pushed_policy_file_path(),
    298                         constants.SIGNED_POLICY_FILE)
    299         else:
    300             utils.system('rm -f "%s"' % constants.SIGNED_POLICY_FILE)
    301         if os.path.exists(self._get_pushed_owner_key_file_path()):
    302             shutil.move(self._get_pushed_owner_key_file_path(),
    303                         constants.OWNER_KEY_FILE)
    304         else:
    305             utils.system('rm -f "%s"' % constants.OWNER_KEY_FILE)
    306 
    307 
    308     def _get_crash_dir(self, username, force_user_crash_dir=False):
    309         """Returns crash directory for process running as the given user.
    310 
    311         @param username: Unix user of the crashing process.
    312         @param force_user_crash_dir: Regardless of |username|, return the crash
    313                                      directory of the current user session, or
    314                                      the fallback directory if no sessions.
    315         """
    316         if username == 'root' and not force_user_crash_dir:
    317             return self._SYSTEM_CRASH_DIR
    318         else:
    319             dirs = glob.glob(self._USER_CRASH_DIRS)
    320             return dirs[0] if dirs else self._FALLBACK_USER_CRASH_DIR
    321 
    322 
    323     def _canonicalize_crash_dir(self, crash_dir):
    324         """Converts /home/chronos crash directory to /home/user counterpart.
    325 
    326         @param crash_dir: A path of the form /home/chronos/u-<hash>/crash.
    327         @returns /home/user/<hash>/crash, or |crash_dir| on form mismatch.
    328         """
    329         match = re.match(self._USER_CRASH_DIR_REGEX, crash_dir)
    330         return ('/home/user/%s/crash' % match.group(1)) if match else crash_dir
    331 
    332 
    333     def _initialize_crash_reporter(self):
    334         """Start up the crash reporter."""
    335         utils.system('%s --init' % self._CRASH_REPORTER_PATH)
    336         # Completely disable crash_reporter from generating crash dumps
    337         # while any tests are running, otherwise a crashy system can make
    338         # these tests flaky.
    339         self.enable_crash_filtering('none')
    340 
    341 
    342     def get_crash_dir_name(self, name):
    343         """Return the full path for |name| inside the system crash directory."""
    344         return os.path.join(self._SYSTEM_CRASH_DIR, name)
    345 
    346 
    347     def write_crash_dir_entry(self, name, contents):
    348         """Writes an empty file to the system crash directory.
    349 
    350         This writes a file to _SYSTEM_CRASH_DIR with the given name. This is
    351         used to insert new crash dump files for testing purposes.
    352 
    353         @param name: Name of file to write.
    354         @param contents: String to write to the file.
    355         """
    356         entry = self.get_crash_dir_name(name)
    357         if not os.path.exists(self._SYSTEM_CRASH_DIR):
    358             os.makedirs(self._SYSTEM_CRASH_DIR)
    359         utils.open_write_close(entry, contents)
    360         return entry
    361 
    362 
    363     def write_fake_meta(self, name, exec_name, payload, log=None,
    364                         complete=True):
    365         """Writes a fake meta entry to the system crash directory.
    366 
    367         @param name: Name of file to write.
    368         @param exec_name: Value for exec_name item.
    369         @param payload: Value for payload item.
    370         @param log: Value for log item.
    371         @param complete: True to close off the record, otherwise leave it
    372                 incomplete.
    373         """
    374         last_line = ''
    375         if complete:
    376             last_line = 'done=1\n'
    377         contents = ('exec_name=%s\n'
    378                     'ver=my_ver\n'
    379                     'payload=%s\n'
    380                     '%s' % (exec_name, payload,
    381                             last_line))
    382         if log:
    383             contents = ('log=%s\n' % log) + contents
    384         return self.write_crash_dir_entry(name, contents)
    385 
    386 
    387     def _prepare_sender_one_crash(self,
    388                                   send_success,
    389                                   reports_enabled,
    390                                   report):
    391         """Create metadata for a fake crash report.
    392 
    393         This enabled mocking of the crash sender, then creates a fake
    394         crash report for testing purposes.
    395 
    396         @param send_success: True to make the crash_sender success, False to
    397                 make it fail.
    398         @param reports_enabled: True to enable consent to that reports will be
    399                 sent.
    400         @param report: Report to use for crash, if None we create one.
    401         """
    402         self._set_sending_mock(mock_enabled=True, send_success=send_success)
    403         self._set_consent(reports_enabled)
    404         if report is None:
    405             # Use the same file format as crash does normally:
    406             # <basename>.#.#.#.meta
    407             payload = self.write_crash_dir_entry(
    408                 '%s.dmp' % self._FAKE_TEST_BASENAME, '')
    409             report = self.write_fake_meta(
    410                 '%s.meta' % self._FAKE_TEST_BASENAME, 'fake', payload)
    411         return report
    412 
    413 
    414     def _parse_sender_output(self, output):
    415         """Parse the log output from the crash_sender script.
    416 
    417         This script can run on the logs from either a mocked or true
    418         crash send.
    419 
    420         @param output: output from the script
    421 
    422         @returns A dictionary with these values:
    423             error_type: an error type, if given
    424             exec_name: name of executable which crashed
    425             image_type: type of image ("dev","force-official",...), if given
    426             boot_mode: current boot mode ("dev",...), if given
    427             meta_path: path to the report metadata file
    428             output: the output from the script, copied
    429             report_kind: kind of report sent (minidump vs kernel)
    430             send_attempt: did the script attempt to send a crash.
    431             send_success: if it attempted, was the crash send successful.
    432             sig: signature of the report, if given.
    433             sleep_time: if it attempted, how long did it sleep before
    434               sending (if mocked, how long would it have slept)
    435         """
    436         sleep_match = re.search('Scheduled to send in (\d+)s', output)
    437         send_attempt = sleep_match is not None
    438         if send_attempt:
    439             sleep_time = int(sleep_match.group(1))
    440         else:
    441             sleep_time = None
    442 
    443         meta_match = re.search('Metadata: (\S+) \((\S+)\)', output)
    444         if meta_match:
    445             meta_path = meta_match.group(1)
    446             report_kind = meta_match.group(2)
    447         else:
    448             meta_path = None
    449             report_kind = None
    450 
    451         payload_match = re.search('Payload: (\S+)', output)
    452         if payload_match:
    453             report_payload = payload_match.group(1)
    454         else:
    455             report_payload = None
    456 
    457         exec_name_match = re.search('Exec name: (\S+)', output)
    458         if exec_name_match:
    459             exec_name = exec_name_match.group(1)
    460         else:
    461             exec_name = None
    462 
    463         sig_match = re.search('sig: (\S+)', output)
    464         if sig_match:
    465             sig = sig_match.group(1)
    466         else:
    467             sig = None
    468 
    469         error_type_match = re.search('Error type: (\S+)', output)
    470         if error_type_match:
    471             error_type = error_type_match.group(1)
    472         else:
    473             error_type = None
    474 
    475         image_type_match = re.search('Image type: (\S+)', output)
    476         if image_type_match:
    477             image_type = image_type_match.group(1)
    478         else:
    479             image_type = None
    480 
    481         boot_mode_match = re.search('Boot mode: (\S+)', output)
    482         if boot_mode_match:
    483             boot_mode = boot_mode_match.group(1)
    484         else:
    485             boot_mode = None
    486 
    487         send_success = 'Mocking successful send' in output
    488         return {'exec_name': exec_name,
    489                 'report_kind': report_kind,
    490                 'meta_path': meta_path,
    491                 'report_payload': report_payload,
    492                 'send_attempt': send_attempt,
    493                 'send_success': send_success,
    494                 'sig': sig,
    495                 'error_type': error_type,
    496                 'image_type': image_type,
    497                 'boot_mode': boot_mode,
    498                 'sleep_time': sleep_time,
    499                 'output': output}
    500 
    501 
    502     def wait_for_sender_completion(self):
    503         """Wait for crash_sender to complete.
    504 
    505         Wait for no crash_sender's last message to be placed in the
    506         system log before continuing and for the process to finish.
    507         Otherwise we might get only part of the output."""
    508         utils.poll_for_condition(
    509             lambda: self._log_reader.can_find('crash_sender done.'),
    510             timeout=60,
    511             exception=error.TestError(
    512               'Timeout waiting for crash_sender to emit done: ' +
    513               self._log_reader.get_logs()))
    514         utils.poll_for_condition(
    515             lambda: utils.system('pgrep crash_sender',
    516                                  ignore_status=True) != 0,
    517             timeout=60,
    518             exception=error.TestError(
    519                 'Timeout waiting for crash_sender to finish: ' +
    520                 self._log_reader.get_logs()))
    521 
    522 
    523     def _call_sender_one_crash(self,
    524                                send_success=True,
    525                                reports_enabled=True,
    526                                report=None,
    527                                should_fail=False):
    528         """Call the crash sender script to mock upload one crash.
    529 
    530         @param send_success: Mock a successful send if true
    531         @param reports_enabled: Has the user consented to sending crash reports.
    532         @param report: report to use for crash, if None we create one.
    533 
    534         @returns a dictionary describing the result with the keys
    535           from _parse_sender_output, as well as:
    536             report_exists: does the minidump still exist after calling
    537               send script
    538             rate_count: how many crashes have been uploaded in the past
    539               24 hours.
    540         """
    541         report = self._prepare_sender_one_crash(send_success,
    542                                                 reports_enabled,
    543                                                 report)
    544         self._log_reader.set_start_by_current()
    545         script_output = ""
    546         try:
    547             script_output = utils.system_output(
    548                 '%s 2>&1' % self._CRASH_SENDER_PATH,
    549                 ignore_status=should_fail)
    550         except error.CmdError as err:
    551             raise error.TestFail('"%s" returned an unexpected non-zero '
    552                                  'value (%s).'
    553                                  % (err.command, err.result_obj.exit_status))
    554 
    555         self.wait_for_sender_completion()
    556         output = self._log_reader.get_logs()
    557         logging.debug('Crash sender message output:\n' + output)
    558 
    559         if script_output != '':
    560             logging.debug('crash_sender stdout/stderr: ' + script_output)
    561 
    562         if os.path.exists(report):
    563             report_exists = True
    564             os.remove(report)
    565         else:
    566             report_exists = False
    567         if os.path.exists(self._CRASH_SENDER_RATE_DIR):
    568             rate_count = len(os.listdir(self._CRASH_SENDER_RATE_DIR))
    569         else:
    570             rate_count = 0
    571 
    572         result = self._parse_sender_output(output)
    573         result['report_exists'] = report_exists
    574         result['rate_count'] = rate_count
    575 
    576         # Show the result for debugging but remove 'output' key
    577         # since it's large and earlier in debug output.
    578         debug_result = dict(result)
    579         del debug_result['output']
    580         logging.debug('Result of send (besides output): %s', debug_result)
    581 
    582         return result
    583 
    584 
    585     def _replace_crash_reporter_filter_in(self, new_parameter):
    586         """Replaces the --filter_in= parameter of the crash reporter.
    587 
    588         The kernel is set up to call the crash reporter with the core dump
    589         as stdin when a process dies. This function adds a filter to the
    590         command line used to call the crash reporter. This is used to ignore
    591         crashes in which we have no interest.
    592 
    593         This removes any --filter_in= parameter and optionally replaces it
    594         with a new one.
    595 
    596         @param new_parameter: This is parameter to add to the command line
    597                 instead of the --filter_in=... that was there.
    598         """
    599         core_pattern = utils.read_file(self._CORE_PATTERN)[:-1]
    600         core_pattern = re.sub('--filter_in=\S*\s*', '',
    601                               core_pattern).rstrip()
    602         if new_parameter:
    603             core_pattern += ' ' + new_parameter
    604         utils.system('echo "%s" > %s' % (core_pattern, self._CORE_PATTERN))
    605 
    606 
    607     def enable_crash_filtering(self, name):
    608         """Add a --filter_in argument to the kernel core dump cmdline.
    609 
    610         @param name: Filter text to use. This is passed as a --filter_in
    611                 argument to the crash reporter.
    612         """
    613         self._replace_crash_reporter_filter_in('--filter_in=' + name)
    614 
    615 
    616     def disable_crash_filtering(self):
    617         """Remove the --filter_in argument from the kernel core dump cmdline.
    618 
    619         Next time the crash reporter is invoked (due to a crash) it will not
    620         receive a --filter_in paramter."""
    621         self._replace_crash_reporter_filter_in('')
    622 
    623 
    624     @contextlib.contextmanager
    625     def hold_crash_lock(self):
    626         """A context manager to hold the crash sender lock."""
    627         with open(self._CRASH_SENDER_LOCK_PATH, 'w+') as f:
    628             fcntl.flock(f.fileno(), fcntl.LOCK_EX)
    629             try:
    630                 yield
    631             finally:
    632                 fcntl.flock(f.fileno(), fcntl.LOCK_UN)
    633 
    634 
    635     def initialize(self):
    636         """Initalize the test."""
    637         test.test.initialize(self)
    638         self._log_reader = cros_logging.make_system_log_reader()
    639         self._leave_crash_sending = True
    640         self._automatic_consent_saving = True
    641         self.enable_crash_filtering('none')
    642         self._set_crash_test_in_progress(True)
    643 
    644 
    645     def cleanup(self):
    646         """Cleanup after the test.
    647 
    648         We reset things back to the way we think they should be. This is
    649         intended to allow the system to continue normal operation.
    650 
    651         Some variables silently change the behavior:
    652             _automatic_consent_saving: if True, we pop the consent file.
    653             _leave_crash_sending: True to enable crash sending, False to
    654                 disable it
    655         """
    656         self._reset_rate_limiting()
    657         self._clear_spooled_crashes()
    658         self._set_system_sending(self._leave_crash_sending)
    659         self._set_sending_mock(mock_enabled=False)
    660         if self._automatic_consent_saving:
    661             self._pop_consent()
    662         self.disable_crash_filtering()
    663         self._set_crash_test_in_progress(False)
    664         test.test.cleanup(self)
    665 
    666 
    667     def run_crash_tests(self,
    668                         test_names,
    669                         initialize_crash_reporter=False,
    670                         clear_spool_first=True,
    671                         must_run_all=True):
    672         """Run crash tests defined in this class.
    673 
    674         @param test_names: Array of test names.
    675         @param initialize_crash_reporter: Should set up crash reporter for every
    676                 run.
    677         @param clear_spool_first: Clear all spooled user/system crashes before
    678                 starting the test.
    679         @param must_run_all: Should make sure every test in this class is
    680                 mentioned in test_names.
    681         """
    682         if self._automatic_consent_saving:
    683             self._push_consent()
    684 
    685         if must_run_all:
    686             # Sanity check test_names is complete
    687             for attr in dir(self):
    688                 if attr.find('_test_') == 0:
    689                     test_name = attr[6:]
    690                     if not test_name in test_names:
    691                         raise error.TestError('Test %s is missing' % test_name)
    692 
    693         for test_name in test_names:
    694             logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20))
    695             if initialize_crash_reporter:
    696                 self._initialize_crash_reporter()
    697             # Disable crash_sender from running, kill off any running ones, but
    698             # set environment so crash_sender may run as a child process.
    699             self._set_system_sending(False)
    700             self._set_child_sending(True)
    701             self._kill_running_sender()
    702             self._reset_rate_limiting()
    703             # Default to not overriding for unofficial versions.
    704             self._set_force_official(False)
    705             # Default to not pretending we're in developer mode.
    706             self._set_mock_developer_mode(False)
    707             if clear_spool_first:
    708                 self._clear_spooled_crashes()
    709 
    710             # Call the test function
    711             getattr(self, '_test_' + test_name)()
    712 
    713         # Clear the intentional crashes, so that the server won't automatically
    714         # report crash as failure.
    715         self._clear_spooled_crashes()
    716