1#!/usr/bin/env python
2#
3# Copyright 2014 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Integration test for breakpad in content shell.
8
9This test checks that content shell and breakpad are correctly hooked up, as
10well as that the tools can symbolize a stack trace."""
11
12
13import glob
14import optparse
15import os
16import shutil
17import subprocess
18import sys
19import tempfile
20
21
22CONCURRENT_TASKS=4
23
24
25def main():
26  parser = optparse.OptionParser()
27  parser.add_option('', '--build-dir', default='',
28                    help='The build output directory.')
29  parser.add_option('', '--binary', default='',
30                    help='The path of the binary to generate symbols for.')
31  parser.add_option('', '--no-symbols', default=False, action='store_true',
32                    help='Symbols are not expected to work.')
33  parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
34                    type='int', help='Number of parallel tasks to run.')
35  parser.add_option('-v', '--verbose', action='store_true',
36                    help='Print verbose status output.')
37
38  (options, _) = parser.parse_args()
39
40  if not options.build_dir:
41    print "Required option --build-dir missing."
42    return 1
43
44  if not options.binary:
45    print "Required option --binary missing."
46    return 1
47
48  if not os.access(options.binary, os.X_OK):
49    print "Cannot find %s." % options.binary
50    return 1
51
52  failure = ''
53
54  # Create a temporary directory to store the crash dumps and symbols in.
55  crash_dir = tempfile.mkdtemp()
56
57  try:
58    print "# Generate symbols."
59    breakpad_tools_dir = os.path.join(
60        os.path.dirname(__file__), '..', '..', '..',
61        'components', 'crash', 'tools')
62    generate_symbols = os.path.join(
63        breakpad_tools_dir, 'generate_breakpad_symbols.py')
64    symbols_dir = os.path.join(crash_dir, 'symbols')
65    cmd = [generate_symbols,
66           '--build-dir=%s' % options.build_dir,
67           '--binary=%s' % options.binary,
68           '--symbols-dir=%s' % symbols_dir,
69           '--jobs=%d' % options.jobs]
70    if options.verbose:
71      cmd.append('--verbose')
72      print ' '.join(cmd)
73    failure = 'Failed to run generate_breakpad_symbols.py.'
74    subprocess.check_call(cmd)
75
76    print "# Run content_shell and make it crash."
77    cmd = [options.binary,
78           '--dump-render-tree',
79           'chrome://crash',
80           '--enable-crash-reporter',
81           '--crash-dumps-dir=%s' % crash_dir]
82    if options.verbose:
83      print ' '.join(cmd)
84    failure = 'Failed to run content_shell.'
85    if options.verbose:
86      subprocess.check_call(cmd)
87    else:
88      with open(os.devnull, 'w') as devnull:
89        subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
90
91    print "# Retrieve crash dump."
92    dmp_files = glob.glob(os.path.join(crash_dir, '*.dmp'))
93    failure = 'Expected 1 crash dump, found %d.' % len(dmp_files)
94    if len(dmp_files) != 1:
95      raise Exception(failure)
96    dmp_file = dmp_files[0]
97    minidump = os.path.join(crash_dir, 'minidump')
98
99    dmp_to_minidump = os.path.join(breakpad_tools_dir, 'dmp2minidump.py')
100    cmd = [dmp_to_minidump, dmp_file, minidump]
101    if options.verbose:
102      print ' '.join(cmd)
103    failure = 'Failed to run dmp_to_minidump.'
104    subprocess.check_call(cmd)
105
106    print "# Symbolize crash dump."
107    minidump_stackwalk = os.path.join(options.build_dir, 'minidump_stackwalk')
108    cmd = [minidump_stackwalk, minidump, symbols_dir]
109    if options.verbose:
110      print ' '.join(cmd)
111    failure = 'Failed to run minidump_stackwalk.'
112    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
113    stack = proc.communicate()[0]
114
115    # Check whether the stack contains a CrashIntentionally symbol.
116    found_symbol = 'CrashIntentionally' in stack
117
118    if options.no_symbols:
119      if found_symbol:
120        if options.verbose:
121          print stack
122        failure = 'Found unexpected reference to CrashIntentionally in stack'
123        raise Exception(failure)
124    else:
125      if not found_symbol:
126        if options.verbose:
127          print stack
128        failure = 'Could not find reference to CrashIntentionally in stack.'
129        raise Exception(failure)
130
131  except:
132    print "FAIL: %s" % failure
133    return 1
134
135  else:
136    print "PASS: Breakpad integration test ran successfully."
137    return 0
138
139  finally:
140    try:
141      shutil.rmtree(crash_dir)
142    except:
143      print 'Failed to delete temp directory "%s".' % crash_dir
144
145
146if '__main__' == __name__:
147  sys.exit(main())
148