Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 grp, logging, os, pwd, re, stat, subprocess | 5 import grp, logging, os, pwd, re, stat, subprocess |
| 6 from signal import SIGSEGV | 6 from signal import SIGSEGV |
| 7 from autotest_lib.client.bin import site_crash_test, site_utils, test | 7 from autotest_lib.client.bin import site_crash_test, site_utils, test |
| 8 from autotest_lib.client.common_lib import error, utils | 8 from autotest_lib.client.common_lib import error, utils |
| 9 | 9 |
| 10 _COLLECTION_ERROR_SIGNATURE = 'crash_reporter-user-collection' | |
| 11 _CORE2MD_PATH = '/usr/bin/core2md' | |
| 10 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' | 12 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' |
| 11 _LEAVE_CORE_PATH = '/root/.leave_core' | 13 _LEAVE_CORE_PATH = '/root/.leave_core' |
| 12 _MAX_CRASH_DIRECTORY_SIZE = 32 | 14 _MAX_CRASH_DIRECTORY_SIZE = 32 |
| 13 | 15 |
| 14 | 16 |
| 15 class logging_UserCrash(site_crash_test.CrashTest): | 17 class logging_UserCrash(site_crash_test.CrashTest): |
| 16 version = 1 | 18 version = 1 |
| 17 | 19 |
| 18 | 20 |
| 19 def setup(self): | 21 def setup(self): |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 148 crasher_command.append(self._crasher_path) | 150 crasher_command.append(self._crasher_path) |
| 149 basename = os.path.basename(self._crasher_path) | 151 basename = os.path.basename(self._crasher_path) |
| 150 if not cause_crash: | 152 if not cause_crash: |
| 151 crasher_command.append('--nocrash') | 153 crasher_command.append('--nocrash') |
| 152 self._set_consent(consent) | 154 self._set_consent(consent) |
| 153 crasher = subprocess.Popen(crasher_command, | 155 crasher = subprocess.Popen(crasher_command, |
| 154 stdout=subprocess.PIPE, | 156 stdout=subprocess.PIPE, |
| 155 stderr=subprocess.PIPE) | 157 stderr=subprocess.PIPE) |
| 156 output = crasher.communicate()[1] | 158 output = crasher.communicate()[1] |
| 157 logging.debug('Output from %s: %s' % | 159 logging.debug('Output from %s: %s' % |
| 158 (self._crasher_path, output)) | 160 (crasher_command, output)) |
| 159 | 161 |
| 160 # Grab the pid from the process output. We can't just use | 162 # Grab the pid from the process output. We can't just use |
| 161 # crasher.pid unfortunately because that may be the PID of su. | 163 # crasher.pid unfortunately because that may be the PID of su. |
| 162 match = re.search(r'pid=(\d+)', output) | 164 match = re.search(r'pid=(\d+)', output) |
| 163 if not match: | 165 if not match: |
| 164 raise error.TestFail('Could not find pid output from crasher: %s' % | 166 raise error.TestFail('Could not find pid output from crasher: %s' % |
| 165 output) | 167 output) |
| 166 pid = int(match.group(1)) | 168 pid = int(match.group(1)) |
| 167 | 169 |
| 168 if consent: | 170 if consent: |
| 169 handled_string = 'handling' | 171 handled_string = 'handling' |
| 170 else: | 172 else: |
| 171 handled_string = 'ignoring' | 173 handled_string = 'ignoring - no consent' |
| 172 expected_message = ( | 174 expected_message = ( |
| 173 'Received crash notification for %s[%d] sig 11 (%s)' % | 175 'Received crash notification for %s[%d] sig 11 (%s)' % |
| 174 (basename, pid, handled_string)) | 176 (basename, pid, handled_string)) |
| 175 | 177 |
| 176 # Wait until no crash_reporter is running. | 178 # Wait until no crash_reporter is running. |
| 177 site_utils.poll_for_condition( | 179 site_utils.poll_for_condition( |
| 178 lambda: utils.system('pgrep crash_reporter', | 180 lambda: utils.system('pgrep crash_reporter', |
| 179 ignore_status=True) != 0, | 181 ignore_status=True) != 0, |
| 180 timeout=10, | 182 timeout=10, |
| 181 exception=error.TestError( | 183 exception=error.TestError( |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 | 221 |
| 220 | 222 |
| 221 def _check_minidump_stackwalk(self, minidump_path, basename, | 223 def _check_minidump_stackwalk(self, minidump_path, basename, |
| 222 from_crash_reporter): | 224 from_crash_reporter): |
| 223 # Now stackwalk the minidump | 225 # Now stackwalk the minidump |
| 224 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' % | 226 stack = utils.system_output('/usr/bin/minidump_stackwalk %s %s' % |
| 225 (minidump_path, self._symbol_dir)) | 227 (minidump_path, self._symbol_dir)) |
| 226 self._verify_stack(stack, basename, from_crash_reporter) | 228 self._verify_stack(stack, basename, from_crash_reporter) |
| 227 | 229 |
| 228 | 230 |
| 229 def _check_generated_minidump_sending(self, meta_path, minidump_path, | 231 def _check_generated_report_sending(self, meta_path, payload_path, |
| 230 username, crasher_basename, | 232 username, exec_name, report_kind, |
| 231 will_syslog_give_name): | 233 expected_sig=None): |
| 232 # Now check that the sending works | 234 # Now check that the sending works |
| 233 result = self._call_sender_one_crash( | 235 result = self._call_sender_one_crash( |
| 234 username=username, | 236 username=username, |
| 235 report=os.path.basename(minidump_path)) | 237 report=os.path.basename(payload_path)) |
| 236 if (not result['send_attempt'] or not result['send_success'] or | 238 if (not result['send_attempt'] or not result['send_success'] or |
| 237 result['report_exists']): | 239 result['report_exists']): |
| 238 raise error.TestFail('Minidump not sent properly') | 240 raise error.TestFail('Report not sent properly') |
| 239 if will_syslog_give_name: | 241 if result['exec_name'] != exec_name: |
| 240 if result['exec_name'] != crasher_basename: | 242 raise error.TestFail('Executable name incorrect') |
| 241 raise error.TestFail('Executable name incorrect') | 243 if result['report_kind'] != report_kind: |
| 242 if result['report_kind'] != 'minidump': | |
| 243 raise error.TestFail('Expected a minidump report') | 244 raise error.TestFail('Expected a minidump report') |
| 244 if result['report_payload'] != minidump_path: | 245 if result['report_payload'] != payload_path: |
| 245 raise error.TestFail('Sent the wrong minidump payload') | 246 raise error.TestFail('Sent the wrong minidump payload') |
| 246 if result['meta_path'] != meta_path: | 247 if result['meta_path'] != meta_path: |
| 247 raise error.TestFail('Used the wrong meta file') | 248 raise error.TestFail('Used the wrong meta file') |
| 248 if result['sig'] is not None: | 249 if expected_sig is None: |
| 249 raise error.TestFail('User crash should not have signature') | 250 if result['sig'] is not None: |
| 251 raise error.TestFail('Report should not have signature') | |
| 252 else: | |
| 253 if not 'sig' in result or result['sig'] != expected_sig: | |
| 254 raise error.TestFail('Report signature mismatch: %s vs %s' % | |
| 255 (result['sig'], expected_sig)) | |
| 250 | 256 |
| 251 # Check version matches. | 257 # Check version matches. |
| 252 lsb_release = utils.read_file('/etc/lsb-release') | 258 lsb_release = utils.read_file('/etc/lsb-release') |
| 253 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) | 259 version_match = re.search(r'CHROMEOS_RELEASE_VERSION=(.*)', lsb_release) |
| 254 if not ('Version: %s' % version_match.group(1)) in result['output']: | 260 if not ('Version: %s' % version_match.group(1)) in result['output']: |
| 255 raise error.TestFail('Did not find version %s in log output' % | 261 raise error.TestFail('Did not find version %s in log output' % |
| 256 version_match.group(1)) | 262 version_match.group(1)) |
| 257 | 263 |
| 258 | 264 |
| 259 def _check_crashing_process(self, username, consent=True): | 265 def _run_crasher_process_and_analyze(self, username, |
| 266 cause_crash=True, consent=True): | |
| 260 self._log_reader.set_start_by_current() | 267 self._log_reader.set_start_by_current() |
| 261 | 268 |
| 262 result = self._run_crasher_process(username, consent=consent) | 269 result = self._run_crasher_process(username, cause_crash=cause_crash, |
| 270 consent=consent) | |
| 263 | 271 |
| 264 if not result['crashed']: | 272 if not result['crashed'] or not result['crash_reporter_caught']: |
| 265 raise error.TestFail('crasher did not do its job of crashing: %d' % | 273 return result; |
| 266 result['returncode']) | |
| 267 | |
| 268 if not result['crash_reporter_caught']: | |
| 269 logging.debug('Messages that should have included segv: %s' % | |
| 270 self._log_reader.get_logs()) | |
| 271 raise error.TestFail('Did not find segv message') | |
| 272 | 274 |
| 273 crash_dir = self._get_crash_dir(username) | 275 crash_dir = self._get_crash_dir(username) |
| 274 | 276 |
| 275 if not consent: | 277 if not consent: |
| 276 if os.path.exists(crash_dir): | 278 if os.path.exists(crash_dir): |
| 277 raise error.TestFail('Crash directory should not exist') | 279 raise error.TestFail('Crash directory should not exist') |
| 278 return | 280 return result |
| 279 | 281 |
| 280 crash_contents = os.listdir(crash_dir) | 282 crash_contents = os.listdir(crash_dir) |
| 281 basename = os.path.basename(self._crasher_path) | 283 basename = os.path.basename(self._crasher_path) |
| 282 | 284 |
| 283 breakpad_minidump = None | 285 breakpad_minidump = None |
| 284 crash_reporter_minidump = None | 286 crash_reporter_minidump = None |
| 285 crash_reporter_meta = None | 287 crash_reporter_meta = None |
| 288 crash_reporter_log = None | |
| 286 | 289 |
| 287 self._check_crash_directory_permissions(crash_dir) | 290 self._check_crash_directory_permissions(crash_dir) |
| 288 | 291 |
| 289 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) | 292 logging.debug('Contents in %s: %s' % (crash_dir, crash_contents)) |
| 290 | 293 |
| 291 for filename in crash_contents: | 294 for filename in crash_contents: |
| 292 if filename.endswith('.core'): | 295 if filename.endswith('.core'): |
| 293 # Ignore core files. We'll test them later. | 296 # Ignore core files. We'll test them later. |
| 294 pass | 297 pass |
| 295 elif (filename.startswith(basename) and | 298 elif (filename.startswith(basename) and |
| 296 filename.endswith('.dmp')): | 299 filename.endswith('.dmp')): |
| 297 # This appears to be a minidump created by the crash reporter. | 300 # This appears to be a minidump created by the crash reporter. |
| 298 if not crash_reporter_minidump is None: | 301 if not crash_reporter_minidump is None: |
| 299 raise error.TestFail('Crash reporter wrote multiple ' | 302 raise error.TestFail('Crash reporter wrote multiple ' |
| 300 'minidumps') | 303 'minidumps') |
| 301 crash_reporter_minidump = os.path.join(crash_dir, filename) | 304 crash_reporter_minidump = os.path.join(crash_dir, filename) |
| 302 elif (filename.startswith(basename) and | 305 elif (filename.startswith(basename) and |
| 303 filename.endswith('.meta')): | 306 filename.endswith('.meta')): |
| 304 if not crash_reporter_meta is None: | 307 if not crash_reporter_meta is None: |
| 305 raise error.TestFail('Crash reported wrote multiple ' | 308 raise error.TestFail('Crash reporter wrote multiple ' |
| 306 'meta files') | 309 'meta files') |
| 307 crash_reporter_meta = os.path.join(crash_dir, filename) | 310 crash_reporter_meta = os.path.join(crash_dir, filename) |
| 311 elif (filename.startswith(basename) and | |
| 312 filename.endswith('.log')): | |
| 313 if not crash_reporter_log is None: | |
| 314 raise error.TestFail('Crash reporter wrote multiple ' | |
| 315 'log files') | |
| 316 crash_reporter_log = os.path.join(crash_dir, filename) | |
| 308 else: | 317 else: |
| 309 # This appears to be a breakpad created minidump. | 318 # This appears to be a breakpad created minidump. |
| 310 if not breakpad_minidump is None: | 319 if not breakpad_minidump is None: |
| 311 raise error.TestFail('Breakpad wrote multimpe minidumps') | 320 raise error.TestFail('Breakpad wrote multimpe minidumps') |
| 312 breakpad_minidump = os.path.join(crash_dir, filename) | 321 breakpad_minidump = os.path.join(crash_dir, filename) |
| 313 | 322 |
| 314 if breakpad_minidump: | 323 if breakpad_minidump: |
| 315 raise error.TestFail('%s did generate breakpad minidump' % basename) | 324 raise error.TestFail('%s did generate breakpad minidump' % basename) |
| 316 | 325 |
| 317 if not crash_reporter_minidump: | |
| 318 raise error.TestFail('crash reporter did not generate minidump') | |
| 319 | |
| 320 if not crash_reporter_meta: | 326 if not crash_reporter_meta: |
| 321 raise error.TestFail('crash reporter did not generate meta') | 327 raise error.TestFail('crash reporter did not generate meta') |
| 322 | 328 |
| 329 result['minidump'] = crash_reporter_minidump | |
| 330 result['basename'] = basename | |
| 331 result['meta'] = crash_reporter_meta | |
| 332 result['log'] = crash_reporter_log | |
| 333 return result | |
| 334 | |
| 335 | |
| 336 def _check_crashed_and_caught(self, result): | |
| 337 if not result['crashed']: | |
| 338 raise error.TestFail('crasher did not do its job of crashing: %d' % | |
| 339 result['returncode']) | |
| 340 | |
| 341 if not result['crash_reporter_caught']: | |
| 342 logging.debug('Messages that should have included segv: %s' % | |
| 343 self._log_reader.get_logs()) | |
| 344 raise error.TestFail('Did not find segv message') | |
| 345 | |
| 346 | |
| 347 def _check_crashing_process(self, username, consent=True): | |
| 348 result = self._run_crasher_process_and_analyze(username, | |
| 349 consent=consent) | |
| 350 | |
| 351 self._check_crashed_and_caught(result) | |
| 352 | |
| 353 if not consent: | |
| 354 return | |
| 355 | |
| 356 if not result['minidump']: | |
| 357 raise error.TestFail('crash reporter did not generate minidump') | |
| 358 | |
| 323 if not self._log_reader.can_find('Stored minidump to ' + | 359 if not self._log_reader.can_find('Stored minidump to ' + |
| 324 crash_reporter_minidump): | 360 result['minidump']): |
| 325 raise error.TestFail('crash reporter did not announce minidump') | 361 raise error.TestFail('crash reporter did not announce minidump') |
| 326 | 362 |
| 327 if crash_reporter_minidump: | 363 self._check_minidump_stackwalk(result['minidump'], |
| 328 self._check_minidump_stackwalk(crash_reporter_minidump, | 364 result['basename'], |
| 329 basename, | 365 from_crash_reporter=True) |
| 330 from_crash_reporter=True) | 366 self._check_generated_report_sending(result['meta'], |
| 331 will_syslog_give_name = True | 367 result['minidump'], |
| 332 | 368 username, |
| 333 self._check_generated_minidump_sending(crash_reporter_meta, | 369 result['basename'], |
| 334 crash_reporter_minidump, | 370 'minidump') |
| 335 username, | |
| 336 basename, | |
| 337 will_syslog_give_name) | |
| 338 | 371 |
| 339 def _test_no_crash(self): | 372 def _test_no_crash(self): |
| 340 """Test a program linked against libcrash_dumper can exit normally.""" | 373 """Test a program linked against libcrash_dumper can exit normally.""" |
| 341 self._log_reader.set_start_by_current() | 374 self._log_reader.set_start_by_current() |
| 342 result = self._run_crasher_process(username='root', | 375 result = self._run_crasher_process_and_analyze(username='root', |
| 343 cause_crash=False) | 376 cause_crash=False) |
| 344 if (result['crashed'] or | 377 if (result['crashed'] or |
| 345 result['crash_reporter_caught'] or | 378 result['crash_reporter_caught'] or |
| 346 result['returncode'] != 0): | 379 result['returncode'] != 0): |
| 347 raise error.TestFail('Normal exit of program with dumper failed') | 380 raise error.TestFail('Normal exit of program with dumper failed') |
| 348 | 381 |
| 349 | 382 |
| 350 def _test_chronos_crasher(self): | 383 def _test_chronos_crasher(self): |
| 351 """Test a user space crash when running as chronos is handled.""" | 384 """Test a user space crash when running as chronos is handled.""" |
| 352 self._check_crashing_process('chronos') | 385 self._check_crashing_process('chronos') |
| 353 | 386 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 402 if not self._log_reader.can_find(full_message): | 435 if not self._log_reader.can_find(full_message): |
| 403 raise error.TestFail('expected full message: ' + full_message) | 436 raise error.TestFail('expected full message: ' + full_message) |
| 404 | 437 |
| 405 if crash_dir_size != len(os.listdir(crash_dir)): | 438 if crash_dir_size != len(os.listdir(crash_dir)): |
| 406 utils.system('ls -l %s' % crash_dir) | 439 utils.system('ls -l %s' % crash_dir) |
| 407 raise error.TestFail('expected no new files (now %d were %d)', | 440 raise error.TestFail('expected no new files (now %d were %d)', |
| 408 len(os.listdir(crash_dir)), | 441 len(os.listdir(crash_dir)), |
| 409 crash_dir_size) | 442 crash_dir_size) |
| 410 | 443 |
| 411 | 444 |
| 445 def _check_collection_failure(self, test_option, failure_string): | |
| 446 # Add parameter to core_pattern. | |
| 447 old_core_pattern = utils.read_file(_CORE_PATTERN)[:-1] | |
| 448 try: | |
| 449 utils.system('echo "%s %s" > %s' % (old_core_pattern, test_option, | |
|
petkov
2010/10/28 22:30:50
You could use utils.write_one_line here and below.
| |
| 450 _CORE_PATTERN)) | |
| 451 result = self._run_crasher_process_and_analyze('root', | |
| 452 consent=True) | |
| 453 self._check_crashed_and_caught(result) | |
| 454 if not self._log_reader.can_find(failure_string): | |
| 455 raise error.TestFail('Did not find fail string in log %s' % | |
| 456 failure_string) | |
| 457 if result['minidump']: | |
| 458 raise error.TestFail('failed collection resulted in minidump') | |
| 459 if not result['log']: | |
| 460 raise error.TestFail('failed collection had no log') | |
| 461 log_contents = utils.read_file(result['log']) | |
| 462 if not log_contents.startswith(failure_string): | |
| 463 raise error.TestFail('Expected logged error ' | |
| 464 '\"%s\" was \"%s\"' % | |
| 465 (failure_string, log_contents)) | |
| 466 self._check_generated_report_sending(result['meta'], | |
| 467 result['log'], | |
| 468 'root', | |
| 469 result['basename'], | |
| 470 'log', | |
| 471 _COLLECTION_ERROR_SIGNATURE) | |
| 472 finally: | |
| 473 utils.system('echo "%s" > %s' % (old_core_pattern, _CORE_PATTERN)) | |
| 474 | |
| 475 | |
| 476 def _test_core2md_failure(self): | |
| 477 self._check_collection_failure('--core2md_failure_test', | |
| 478 'Problem during %s [result=1]: Usage:' % | |
| 479 _CORE2MD_PATH) | |
| 480 | |
| 481 | |
| 482 def _test_internal_directory_failure(self): | |
| 483 self._check_collection_failure('--directory_failure_test', | |
| 484 'Purposefully failing to create') | |
| 485 | |
| 486 | |
| 412 def _check_core_file_persisting(self, expect_persist): | 487 def _check_core_file_persisting(self, expect_persist): |
| 413 self._log_reader.set_start_by_current() | 488 self._log_reader.set_start_by_current() |
| 414 | 489 |
| 415 result = self._run_crasher_process('root') | 490 result = self._run_crasher_process('root') |
| 416 | 491 |
| 417 if not result['crashed']: | 492 if not result['crashed']: |
| 418 raise error.TestFail('crasher did not crash') | 493 raise error.TestFail('crasher did not crash') |
| 419 | 494 |
| 420 crash_contents = os.listdir(self._get_crash_dir('root')) | 495 crash_contents = os.listdir(self._get_crash_dir('root')) |
| 421 | 496 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 474 | 549 |
| 475 def run_once(self): | 550 def run_once(self): |
| 476 self.run_crash_tests(['reporter_startup', | 551 self.run_crash_tests(['reporter_startup', |
| 477 'reporter_shutdown', | 552 'reporter_shutdown', |
| 478 'no_crash', | 553 'no_crash', |
| 479 'chronos_crasher', | 554 'chronos_crasher', |
| 480 'chronos_crasher_no_consent', | 555 'chronos_crasher_no_consent', |
| 481 'root_crasher', | 556 'root_crasher', |
| 482 'root_crasher_no_consent', | 557 'root_crasher_no_consent', |
| 483 'max_enqueued_crashes', | 558 'max_enqueued_crashes', |
| 559 'core2md_failure', | |
| 560 'internal_directory_failure', | |
| 484 'core_file_persists_in_debug', | 561 'core_file_persists_in_debug', |
| 485 'core_file_removed_in_production'], | 562 'core_file_removed_in_production'], |
| 486 initialize_crash_reporter = True) | 563 initialize_crash_reporter = True) |
| OLD | NEW |