1#!/usr/bin/env python 2# 3# Copyright 2014 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Integration test for breakpad in content shell. 8 9This test checks that content shell and breakpad are correctly hooked up, as 10well as that the tools can symbolize a stack trace.""" 11 12 13import glob 14import optparse 15import os 16import shutil 17import subprocess 18import sys 19import tempfile 20 21 22CONCURRENT_TASKS=4 23 24 25def main(): 26 parser = optparse.OptionParser() 27 parser.add_option('', '--build-dir', default='', 28 help='The build output directory.') 29 parser.add_option('', '--binary', default='', 30 help='The path of the binary to generate symbols for.') 31 parser.add_option('', '--no-symbols', default=False, action='store_true', 32 help='Symbols are not expected to work.') 33 parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store', 34 type='int', help='Number of parallel tasks to run.') 35 parser.add_option('-v', '--verbose', action='store_true', 36 help='Print verbose status output.') 37 38 (options, _) = parser.parse_args() 39 40 if not options.build_dir: 41 print "Required option --build-dir missing." 42 return 1 43 44 if not options.binary: 45 print "Required option --binary missing." 46 return 1 47 48 if not os.access(options.binary, os.X_OK): 49 print "Cannot find %s." % options.binary 50 return 1 51 52 failure = '' 53 54 # Create a temporary directory to store the crash dumps and symbols in. 55 crash_dir = tempfile.mkdtemp() 56 57 try: 58 print "# Generate symbols." 59 breakpad_tools_dir = os.path.join( 60 os.path.dirname(__file__), '..', '..', '..', 61 'components', 'crash', 'tools') 62 generate_symbols = os.path.join( 63 breakpad_tools_dir, 'generate_breakpad_symbols.py') 64 symbols_dir = os.path.join(crash_dir, 'symbols') 65 cmd = [generate_symbols, 66 '--build-dir=%s' % options.build_dir, 67 '--binary=%s' % options.binary, 68 '--symbols-dir=%s' % symbols_dir, 69 '--jobs=%d' % options.jobs] 70 if options.verbose: 71 cmd.append('--verbose') 72 print ' '.join(cmd) 73 failure = 'Failed to run generate_breakpad_symbols.py.' 74 subprocess.check_call(cmd) 75 76 print "# Run content_shell and make it crash." 77 cmd = [options.binary, 78 '--dump-render-tree', 79 'chrome://crash', 80 '--enable-crash-reporter', 81 '--crash-dumps-dir=%s' % crash_dir] 82 if options.verbose: 83 print ' '.join(cmd) 84 failure = 'Failed to run content_shell.' 85 if options.verbose: 86 subprocess.check_call(cmd) 87 else: 88 with open(os.devnull, 'w') as devnull: 89 subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 90 91 print "# Retrieve crash dump." 92 dmp_files = glob.glob(os.path.join(crash_dir, '*.dmp')) 93 failure = 'Expected 1 crash dump, found %d.' % len(dmp_files) 94 if len(dmp_files) != 1: 95 raise Exception(failure) 96 dmp_file = dmp_files[0] 97 minidump = os.path.join(crash_dir, 'minidump') 98 99 dmp_to_minidump = os.path.join(breakpad_tools_dir, 'dmp2minidump.py') 100 cmd = [dmp_to_minidump, dmp_file, minidump] 101 if options.verbose: 102 print ' '.join(cmd) 103 failure = 'Failed to run dmp_to_minidump.' 104 subprocess.check_call(cmd) 105 106 print "# Symbolize crash dump." 107 minidump_stackwalk = os.path.join(options.build_dir, 'minidump_stackwalk') 108 cmd = [minidump_stackwalk, minidump, symbols_dir] 109 if options.verbose: 110 print ' '.join(cmd) 111 failure = 'Failed to run minidump_stackwalk.' 112 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 113 stack = proc.communicate()[0] 114 115 # Check whether the stack contains a CrashIntentionally symbol. 116 found_symbol = 'CrashIntentionally' in stack 117 118 if options.no_symbols: 119 if found_symbol: 120 if options.verbose: 121 print stack 122 failure = 'Found unexpected reference to CrashIntentionally in stack' 123 raise Exception(failure) 124 else: 125 if not found_symbol: 126 if options.verbose: 127 print stack 128 failure = 'Could not find reference to CrashIntentionally in stack.' 129 raise Exception(failure) 130 131 except: 132 print "FAIL: %s" % failure 133 return 1 134 135 else: 136 print "PASS: Breakpad integration test ran successfully." 137 return 0 138 139 finally: 140 try: 141 shutil.rmtree(crash_dir) 142 except: 143 print 'Failed to delete temp directory "%s".' % crash_dir 144 145 146if '__main__' == __name__: 147 sys.exit(main()) 148