win: Test some basic ! windbg commands

R=mark@chromium.org
BUG=crashpad:20, crashpad:46, crashpad:52

Review URL: https://codereview.chromium.org/1397833004 .
This commit is contained in:
Scott Graham 2015-10-09 13:39:39 -07:00
parent a2740b23a2
commit bbd00c3a91
2 changed files with 154 additions and 10 deletions

View File

@ -64,7 +64,7 @@ def main(args):
print name print name
print '-' * 80 print '-' * 80
subprocess.check_call( subprocess.check_call(
[sys.executable, os.path.join(crashpad_dir, name), args[0]]) [sys.executable, os.path.join(crashpad_dir, name), out_dir])
return 0 return 0

View File

@ -15,7 +15,26 @@
# limitations under the License. # limitations under the License.
import os import os
import random
import re
import subprocess
import sys import sys
import tempfile
g_temp_dirs = []
def MakeTempDir():
global g_temp_dirs
new_dir = tempfile.mkdtemp()
g_temp_dirs.append(new_dir)
return new_dir
def CleanUpTempDirs():
global g_temp_dirs
for d in g_temp_dirs:
subprocess.call(['rmdir', '/s', '/q', d], shell=True)
def FindInstalledWindowsApplication(app_path): def FindInstalledWindowsApplication(app_path):
@ -35,6 +54,9 @@ def FindInstalledWindowsApplication(app_path):
def GetCdbPath(): def GetCdbPath():
"""Search in some reasonable places to find cdb.exe. Searches x64 before x86
and newer versions before older versions.
"""
possible_paths = ( possible_paths = (
os.path.join('Windows Kits', '10', 'Debuggers', 'x64'), os.path.join('Windows Kits', '10', 'Debuggers', 'x64'),
os.path.join('Windows Kits', '10', 'Debuggers', 'x86'), os.path.join('Windows Kits', '10', 'Debuggers', 'x86'),
@ -44,12 +66,7 @@ def GetCdbPath():
os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'), os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'),
'Debugging Tools For Windows (x64)', 'Debugging Tools For Windows (x64)',
'Debugging Tools For Windows (x86)', 'Debugging Tools For Windows (x86)',
'Debugging Tools For Windows', 'Debugging Tools For Windows',)
os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers',
'x64'),
os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers',
'x86'),
)
for possible_path in possible_paths: for possible_path in possible_paths:
app_path = os.path.join(possible_path, 'cdb.exe') app_path = os.path.join(possible_path, 'cdb.exe')
app_path = FindInstalledWindowsApplication(app_path) app_path = FindInstalledWindowsApplication(app_path)
@ -58,10 +75,137 @@ def GetCdbPath():
return None return None
def GetDumpFromCrashyProgram(out_dir, pipe_name):
"""Initialize a crash database, run crashpad_handler, run crashy_program
connecting to the crash_handler. Returns the minidump generated by
crash_handler for further testing.
"""
test_database = MakeTempDir()
try:
if subprocess.call(
[os.path.join(out_dir, 'crashpad_database_util.exe'), '--create',
'--database=' + test_database]) != 0:
print 'could not initialize report database'
return None
handler = subprocess.Popen([
os.path.join(out_dir, 'crashpad_handler.exe'),
'--pipe-name=' + pipe_name,
'--database=' + test_database
])
subprocess.call([os.path.join(out_dir, 'crashy_program.exe'), pipe_name])
out = subprocess.check_output([
os.path.join(out_dir, 'crashpad_database_util.exe'),
'--database=' + test_database,
'--show-completed-reports',
'--show-all-report-info',
])
for line in out.splitlines():
if line.strip().startswith('Path:'):
return line.partition(':')[2].strip()
finally:
if handler:
handler.kill()
class CdbRun(object):
"""Run cdb.exe passing it a cdb command and capturing the output.
`Check()` searches for regex patterns in sequence allowing verification of
expected output.
"""
def __init__(self, cdb_path, dump_path, command):
# Run a command line that loads the dump, runs the specified cdb command,
# and then quits, and capturing stdout.
self.out = subprocess.check_output([
cdb_path,
'-z', dump_path,
'-c', command + ';q'
])
def Check(self, pattern, message):
match_obj = re.search(pattern, self.out)
if match_obj:
# Matched. Consume up to end of match.
self.out = self.out[match_obj.end(0):]
print 'ok - %s' % message
else:
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'FAILED - %s' % message
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'did not match:\n %s' % pattern
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'remaining output was:\n %s' % self.out
print >>sys.stderr, '-' * 80
sys.exit(1)
def RunTests(cdb_path, dump_path, pipe_name):
"""Runs various tests in sequence. Runs a new cdb instance on the dump for
each block of tests to reduce the chances that output from one command is
confused for output from another.
"""
out = CdbRun(cdb_path, dump_path, '.ecxr')
out.Check('This dump file has an exception of interest stored in it',
'captured exception')
out.Check(
'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction',
'exception at correct location')
out = CdbRun(cdb_path, dump_path, '!peb')
out.Check(r'PEB at', 'found the PEB')
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
out.Check(r'Base TimeStamp Module', 'module list present')
pipe_name_escaped = pipe_name.replace('\\', '\\\\')
out.Check(r'CommandLine: *\'.*crashy_program.exe *' + pipe_name_escaped,
'some PEB data is correct')
out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured')
out = CdbRun(cdb_path, dump_path, '!teb')
out.Check(r'TEB at', 'found the TEB')
out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data')
out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue')
out = CdbRun(cdb_path, dump_path, '!gle')
out.Check('LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the '
'file specified.', '!gle gets last error')
out.Check('LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The '
'file %hs does not exist.', '!gle gets last ntstatus')
# Locks.
out = CdbRun(cdb_path, dump_path, '!locks')
out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
r'g_test_critical_section', 'lock was captured')
out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
def main(args): def main(args):
cdb_path = GetCdbPath() try:
print 'cdb_path:', cdb_path if len(args) != 1:
return 0 print >>sys.stderr, 'must supply out dir'
return 1
cdb_path = GetCdbPath()
if not cdb_path:
print >>sys.stderr, 'could not find cdb'
return 1
pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
os.getpid(), str(random.getrandbits(64)))
dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
if not dump_path:
return 1
RunTests(cdb_path, dump_path, pipe_name)
return 0
finally:
CleanUpTempDirs()
if __name__ == '__main__': if __name__ == '__main__':