• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import contextlib
6import fcntl
7import glob
8import logging
9import os
10import re
11import shutil
12
13import common
14from autotest_lib.client.bin import test, utils
15from autotest_lib.client.common_lib import error
16from autotest_lib.client.cros import constants, cros_logging
17
18
19class 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