OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 # Copyright 2015 The Crashpad Authors. All rights reserved. | 3 # Copyright 2015 The Crashpad Authors. All rights reserved. |
4 # | 4 # |
5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
6 # you may not use this file except in compliance with the License. | 6 # you may not use this file except in compliance with the License. |
7 # You may obtain a copy of the License at | 7 # You may obtain a copy of the License at |
8 # | 8 # |
9 # http://www.apache.org/licenses/LICENSE-2.0 | 9 # http://www.apache.org/licenses/LICENSE-2.0 |
10 # | 10 # |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 'Debugging Tools For Windows (x86)', | 69 'Debugging Tools For Windows (x86)', |
70 'Debugging Tools For Windows',) | 70 'Debugging Tools For Windows',) |
71 for possible_path in possible_paths: | 71 for possible_path in possible_paths: |
72 app_path = os.path.join(possible_path, 'cdb.exe') | 72 app_path = os.path.join(possible_path, 'cdb.exe') |
73 app_path = FindInstalledWindowsApplication(app_path) | 73 app_path = FindInstalledWindowsApplication(app_path) |
74 if app_path: | 74 if app_path: |
75 return app_path | 75 return app_path |
76 return None | 76 return None |
77 | 77 |
78 | 78 |
79 def GetDumpFromCrashyProgram(out_dir, pipe_name): | 79 def GetDumpFromProgram(out_dir, pipe_name, executable_name): |
80 """Initialize a crash database, run crashpad_handler, run crashy_program | 80 """Initialize a crash database, run crashpad_handler, run |executable_name| |
81 connecting to the crash_handler. Returns the minidump generated by | 81 connecting to the crash_handler. Returns the minidump generated by |
82 crash_handler for further testing. | 82 crash_handler for further testing. |
83 """ | 83 """ |
84 test_database = MakeTempDir() | 84 test_database = MakeTempDir() |
85 handler = None | 85 handler = None |
86 | 86 |
87 try: | 87 try: |
88 if subprocess.call( | 88 if subprocess.call( |
89 [os.path.join(out_dir, 'crashpad_database_util.exe'), '--create', | 89 [os.path.join(out_dir, 'crashpad_database_util.exe'), '--create', |
90 '--database=' + test_database]) != 0: | 90 '--database=' + test_database]) != 0: |
91 print 'could not initialize report database' | 91 print 'could not initialize report database' |
92 return None | 92 return None |
93 | 93 |
94 handler = subprocess.Popen([ | 94 handler = subprocess.Popen([ |
95 os.path.join(out_dir, 'crashpad_handler.exe'), | 95 os.path.join(out_dir, 'crashpad_handler.exe'), |
96 '--pipe-name=' + pipe_name, | 96 '--pipe-name=' + pipe_name, |
97 '--database=' + test_database | 97 '--database=' + test_database |
98 ]) | 98 ]) |
99 | 99 |
100 subprocess.call([os.path.join(out_dir, 'crashy_program.exe'), pipe_name]) | 100 subprocess.call([os.path.join(out_dir, executable_name), pipe_name]) |
101 | 101 |
102 out = subprocess.check_output([ | 102 out = subprocess.check_output([ |
103 os.path.join(out_dir, 'crashpad_database_util.exe'), | 103 os.path.join(out_dir, 'crashpad_database_util.exe'), |
104 '--database=' + test_database, | 104 '--database=' + test_database, |
105 '--show-completed-reports', | 105 '--show-completed-reports', |
106 '--show-all-report-info', | 106 '--show-all-report-info', |
107 ]) | 107 ]) |
108 for line in out.splitlines(): | 108 for line in out.splitlines(): |
109 if line.strip().startswith('Path:'): | 109 if line.strip().startswith('Path:'): |
110 return line.partition(':')[2].strip() | 110 return line.partition(':')[2].strip() |
111 finally: | 111 finally: |
112 if handler: | 112 if handler: |
113 handler.kill() | 113 handler.kill() |
114 | 114 |
115 | 115 |
| 116 def GetDumpFromCrashyProgram(out_dir, pipe_name): |
| 117 return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe') |
| 118 |
| 119 |
| 120 def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name): |
| 121 return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe') |
| 122 |
| 123 |
116 class CdbRun(object): | 124 class CdbRun(object): |
117 """Run cdb.exe passing it a cdb command and capturing the output. | 125 """Run cdb.exe passing it a cdb command and capturing the output. |
118 `Check()` searches for regex patterns in sequence allowing verification of | 126 `Check()` searches for regex patterns in sequence allowing verification of |
119 expected output. | 127 expected output. |
120 """ | 128 """ |
121 | 129 |
122 def __init__(self, cdb_path, dump_path, command): | 130 def __init__(self, cdb_path, dump_path, command): |
123 # Run a command line that loads the dump, runs the specified cdb command, | 131 # Run a command line that loads the dump, runs the specified cdb command, |
124 # and then quits, and capturing stdout. | 132 # and then quits, and capturing stdout. |
125 self.out = subprocess.check_output([ | 133 self.out = subprocess.check_output([ |
(...skipping 14 matching lines...) Expand all Loading... |
140 print >>sys.stderr, 'FAILED - %s' % message | 148 print >>sys.stderr, 'FAILED - %s' % message |
141 print >>sys.stderr, '-' * 80 | 149 print >>sys.stderr, '-' * 80 |
142 print >>sys.stderr, 'did not match:\n %s' % pattern | 150 print >>sys.stderr, 'did not match:\n %s' % pattern |
143 print >>sys.stderr, '-' * 80 | 151 print >>sys.stderr, '-' * 80 |
144 print >>sys.stderr, 'remaining output was:\n %s' % self.out | 152 print >>sys.stderr, 'remaining output was:\n %s' % self.out |
145 print >>sys.stderr, '-' * 80 | 153 print >>sys.stderr, '-' * 80 |
146 sys.stderr.flush() | 154 sys.stderr.flush() |
147 sys.exit(1) | 155 sys.exit(1) |
148 | 156 |
149 | 157 |
150 def RunTests(cdb_path, dump_path, pipe_name): | 158 def RunTests(cdb_path, dump_path, destroyed_dump_path, pipe_name): |
151 """Runs various tests in sequence. Runs a new cdb instance on the dump for | 159 """Runs various tests in sequence. Runs a new cdb instance on the dump for |
152 each block of tests to reduce the chances that output from one command is | 160 each block of tests to reduce the chances that output from one command is |
153 confused for output from another. | 161 confused for output from another. |
154 """ | 162 """ |
155 out = CdbRun(cdb_path, dump_path, '.ecxr') | 163 out = CdbRun(cdb_path, dump_path, '.ecxr') |
156 out.Check('This dump file has an exception of interest stored in it', | 164 out.Check('This dump file has an exception of interest stored in it', |
157 'captured exception') | 165 'captured exception') |
158 out.Check( | 166 out.Check( |
159 'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction', | 167 'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction', |
160 'exception at correct location') | 168 'exception at correct location') |
(...skipping 24 matching lines...) Expand all Loading... |
185 r'g_test_critical_section', 'lock was captured') | 193 r'g_test_critical_section', 'lock was captured') |
186 if platform.win32_ver()[0] != '7': | 194 if platform.win32_ver()[0] != '7': |
187 # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7. | 195 # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7. |
188 out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked') | 196 out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked') |
189 | 197 |
190 out = CdbRun(cdb_path, dump_path, '!handle') | 198 out = CdbRun(cdb_path, dump_path, '!handle') |
191 out.Check(r'\d+ Handles', 'captured handles') | 199 out.Check(r'\d+ Handles', 'captured handles') |
192 out.Check(r'Event\s+\d+', 'capture some event handles') | 200 out.Check(r'Event\s+\d+', 'capture some event handles') |
193 out.Check(r'File\s+\d+', 'capture some file handles') | 201 out.Check(r'File\s+\d+', 'capture some file handles') |
194 | 202 |
| 203 out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2') |
| 204 out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') |
| 205 out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE) |
| 206 |
| 207 # Check that there is no stack trace in the self-destroyed process. Confirm |
| 208 # that the top is where we expect it (that's based only on IP), but subsequent |
| 209 # stack entries will not be available. This confirms that we have a mostly |
| 210 # valid dump, but that the stack was omitted. |
| 211 out.Check(r'self_destroying_program!crashpad::`anonymous namespace\'::' |
| 212 r'FreeOwnStackAndBreak.*\nquit:', |
| 213 'at correct location, no additional stack entries') |
| 214 |
195 def main(args): | 215 def main(args): |
196 try: | 216 try: |
197 if len(args) != 1: | 217 if len(args) != 1: |
198 print >>sys.stderr, 'must supply binary dir' | 218 print >>sys.stderr, 'must supply binary dir' |
199 return 1 | 219 return 1 |
200 | 220 |
201 cdb_path = GetCdbPath() | 221 cdb_path = GetCdbPath() |
202 if not cdb_path: | 222 if not cdb_path: |
203 print >>sys.stderr, 'could not find cdb' | 223 print >>sys.stderr, 'could not find cdb' |
204 return 1 | 224 return 1 |
205 | 225 |
206 # Make sure we can download Windows symbols. | 226 # Make sure we can download Windows symbols. |
207 if not os.environ.get('_NT_SYMBOL_PATH'): | 227 if not os.environ.get('_NT_SYMBOL_PATH'): |
208 symbol_dir = MakeTempDir() | 228 symbol_dir = MakeTempDir() |
209 protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http' | 229 protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http' |
210 os.environ['_NT_SYMBOL_PATH'] = ( | 230 os.environ['_NT_SYMBOL_PATH'] = ( |
211 'SRV*' + symbol_dir + '*' + | 231 'SRV*' + symbol_dir + '*' + |
212 protocol + '://msdl.microsoft.com/download/symbols') | 232 protocol + '://msdl.microsoft.com/download/symbols') |
213 | 233 |
214 pipe_name = r'\\.\pipe\end-to-end_%s_%s' % ( | 234 pipe_name = r'\\.\pipe\end-to-end_%s_%s' % ( |
215 os.getpid(), str(random.getrandbits(64))) | 235 os.getpid(), str(random.getrandbits(64))) |
216 | 236 |
217 dump_path = GetDumpFromCrashyProgram(args[0], pipe_name) | 237 crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name) |
218 if not dump_path: | 238 if not crashy_dump_path: |
219 return 1 | 239 return 1 |
220 | 240 |
221 RunTests(cdb_path, dump_path, pipe_name) | 241 destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name) |
| 242 if not destroyed_dump_path: |
| 243 return 1 |
| 244 |
| 245 RunTests(cdb_path, crashy_dump_path, destroyed_dump_path, pipe_name) |
222 | 246 |
223 return 0 | 247 return 0 |
224 finally: | 248 finally: |
225 CleanUpTempDirs() | 249 CleanUpTempDirs() |
226 | 250 |
227 | 251 |
228 if __name__ == '__main__': | 252 if __name__ == '__main__': |
229 sys.exit(main(sys.argv[1:])) | 253 sys.exit(main(sys.argv[1:])) |
OLD | NEW |