| 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 |