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

Side by Side Diff: client/cros/crash_test.py

Issue 6577014: Added some documentation to the CrashTest class. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git@master
Patch Set: Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging, os, re, shutil 5 import logging, os, re, shutil
6 import common 6 import common
7 import cros_logging 7 import cros_logging
8 from autotest_lib.client.bin import test, utils 8 from autotest_lib.client.bin import test, utils
9 from autotest_lib.client.common_lib import error 9 from autotest_lib.client.common_lib import error
10 10
11 11
12 class CrashTest(test.test): 12 class CrashTest(test.test):
13 """
14 This class deals with running crash tests, which are tests which crash the
15 machine and generate a core dump. We want to check that the correct crash
kmixter1 2011/02/25 00:15:15 Not necessarily the whole machine - both kernel or
16 dump is available and can be retrieved.
17
18 Chromium OS has a crash sender which checks for new crash data and sends
19 it to a server. This crash data is used to track software quality and find
20 bugs. The system crash sender normally is always running, but can be paused
21 by creating _PAUSE_FILE. When crash sender sees this, it pauses operation.
22
23 The pid of the system crash sender is stored in _CRASH_SENDER_RUN_PATH so
24 we can use this to kill the system crash sender for when we want to run
25 our own.
26
27 For testing purposes we sometimes want to run the crash sender manually.
28 In this case we can set 'OVERRIDE_PAUSE_SENDING=1' in the environment and
29 run the crash sender manually (as a child process).
30
31 Also for testing we sometimes want to mock out the crash sender, and just
32 have it pretend to succeed or fail. The _MOCK_CRASH_SENDING file is used
33 for this. If it doesn't exist, then the crash sender runs normally. If
34 it exists but is empty, the crash sender will succeed (but actually do
35 nothing). If the file contains something, then the crash sender will fail.
36
37 If the user consents to sending crash tests, then the _CONSENT_FILE will
38 exist in the home directory. This test needs to create this file for the
39 crash sending to work.
40
41 Crash reports are rate limited to a certain number of reports each 24
42 hours. If the maximum number has already been sent then reports are held
43 until later. This is administered by a directory _CRASH_SENDER_RATE_DIR
44 which contains one temporary file for each time a report is sent.
45
46 The class provides the ability to push a consent file. This disables
47 consent for this test but allows it to be popped back at later. This
48 makes nested tests easier. If _automatic_consent_saving is True (the
49 default) then consent will be pushed at the start and popped at the end.
50
51 Interesting variables:
52 _log_reader: the log reader used for reading log files
53 _leave_crash_sending: True to enable crash sending on exit from the
54 test, False to disable it. (Default True).
55 _automatic_consent_saving: True to push the consent at the start of
56 the test and pop it afterwards. (Default True).
57
58 Useful places to look for more information are:
59
60 chromeos/src/platform/crash-reporter/crash_sender
61 - sender script which crash crash reporter to create reports, then
62
63 chromeos/src/platform/crash-reporter/
64 - crash reporter program
65 """
66
13 67
14 _CONSENT_FILE = '/home/chronos/Consent To Send Stats' 68 _CONSENT_FILE = '/home/chronos/Consent To Send Stats'
15 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' 69 _CORE_PATTERN = '/proc/sys/kernel/core_pattern'
16 _CRASH_REPORTER_PATH = '/sbin/crash_reporter' 70 _CRASH_REPORTER_PATH = '/sbin/crash_reporter'
17 _CRASH_SENDER_PATH = '/sbin/crash_sender' 71 _CRASH_SENDER_PATH = '/sbin/crash_sender'
18 _CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender' 72 _CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender'
19 _CRASH_SENDER_RUN_PATH = '/var/run/crash_sender.pid' 73 _CRASH_SENDER_RUN_PATH = '/var/run/crash_sender.pid'
20 _MOCK_CRASH_SENDING = '/tmp/mock-crash-sending' 74 _MOCK_CRASH_SENDING = '/tmp/mock-crash-sending'
21 _PAUSE_FILE = '/var/lib/crash_sender_paused' 75 _PAUSE_FILE = '/var/lib/crash_sender_paused'
22 _SYSTEM_CRASH_DIR = '/var/spool/crash' 76 _SYSTEM_CRASH_DIR = '/var/spool/crash'
23 _USER_CRASH_DIR = '/home/chronos/user/crash' 77 _USER_CRASH_DIR = '/home/chronos/user/crash'
24 78
25 def _set_system_sending(self, is_enabled): 79 def _set_system_sending(self, is_enabled):
26 """Sets whether or not the system crash_sender is allowed to run. 80 """Sets whether or not the system crash_sender is allowed to run.
27 81
82 This is done by creating or removing _PAUSE_FILE.
83
28 crash_sender may still be allowed to run if _set_child_sending is 84 crash_sender may still be allowed to run if _set_child_sending is
29 called with true and it is run as a child process.""" 85 called with True and it is run as a child process.
86
87 Args:
88 is_enabled: True to enable crash_sender, False to disable it.
89 """
30 if is_enabled: 90 if is_enabled:
31 if os.path.exists(self._PAUSE_FILE): 91 if os.path.exists(self._PAUSE_FILE):
32 os.remove(self._PAUSE_FILE) 92 os.remove(self._PAUSE_FILE)
33 else: 93 else:
34 utils.system('touch ' + self._PAUSE_FILE) 94 utils.system('touch ' + self._PAUSE_FILE)
35 95
36 96
37 def _set_child_sending(self, is_enabled): 97 def _set_child_sending(self, is_enabled):
38 """Overrides crash sending enabling for child processes.""" 98 """Overrides crash sending enabling for child processes.
99
100 When the system crash sender is disabled this test can manually run
101 the crash sender as a child process. Normally this would do nothing,
102 but this function sets up crash_sender to ignore its disabled status
103 and do its job.
104
105 Args:
106 is_enabled: True to enable crash sending for child processes.
107 """
39 if is_enabled: 108 if is_enabled:
40 os.environ['OVERRIDE_PAUSE_SENDING'] = "1" 109 os.environ['OVERRIDE_PAUSE_SENDING'] = "1"
41 else: 110 else:
42 del os.environ['OVERRIDE_PAUSE_SENDING'] 111 del os.environ['OVERRIDE_PAUSE_SENDING']
43 112
44 113
45 def _reset_rate_limiting(self): 114 def _reset_rate_limiting(self):
115 """Reset the count of crash reports sent today.
116
117 This clears the contents of the rate limiting directory which has
118 the effect of reseting our count of crash reports sent.
119 """
46 utils.system('rm -rf ' + self._CRASH_SENDER_RATE_DIR) 120 utils.system('rm -rf ' + self._CRASH_SENDER_RATE_DIR)
47 121
48 122
49 def _clear_spooled_crashes(self): 123 def _clear_spooled_crashes(self):
124 """Clears system and user crash directories.
125
126 This will remove all crash reports which are waiting to be sent.
127 """
50 utils.system('rm -rf ' + self._SYSTEM_CRASH_DIR) 128 utils.system('rm -rf ' + self._SYSTEM_CRASH_DIR)
51 utils.system('rm -rf ' + self._USER_CRASH_DIR) 129 utils.system('rm -rf ' + self._USER_CRASH_DIR)
52 130
53 131
54 def _kill_running_sender(self): 132 def _kill_running_sender(self):
133 """Kill the the crash_sender process if running.
134
135 We use the PID file to find the process ID, then kill it with signal 9.
136 """
55 if not os.path.exists(self._CRASH_SENDER_RUN_PATH): 137 if not os.path.exists(self._CRASH_SENDER_RUN_PATH):
56 return 138 return
57 running_pid = int(utils.read_file(self._CRASH_SENDER_RUN_PATH)) 139 running_pid = int(utils.read_file(self._CRASH_SENDER_RUN_PATH))
58 logging.warning('Detected running crash sender (%d), killing' % 140 logging.warning('Detected running crash sender (%d), killing' %
59 running_pid) 141 running_pid)
60 utils.system('kill -9 %d' % running_pid) 142 utils.system('kill -9 %d' % running_pid)
61 os.remove(self._CRASH_SENDER_RUN_PATH) 143 os.remove(self._CRASH_SENDER_RUN_PATH)
62 144
63 145
64 def _set_sending_mock(self, mock_enabled, send_success=True): 146 def _set_sending_mock(self, mock_enabled, send_success=True):
147 """Enables / disables mocking of the sending process.
148
149 This uses the _MOCK_CRASH_SENDING file to achieve its aims. See notes
150 at the top.
151
152 Args:
153 mock_enabled: If True, mocking is enabled, else it is disabled.
154 send_success: If mock_enabled this is True for the mocking to
155 indicate success, False to indicate failure.
156 """
65 if mock_enabled: 157 if mock_enabled:
66 if send_success: 158 if send_success:
67 data = '' 159 data = ''
68 else: 160 else:
69 data = '1' 161 data = '1'
70 logging.info('Setting sending mock') 162 logging.info('Setting sending mock')
71 utils.open_write_close(self._MOCK_CRASH_SENDING, data) 163 utils.open_write_close(self._MOCK_CRASH_SENDING, data)
72 else: 164 else:
73 utils.system('rm -f ' + self._MOCK_CRASH_SENDING) 165 utils.system('rm -f ' + self._MOCK_CRASH_SENDING)
74 166
75 167
76 def _set_consent(self, has_consent): 168 def _set_consent(self, has_consent):
169 """Sets whether or not we have consent to send crash reports.
170
171 This creates or deletes the _CONSENT_FILE to control whether
172 crash_sender will consider that it has consent to send crash reports.
173
174 Args:
175 has_consent: True to indicate consent, False otherwise
176 """
77 if has_consent: 177 if has_consent:
78 utils.open_write_close(self._CONSENT_FILE, 'test-consent') 178 utils.open_write_close(self._CONSENT_FILE, 'test-consent')
79 logging.info('Created ' + self._CONSENT_FILE) 179 logging.info('Created ' + self._CONSENT_FILE)
80 else: 180 else:
81 utils.system('rm -f "%s"' % (self._CONSENT_FILE)) 181 utils.system('rm -f "%s"' % (self._CONSENT_FILE))
82 182
83 183
84 def _get_pushed_consent_file_path(self): 184 def _get_pushed_consent_file_path(self):
185 """Returns filename of the pushed consent file."""
85 return os.path.join(self.bindir, 'pushed_consent') 186 return os.path.join(self.bindir, 'pushed_consent')
86 187
87 188
88 def _push_consent(self): 189 def _push_consent(self):
190 """Push the consent file, thus disabling consent.
191
192 The consent file can be created in the new test if required. Call
193 _pop_consent() to restore the original state.
194 """
89 if os.path.exists(self._CONSENT_FILE): 195 if os.path.exists(self._CONSENT_FILE):
90 shutil.move(self._CONSENT_FILE, 196 shutil.move(self._CONSENT_FILE,
91 self._get_pushed_consent_file_path()) 197 self._get_pushed_consent_file_path())
92 198
93 199
94 def _pop_consent(self): 200 def _pop_consent(self):
201 """Pop the consent file, enabling/disabling consent as it was before
202 we pushed the consent."""
95 self._set_consent(False) 203 self._set_consent(False)
96 if os.path.exists(self._get_pushed_consent_file_path()): 204 if os.path.exists(self._get_pushed_consent_file_path()):
97 shutil.move(self._get_pushed_consent_file_path(), 205 shutil.move(self._get_pushed_consent_file_path(),
98 self._CONSENT_FILE) 206 self._CONSENT_FILE)
99 207
100 208
101 def _get_crash_dir(self, username): 209 def _get_crash_dir(self, username):
210 """Returns full path to the crash directory for a given username
211
212 Args:
213 username: username to use:
214 'chronos': Returns user crash directory.
215 'root': Returns system crash directory.
216 """
102 if username == 'chronos': 217 if username == 'chronos':
103 return self._USER_CRASH_DIR 218 return self._USER_CRASH_DIR
104 else: 219 else:
105 return self._SYSTEM_CRASH_DIR 220 return self._SYSTEM_CRASH_DIR
106 221
107 222
108 def _initialize_crash_reporter(self): 223 def _initialize_crash_reporter(self):
224 """Start up the crash reporter."""
109 utils.system('%s --init --nounclean_check' % self._CRASH_REPORTER_PATH) 225 utils.system('%s --init --nounclean_check' % self._CRASH_REPORTER_PATH)
110 # Completely disable crash_reporter from generating crash dumps 226 # Completely disable crash_reporter from generating crash dumps
111 # while any tests are running, otherwise a crashy system can make 227 # while any tests are running, otherwise a crashy system can make
112 # these tests flaky. 228 # these tests flaky.
113 self.enable_crash_filtering('none') 229 self.enable_crash_filtering('none')
114 230
115 231
116 def write_crash_dir_entry(self, name, contents): 232 def write_crash_dir_entry(self, name, contents):
233 """Writes an empty file to the system crash directory.
234
235 This writes a file to _SYSTEM_CRASH_DIR with the given name. This is
236 used to insert new crash dump files for testing purposes.
237
238 Args:
239 name: Name of file to write.
240 contents: String to write to the file.
241 """
117 entry = os.path.join(self._SYSTEM_CRASH_DIR, name) 242 entry = os.path.join(self._SYSTEM_CRASH_DIR, name)
118 if not os.path.exists(self._SYSTEM_CRASH_DIR): 243 if not os.path.exists(self._SYSTEM_CRASH_DIR):
119 os.makedirs(self._SYSTEM_CRASH_DIR) 244 os.makedirs(self._SYSTEM_CRASH_DIR)
120 utils.open_write_close(entry, contents) 245 utils.open_write_close(entry, contents)
121 return entry 246 return entry
122 247
123 248
124 def write_fake_meta(self, name, exec_name, payload, log=None, 249 def write_fake_meta(self, name, exec_name, payload, log=None,
125 complete=True): 250 complete=True):
251 """Writes a fake meta entry to the system crash directory.
252
253 Args:
254 name: Name of file to write.
255 exec_name: Value for exec_name item.
256 payload: Value for payload item.
257 log: Value for log item.
258 complete: True to close off the record, otherwise leave it
259 incomplete.
260 """
126 last_line = '' 261 last_line = ''
127 if complete: 262 if complete:
128 last_line = 'done=1\n' 263 last_line = 'done=1\n'
129 contents = ('exec_name=%s\n' 264 contents = ('exec_name=%s\n'
130 'ver=my_ver\n' 265 'ver=my_ver\n'
131 'payload=%s\n' 266 'payload=%s\n'
132 '%s' % (exec_name, payload, 267 '%s' % (exec_name, payload,
133 last_line)) 268 last_line))
134 if log: 269 if log:
135 contents = ('log=%s\n' % log) + contents 270 contents = ('log=%s\n' % log) + contents
136 return self.write_crash_dir_entry(name, contents) 271 return self.write_crash_dir_entry(name, contents)
137 272
138 273
139 def _prepare_sender_one_crash(self, 274 def _prepare_sender_one_crash(self,
140 send_success, 275 send_success,
141 reports_enabled, 276 reports_enabled,
142 username, 277 username,
143 report): 278 report):
279 """Create metadata for a fake crash report.
280
281 This enabled mocking of the crash sender, then creates a fake
282 crash report for testing purposes.
283
284 Args:
285 send_success: True to make the crash_sender success, False to make
286 it fail.
287 reports_enabled: True to enable consent to that reports will be
288 sent.
289 **username: not used
kmixter1 2011/02/25 00:15:15 Feel free to remove this.
290 report: Report to use for crash, if None we create one.
291 """
144 self._set_sending_mock(mock_enabled=True, send_success=send_success) 292 self._set_sending_mock(mock_enabled=True, send_success=send_success)
145 self._set_consent(reports_enabled) 293 self._set_consent(reports_enabled)
146 if report is None: 294 if report is None:
147 payload = self.write_crash_dir_entry('fake.dmp', '') 295 payload = self.write_crash_dir_entry('fake.dmp', '')
148 report = self.write_fake_meta('fake.meta', 'fake', payload) 296 report = self.write_fake_meta('fake.meta', 'fake', payload)
149 return report 297 return report
150 298
151 299
152 def _parse_sender_output(self, output): 300 def _parse_sender_output(self, output):
153 """Parse the log output from the crash_sender script. 301 """Parse the log output from the crash_sender script.
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 # Show the result for debugging but remove 'output' key 432 # Show the result for debugging but remove 'output' key
285 # since it's large and earlier in debug output. 433 # since it's large and earlier in debug output.
286 debug_result = dict(result) 434 debug_result = dict(result)
287 del debug_result['output'] 435 del debug_result['output']
288 logging.debug('Result of send (besides output): %s' % debug_result) 436 logging.debug('Result of send (besides output): %s' % debug_result)
289 437
290 return result 438 return result
291 439
292 440
293 def _replace_crash_reporter_filter_in(self, new_parameter): 441 def _replace_crash_reporter_filter_in(self, new_parameter):
442 """Replaces the --filter_in= parameter of the crash reporter.
443
444 The kernel is set up to call the crash reporter with the core dump
445 as stdin when a process dies. This function adds a filter to the
446 command line used to call the crash reporter. This is used to ignore
447 crashes in which we have no interest.
448
449 This removes any --filter_in= parameter and optionally replaces it
450 with a new one.
451
452 Args:
453 new_parameter: This is parameter to add to the command line
454 instead of the --filter_in=... that was there.
455 """
294 core_pattern = utils.read_file(self._CORE_PATTERN)[:-1] 456 core_pattern = utils.read_file(self._CORE_PATTERN)[:-1]
295 core_pattern = re.sub('--filter_in=\S*\s*', '', 457 core_pattern = re.sub('--filter_in=\S*\s*', '',
296 core_pattern).rstrip() 458 core_pattern).rstrip()
297 if new_parameter: 459 if new_parameter:
298 core_pattern += ' ' + new_parameter 460 core_pattern += ' ' + new_parameter
299 utils.system('echo "%s" > %s' % (core_pattern, self._CORE_PATTERN)) 461 utils.system('echo "%s" > %s' % (core_pattern, self._CORE_PATTERN))
300 462
301 463
302 def enable_crash_filtering(self, name): 464 def enable_crash_filtering(self, name):
465 """Add a --filter_in argument to the kernel core dump cmdline.
466
467 Args:
468 name: Filter text to use. This is passed as a --filter_in
469 argument to the crash reporter.
470 """
303 self._replace_crash_reporter_filter_in('--filter_in=' + name) 471 self._replace_crash_reporter_filter_in('--filter_in=' + name)
304 472
305 473
306 def disable_crash_filtering(self): 474 def disable_crash_filtering(self):
475 """Remove the --filter_in argument from the kernel core dump cmdline.
476
477 Next time the crash reporter is invoked (due to a crash) it will not
478 receive a --filter_in paramter."""
307 self._replace_crash_reporter_filter_in('') 479 self._replace_crash_reporter_filter_in('')
308 480
309 481
310 def initialize(self): 482 def initialize(self):
483 """Initalize the test."""
311 test.test.initialize(self) 484 test.test.initialize(self)
312 self._log_reader = cros_logging.LogReader() 485 self._log_reader = cros_logging.LogReader()
313 self._leave_crash_sending = True 486 self._leave_crash_sending = True
314 self._automatic_consent_saving = True 487 self._automatic_consent_saving = True
315 self.enable_crash_filtering('none') 488 self.enable_crash_filtering('none')
316 489
317 490
318 def cleanup(self): 491 def cleanup(self):
492 """Cleanup after the test.
493
494 We reset things back to the way we think they should be. This is
495 intended to allow the system to continue normal operation.
496
497 - Reset rate limiting.
kmixter1 2011/02/25 00:15:15 I'd prefer not to list things at this level since
498 - Clear any spooled crashes.
499 - Turn off mocking of crash sending.
500 - Maybe pop the consent.
501 - disable filtering of crash info.
502
503 Some variables change the behavior:
504 _automatic_consent_saving: if True, we pop the consent file.
505 _leave_crash_sending: True to enable crash sending, False to
506 disable it
507 """
319 self._reset_rate_limiting() 508 self._reset_rate_limiting()
320 self._clear_spooled_crashes() 509 self._clear_spooled_crashes()
321 self._set_system_sending(self._leave_crash_sending) 510 self._set_system_sending(self._leave_crash_sending)
322 self._set_sending_mock(mock_enabled=False) 511 self._set_sending_mock(mock_enabled=False)
323 if self._automatic_consent_saving: 512 if self._automatic_consent_saving:
324 self._pop_consent() 513 self._pop_consent()
325 self.disable_crash_filtering() 514 self.disable_crash_filtering()
326 test.test.cleanup(self) 515 test.test.cleanup(self)
327 516
328 517
329 def run_crash_tests(self, 518 def run_crash_tests(self,
330 test_names, 519 test_names,
331 initialize_crash_reporter=False, 520 initialize_crash_reporter=False,
332 clear_spool_first=True, 521 clear_spool_first=True,
333 must_run_all=True): 522 must_run_all=True):
334 """Run crash tests defined in this class. 523 """Run crash tests defined in this class.
335 524
336 Args: 525 Args:
337 test_names: array of test names 526 test_names: Array of test names.
338 initialize_crash_reporter: should set up crash reporter for every run 527 initialize_crash_reporter: Should set up crash reporter for every
339 must_run_all: should make sure every test in this class is mentioned 528 run.
340 in test_names 529 clear_spool_first: Clear all spooled user/system crashes before
530 starting the test.
531 must_run_all: Should make sure every test in this class is
532 mentioned in test_names.
341 """ 533 """
342 if self._automatic_consent_saving: 534 if self._automatic_consent_saving:
343 self._push_consent() 535 self._push_consent()
344 536
345 if must_run_all: 537 if must_run_all:
346 # Sanity check test_names is complete 538 # Sanity check test_names is complete
347 for attr in dir(self): 539 for attr in dir(self):
348 if attr.find('_test_') == 0: 540 if attr.find('_test_') == 0:
349 test_name = attr[6:] 541 test_name = attr[6:]
350 if not test_name in test_names: 542 if not test_name in test_names:
351 raise error.TestError('Test %s is missing' % test_name) 543 raise error.TestError('Test %s is missing' % test_name)
352 544
353 for test_name in test_names: 545 for test_name in test_names:
354 logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20)) 546 logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20))
355 if initialize_crash_reporter: 547 if initialize_crash_reporter:
356 self._initialize_crash_reporter() 548 self._initialize_crash_reporter()
357 # Disable crash_sender from running, kill off any running ones, but 549 # Disable crash_sender from running, kill off any running ones, but
358 # set environment so crash_sender may run as a child process. 550 # set environment so crash_sender may run as a child process.
359 self._set_system_sending(False) 551 self._set_system_sending(False)
360 self._set_child_sending(True) 552 self._set_child_sending(True)
361 self._kill_running_sender() 553 self._kill_running_sender()
362 self._reset_rate_limiting() 554 self._reset_rate_limiting()
363 if clear_spool_first: 555 if clear_spool_first:
364 self._clear_spooled_crashes() 556 self._clear_spooled_crashes()
557
558 # Call the test function
365 getattr(self, '_test_' + test_name)() 559 getattr(self, '_test_' + test_name)()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698