cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / ppapi / native_client / tests / breakpad_crash_test / crash_dump_tester.py
blobdeb336f08726b32f614695bf5e25781785a79834
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 import os
7 import subprocess
8 import sys
9 import tempfile
10 import time
12 script_dir = os.path.dirname(__file__)
13 sys.path.append(os.path.join(script_dir,
14 '../../tools/browser_tester'))
16 import browser_tester
17 import browsertester.browserlauncher
19 # This script extends browser_tester to check for the presence of
20 # Breakpad crash dumps.
23 # This reads a file of lines containing 'key:value' pairs.
24 # The file contains entries like the following:
25 # plat:Win32
26 # prod:Chromium
27 # ptype:nacl-loader
28 # rept:crash svc
29 def ReadDumpTxtFile(filename):
30 dump_info = {}
31 fh = open(filename, 'r')
32 for line in fh:
33 if ':' in line:
34 key, value = line.rstrip().split(':', 1)
35 dump_info[key] = value
36 fh.close()
37 return dump_info
40 def StartCrashService(browser_path, dumps_dir, windows_pipe_name,
41 cleanup_funcs, crash_service_exe,
42 skip_if_missing=False):
43 # Find crash_service.exe relative to chrome.exe. This is a bit icky.
44 browser_dir = os.path.dirname(browser_path)
45 crash_service_path = os.path.join(browser_dir, crash_service_exe)
46 if skip_if_missing and not os.path.exists(crash_service_path):
47 return
48 proc = subprocess.Popen([crash_service_path,
49 '--v=1', # Verbose output for debugging failures
50 '--dumps-dir=%s' % dumps_dir,
51 '--pipe-name=%s' % windows_pipe_name])
53 def Cleanup():
54 # Note that if the process has already exited, this will raise
55 # an 'Access is denied' WindowsError exception, but
56 # crash_service.exe is not supposed to do this and such
57 # behaviour should make the test fail.
58 proc.terminate()
59 status = proc.wait()
60 sys.stdout.write('crash_dump_tester: %s exited with status %s\n'
61 % (crash_service_exe, status))
63 cleanup_funcs.append(Cleanup)
66 def ListPathsInDir(dir_path):
67 if os.path.exists(dir_path):
68 return [os.path.join(dir_path, name)
69 for name in os.listdir(dir_path)]
70 else:
71 return []
74 def GetDumpFiles(dumps_dirs):
75 all_files = [filename
76 for dumps_dir in dumps_dirs
77 for filename in ListPathsInDir(dumps_dir)]
78 sys.stdout.write('crash_dump_tester: Found %i files\n' % len(all_files))
79 for dump_file in all_files:
80 sys.stdout.write(' %s (size %i)\n'
81 % (dump_file, os.stat(dump_file).st_size))
82 return [dump_file for dump_file in all_files
83 if dump_file.endswith('.dmp')]
86 def Main(cleanup_funcs):
87 parser = browser_tester.BuildArgParser()
88 parser.add_option('--expected_crash_dumps', dest='expected_crash_dumps',
89 type=int, default=0,
90 help='The number of crash dumps that we should expect')
91 parser.add_option('--expected_process_type_for_crash',
92 dest='expected_process_type_for_crash',
93 type=str, default='nacl-loader',
94 help='The type of Chromium process that we expect the '
95 'crash dump to be for')
96 # Ideally we would just query the OS here to find out whether we are
97 # running x86-32 or x86-64 Windows, but Python's win32api module
98 # does not contain a wrapper for GetNativeSystemInfo(), which is
99 # what NaCl uses to check this, or for IsWow64Process(), which is
100 # what Chromium uses. Instead, we just rely on the build system to
101 # tell us.
102 parser.add_option('--win64', dest='win64', action='store_true',
103 help='Pass this if we are running tests for x86-64 Windows')
104 options, args = parser.parse_args()
106 temp_dir = tempfile.mkdtemp(prefix='nacl_crash_dump_tester_')
107 def CleanUpTempDir():
108 browsertester.browserlauncher.RemoveDirectory(temp_dir)
109 cleanup_funcs.append(CleanUpTempDir)
111 # To get a guaranteed unique pipe name, use the base name of the
112 # directory we just created.
113 windows_pipe_name = r'\\.\pipe\%s_crash_service' % os.path.basename(temp_dir)
115 # This environment variable enables Breakpad crash dumping in
116 # non-official builds of Chromium.
117 os.environ['CHROME_HEADLESS'] = '1'
118 if sys.platform == 'win32':
119 dumps_dir = temp_dir
120 # Override the default (global) Windows pipe name that Chromium will
121 # use for out-of-process crash reporting.
122 os.environ['CHROME_BREAKPAD_PIPE_NAME'] = windows_pipe_name
123 # Launch the x86-32 crash service so that we can handle crashes in
124 # the browser process.
125 StartCrashService(options.browser_path, dumps_dir, windows_pipe_name,
126 cleanup_funcs, 'crash_service.exe')
127 if options.win64:
128 # Launch the x86-64 crash service so that we can handle crashes
129 # in the NaCl loader process (nacl64.exe).
130 # Skip if missing, since in win64 builds crash_service.exe is 64-bit
131 # and crash_service64.exe does not exist.
132 StartCrashService(options.browser_path, dumps_dir, windows_pipe_name,
133 cleanup_funcs, 'crash_service64.exe',
134 skip_if_missing=True)
135 # We add a delay because there is probably a race condition:
136 # crash_service.exe might not have finished doing
137 # CreateNamedPipe() before NaCl does a crash dump and tries to
138 # connect to that pipe.
139 # TODO(mseaborn): We could change crash_service.exe to report when
140 # it has successfully created the named pipe.
141 time.sleep(1)
142 elif sys.platform == 'darwin':
143 dumps_dir = temp_dir
144 os.environ['BREAKPAD_DUMP_LOCATION'] = dumps_dir
145 elif sys.platform.startswith('linux'):
146 # The "--user-data-dir" option is not effective for the Breakpad
147 # setup in Linux Chromium, because Breakpad is initialized before
148 # "--user-data-dir" is read. So we set HOME to redirect the crash
149 # dumps to a temporary directory.
150 home_dir = temp_dir
151 os.environ['HOME'] = home_dir
152 options.enable_crash_reporter = True
154 result = browser_tester.Run(options.url, options)
156 # Find crash dump results.
157 if sys.platform.startswith('linux'):
158 # Look in "~/.config/*/Crash Reports". This will find crash
159 # reports under ~/.config/chromium or ~/.config/google-chrome, or
160 # under other subdirectories in case the branding is changed.
161 dumps_dirs = [os.path.join(path, 'Crash Reports')
162 for path in ListPathsInDir(os.path.join(home_dir, '.config'))]
163 else:
164 dumps_dirs = [dumps_dir]
165 dmp_files = GetDumpFiles(dumps_dirs)
167 failed = False
168 msg = ('crash_dump_tester: ERROR: Got %i crash dumps but expected %i\n' %
169 (len(dmp_files), options.expected_crash_dumps))
170 if len(dmp_files) != options.expected_crash_dumps:
171 sys.stdout.write(msg)
172 failed = True
174 for dump_file in dmp_files:
175 # Sanity check: Make sure dumping did not fail after opening the file.
176 msg = 'crash_dump_tester: ERROR: Dump file is empty\n'
177 if os.stat(dump_file).st_size == 0:
178 sys.stdout.write(msg)
179 failed = True
181 # On Windows, the crash dumps should come in pairs of a .dmp and
182 # .txt file.
183 if sys.platform == 'win32':
184 second_file = dump_file[:-4] + '.txt'
185 msg = ('crash_dump_tester: ERROR: File %r is missing a corresponding '
186 '%r file\n' % (dump_file, second_file))
187 if not os.path.exists(second_file):
188 sys.stdout.write(msg)
189 failed = True
190 continue
191 # Check that the crash dump comes from the NaCl process.
192 dump_info = ReadDumpTxtFile(second_file)
193 if 'ptype' in dump_info:
194 msg = ('crash_dump_tester: ERROR: Unexpected ptype value: %r != %r\n'
195 % (dump_info['ptype'], options.expected_process_type_for_crash))
196 if dump_info['ptype'] != options.expected_process_type_for_crash:
197 sys.stdout.write(msg)
198 failed = True
199 else:
200 sys.stdout.write('crash_dump_tester: ERROR: Missing ptype field\n')
201 failed = True
202 # TODO(mseaborn): Ideally we would also check that a backtrace
203 # containing an expected function name can be extracted from the
204 # crash dump.
206 if failed:
207 sys.stdout.write('crash_dump_tester: FAILED\n')
208 result = 1
209 else:
210 sys.stdout.write('crash_dump_tester: PASSED\n')
212 return result
215 def MainWrapper():
216 cleanup_funcs = []
217 try:
218 return Main(cleanup_funcs)
219 finally:
220 for func in cleanup_funcs:
221 func()
224 if __name__ == '__main__':
225 sys.exit(MainWrapper())