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

Side by Side Diff: ppapi/native_client/tests/breakpad_crash_test/crash_dump_tester.py

Issue 11818007: Breakpad: Add a test for crashes in the browser process (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review changes Created 7 years, 11 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
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import os 6 import os
7 import subprocess 7 import subprocess
8 import sys 8 import sys
9 import tempfile 9 import tempfile
10 import time 10 import time
(...skipping 19 matching lines...) Expand all
30 dump_info = {} 30 dump_info = {}
31 fh = open(filename, 'r') 31 fh = open(filename, 'r')
32 for line in fh: 32 for line in fh:
33 if ':' in line: 33 if ':' in line:
34 key, value = line.rstrip().split(':', 1) 34 key, value = line.rstrip().split(':', 1)
35 dump_info[key] = value 35 dump_info[key] = value
36 fh.close() 36 fh.close()
37 return dump_info 37 return dump_info
38 38
39 39
40 def StartCrashService(browser_path, dumps_dir, windows_pipe_name, win64): 40 def StartCrashService(browser_path, dumps_dir, windows_pipe_name,
41 if sys.platform == 'win32': 41 cleanup_funcs, crash_service_exe):
42 # Find crash_service.exe relative to chrome.exe. This is a bit icky. 42 # Find crash_service.exe relative to chrome.exe. This is a bit icky.
43 browser_dir = os.path.dirname(browser_path) 43 browser_dir = os.path.dirname(browser_path)
44 # Ideally we would just query the OS here to find out whether we 44 proc = subprocess.Popen([os.path.join(browser_dir, crash_service_exe),
45 # are running x86-32 or x86-64 Windows, but Python's win32api 45 '--dumps-dir=%s' % dumps_dir,
46 # module does not contain a wrapper for GetNativeSystemInfo(), 46 '--pipe-name=%s' % windows_pipe_name])
47 # which is what NaCl uses to check this, or for IsWow64Process(), 47
48 # which is what Chromium uses. Instead, we just rely on the build 48 def Cleanup():
49 # system to tell us. Furthermore, on an x86-64 Windows system we 49 # Note that if the process has already exited, this will raise
50 # could launch both versions of crash_service, in order to check 50 # an 'Access is denied' WindowsError exception, but
51 # that they do not interfere, but for simplicity we do not. 51 # crash_service.exe is not supposed to do this and such
52 if win64: 52 # behaviour should make the test fail.
53 executable_name = 'crash_service64.exe' 53 proc.terminate()
54 else: 54 status = proc.wait()
55 executable_name = 'crash_service.exe' 55 sys.stdout.write('crash_dump_tester: %s exited with status %s\n'
56 proc = subprocess.Popen([os.path.join(browser_dir, executable_name), 56 % (crash_service_exe, status))
57 '--dumps-dir=%s' % dumps_dir, 57
58 '--pipe-name=%s' % windows_pipe_name]) 58 cleanup_funcs.append(Cleanup)
59 def Cleanup():
60 # Note that if the process has already exited, this will raise
61 # an 'Access is denied' WindowsError exception, but
62 # crash_service.exe is not supposed to do this and such
63 # behaviour should make the test fail.
64 proc.terminate()
65 status = proc.wait()
66 sys.stdout.write('crash_dump_tester: '
67 'crash_service.exe exited with status %s\n' % status)
68 # We add a delay because there is probably a race condition:
69 # crash_service.exe might not have finished doing
70 # CreateNamedPipe() before NaCl does a crash dump and tries to
71 # connect to that pipe.
72 # TODO(mseaborn): We could change crash_service.exe to report when
73 # it has successfully created the named pipe.
74 time.sleep(1)
75 else:
76 def Cleanup():
77 pass
78 return Cleanup
79 59
80 60
81 def GetDumpFiles(dumps_dir): 61 def GetDumpFiles(dumps_dir):
82 all_files = [os.path.join(dumps_dir, dump_file) 62 all_files = [os.path.join(dumps_dir, dump_file)
83 for dump_file in os.listdir(dumps_dir)] 63 for dump_file in os.listdir(dumps_dir)]
84 sys.stdout.write('crash_dump_tester: Found %i files\n' % len(all_files)) 64 sys.stdout.write('crash_dump_tester: Found %i files\n' % len(all_files))
85 for dump_file in all_files: 65 for dump_file in all_files:
86 sys.stdout.write(' %s\n' % dump_file) 66 sys.stdout.write(' %s\n' % dump_file)
87 return [dump_file for dump_file in all_files 67 return [dump_file for dump_file in all_files
88 if dump_file.endswith('.dmp')] 68 if dump_file.endswith('.dmp')]
89 69
90 70
91 def Main(): 71 def Main(cleanup_funcs):
92 parser = browser_tester.BuildArgParser() 72 parser = browser_tester.BuildArgParser()
93 parser.add_option('--expected_crash_dumps', dest='expected_crash_dumps', 73 parser.add_option('--expected_crash_dumps', dest='expected_crash_dumps',
94 type=int, default=0, 74 type=int, default=0,
95 help='The number of crash dumps that we should expect') 75 help='The number of crash dumps that we should expect')
76 parser.add_option('--expected_process_type_for_crash',
77 dest='expected_process_type_for_crash',
78 type=str, default='nacl-loader',
79 help='The type of Chromium process that we expect the '
80 'crash dump to be for')
81 # Ideally we would just query the OS here to find out whether we are
82 # running x86-32 or x86-64 Windows, but Python's win32api module
83 # does not contain a wrapper for GetNativeSystemInfo(), which is
84 # what NaCl uses to check this, or for IsWow64Process(), which is
85 # what Chromium uses. Instead, we just rely on the build system to
86 # tell us.
96 parser.add_option('--win64', dest='win64', action='store_true', 87 parser.add_option('--win64', dest='win64', action='store_true',
97 help='Pass this if we are running tests for x86-64 Windows') 88 help='Pass this if we are running tests for x86-64 Windows')
98 options, args = parser.parse_args() 89 options, args = parser.parse_args()
99 90
100 dumps_dir = tempfile.mkdtemp(prefix='nacl_crash_dump_tester_') 91 dumps_dir = tempfile.mkdtemp(prefix='nacl_crash_dump_tester_')
92 def CleanUpDumpsDir():
93 browsertester.browserlauncher.RemoveDirectory(dumps_dir)
94 cleanup_funcs.append(CleanUpDumpsDir)
95
101 # To get a guaranteed unique pipe name, use the base name of the 96 # To get a guaranteed unique pipe name, use the base name of the
102 # directory we just created. 97 # directory we just created.
103 windows_pipe_name = r'\\.\pipe\%s_crash_service' % os.path.basename(dumps_dir) 98 windows_pipe_name = r'\\.\pipe\%s_crash_service' % os.path.basename(dumps_dir)
104 99
105 # This environment variable enables Breakpad crash dumping in 100 # This environment variable enables Breakpad crash dumping in
106 # non-official builds of Chromium. 101 # non-official builds of Chromium.
107 os.environ['CHROME_HEADLESS'] = '1' 102 os.environ['CHROME_HEADLESS'] = '1'
108 if sys.platform == 'win32': 103 if sys.platform == 'win32':
109 # Override the default (global) Windows pipe name that Chromium will 104 # Override the default (global) Windows pipe name that Chromium will
110 # use for out-of-process crash reporting. 105 # use for out-of-process crash reporting.
111 os.environ['CHROME_BREAKPAD_PIPE_NAME'] = windows_pipe_name 106 os.environ['CHROME_BREAKPAD_PIPE_NAME'] = windows_pipe_name
107 # Launch the x86-32 crash service so that we can handle crashes in
108 # the browser process.
109 StartCrashService(options.browser_path, dumps_dir, windows_pipe_name,
110 cleanup_funcs, 'crash_service.exe')
111 if options.win64:
112 # Launch the x86-64 crash service so that we can handle crashes
113 # in the NaCl loader process (nacl64.exe).
114 StartCrashService(options.browser_path, dumps_dir, windows_pipe_name,
115 cleanup_funcs, 'crash_service64.exe')
116 # We add a delay because there is probably a race condition:
117 # crash_service.exe might not have finished doing
118 # CreateNamedPipe() before NaCl does a crash dump and tries to
119 # connect to that pipe.
120 # TODO(mseaborn): We could change crash_service.exe to report when
121 # it has successfully created the named pipe.
122 time.sleep(1)
112 elif sys.platform == 'darwin': 123 elif sys.platform == 'darwin':
113 os.environ['BREAKPAD_DUMP_LOCATION'] = dumps_dir 124 os.environ['BREAKPAD_DUMP_LOCATION'] = dumps_dir
114 125
115 cleanup_func = StartCrashService(options.browser_path, dumps_dir, 126 result = browser_tester.Run(options.url, options)
116 windows_pipe_name, options.win64)
117 try:
118 result = browser_tester.Run(options.url, options)
119 finally:
120 cleanup_func()
121 127
122 dmp_files = GetDumpFiles(dumps_dir) 128 dmp_files = GetDumpFiles(dumps_dir)
123 failed = False 129 failed = False
124 msg = ('crash_dump_tester: ERROR: Got %i crash dumps but expected %i\n' % 130 msg = ('crash_dump_tester: ERROR: Got %i crash dumps but expected %i\n' %
125 (len(dmp_files), options.expected_crash_dumps)) 131 (len(dmp_files), options.expected_crash_dumps))
126 if len(dmp_files) != options.expected_crash_dumps: 132 if len(dmp_files) != options.expected_crash_dumps:
127 sys.stdout.write(msg) 133 sys.stdout.write(msg)
128 failed = True 134 failed = True
129 # On Windows, the crash dumps should come in pairs of a .dmp and 135 # On Windows, the crash dumps should come in pairs of a .dmp and
130 # .txt file. 136 # .txt file.
131 if sys.platform == 'win32': 137 if sys.platform == 'win32':
132 for dump_file in dmp_files: 138 for dump_file in dmp_files:
133 second_file = dump_file[:-4] + '.txt' 139 second_file = dump_file[:-4] + '.txt'
134 msg = ('crash_dump_tester: ERROR: File %r is missing a corresponding ' 140 msg = ('crash_dump_tester: ERROR: File %r is missing a corresponding '
135 '%r file\n' % (dump_file, second_file)) 141 '%r file\n' % (dump_file, second_file))
136 if not os.path.exists(second_file): 142 if not os.path.exists(second_file):
137 sys.stdout.write(msg) 143 sys.stdout.write(msg)
138 failed = True 144 failed = True
139 continue 145 continue
140 # Check that the crash dump comes from the NaCl process. 146 # Check that the crash dump comes from the NaCl process.
141 dump_info = ReadDumpTxtFile(second_file) 147 dump_info = ReadDumpTxtFile(second_file)
142 if 'ptype' in dump_info: 148 if 'ptype' in dump_info:
143 msg = ('crash_dump_tester: ERROR: Unexpected ptype value: %r\n' 149 msg = ('crash_dump_tester: ERROR: Unexpected ptype value: %r != %r\n'
144 % dump_info['ptype']) 150 % (dump_info['ptype'], options.expected_process_type_for_crash))
145 if dump_info['ptype'] != 'nacl-loader': 151 if dump_info['ptype'] != options.expected_process_type_for_crash:
146 sys.stdout.write(msg) 152 sys.stdout.write(msg)
147 failed = True 153 failed = True
148 else: 154 else:
149 sys.stdout.write('crash_dump_tester: ERROR: Missing ptype field\n') 155 sys.stdout.write('crash_dump_tester: ERROR: Missing ptype field\n')
150 failed = True 156 failed = True
151 # TODO(mseaborn): Ideally we would also check that a backtrace 157 # TODO(mseaborn): Ideally we would also check that a backtrace
152 # containing an expected function name can be extracted from the 158 # containing an expected function name can be extracted from the
153 # crash dump. 159 # crash dump.
154 160
155 if failed: 161 if failed:
156 sys.stdout.write('crash_dump_tester: FAILED\n') 162 sys.stdout.write('crash_dump_tester: FAILED\n')
157 result = 1 163 result = 1
158 else: 164 else:
159 sys.stdout.write('crash_dump_tester: PASSED\n') 165 sys.stdout.write('crash_dump_tester: PASSED\n')
160 166
161 browsertester.browserlauncher.RemoveDirectory(dumps_dir)
162 return result 167 return result
163 168
164 169
170 def MainWrapper():
171 cleanup_funcs = []
172 try:
173 return Main(cleanup_funcs)
174 finally:
175 for func in cleanup_funcs:
176 func()
177
178
165 if __name__ == '__main__': 179 if __name__ == '__main__':
166 sys.exit(Main()) 180 sys.exit(MainWrapper())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698