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