• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
2
3import argparse
4import collections
5import ConfigParser
6import os
7import shutil
8import sys
9
10from utils import shellcmd
11from utils import FindBaseNaCl
12
13def Match(desc, includes, excludes, default_match):
14  """Determines whether desc is a match against includes and excludes.
15
16  'desc' is a set of attributes, and 'includes' and 'excludes' are lists of sets
17  of attributes.
18
19  If 'desc' matches any element from 'excludes', the result is False.
20  Otherwise, if 'desc' matches any element from 'includes', the result is True.
21  Otherwise, the 'default_match' value is returned.
22  """
23  for exclude in excludes:
24    if exclude <= desc:
25      return False
26  for include in includes:
27    if include <= desc:
28      return True
29  return default_match
30
31
32def RunNativePrefix(toolchain_root, target, attr, run_cmd):
33  """Returns a prefix for running an executable for the target.
34
35  For example, we may be running an ARM or MIPS target executable on an
36  x86 machine and need to use an emulator.
37  """
38  arch_map = { 'x8632' : '',
39               'x8664' : '',
40               'arm32' : os.path.join(toolchain_root, 'arm_trusted',
41                                      'run_under_qemu_arm'),
42               'mips32': os.path.join(toolchain_root, 'mips_trusted',
43                                      'run_under_qemu_mips32'),
44             }
45  attr_map = collections.defaultdict(str, {
46      'arm32-neon': ' -cpu cortex-a9',
47      'arm32-hwdiv-arm': ' -cpu cortex-a15',
48      'mips32-base': ' -cpu mips32r5-generic'})
49  prefix = arch_map[target] + attr_map[target + '-' + attr]
50  if target == 'mips32':
51    prefix = 'QEMU_SET_ENV=LD_LIBRARY_PATH=/usr/mipsel-linux-gnu/lib/ ' + prefix
52  return (prefix + ' ' + run_cmd) if prefix else run_cmd
53
54def NonsfiLoaderArch(target):
55  """Returns the arch for the nonsfi_loader"""
56  arch_map = { 'arm32' : 'arm',
57               'x8632' : 'x86-32',
58               'mips32' : 'mips32',
59             }
60  return arch_map[target]
61
62
63def main():
64  """Framework for cross test generation and execution.
65
66  Builds and executes cross tests from the space of all possible attribute
67  combinations.  The space can be restricted by providing subsets of attributes
68  to specifically include or exclude.
69  """
70  # pypath is where to find other Subzero python scripts.
71  pypath = os.path.abspath(os.path.dirname(sys.argv[0]))
72  root = FindBaseNaCl()
73
74  # The rest of the attribute sets.
75  targets = [ 'x8632', 'x8664', 'arm32', 'mips32' ]
76  sandboxing = [ 'native', 'sandbox', 'nonsfi' ]
77  opt_levels = [ 'Om1', 'O2' ]
78  arch_attrs = { 'x8632': [ 'sse2', 'sse4.1' ],
79                 'x8664': [ 'sse2', 'sse4.1' ],
80                 'arm32': [ 'neon', 'hwdiv-arm' ],
81                 'mips32': [ 'base' ]
82               }
83  flat_attrs = []
84  for v in arch_attrs.values():
85    flat_attrs += v
86  arch_flags = { 'x8632': [],
87                 'x8664': [],
88                 'arm32': [],
89                 'mips32': []
90               }
91  # all_keys is only used in the help text.
92  all_keys = '; '.join([' '.join(targets), ' '.join(sandboxing),
93                        ' '.join(opt_levels), ' '.join(flat_attrs)])
94
95  argparser = argparse.ArgumentParser(
96    description='  ' + main.__doc__ +
97    'The set of attributes is the set of tests plus the following:\n' +
98    all_keys, formatter_class=argparse.RawTextHelpFormatter)
99  argparser.add_argument('--config', default='crosstest.cfg', dest='config',
100                         metavar='FILE', help='Test configuration file')
101  argparser.add_argument('--print-tests', default=False, action='store_true',
102                         help='Print the set of test names and exit')
103  argparser.add_argument('--include', '-i', default=[], dest='include',
104                         action='append', metavar='ATTR_LIST',
105                         help='Attributes to include (comma-separated). ' +
106                              'Can be used multiple times.')
107  argparser.add_argument('--exclude', '-e', default=[], dest='exclude',
108                         action='append', metavar='ATTR_LIST',
109                         help='Attributes to include (comma-separated). ' +
110                              'Can be used multiple times.')
111  argparser.add_argument('--verbose', '-v', default=False, action='store_true',
112                         help='Use verbose output')
113  argparser.add_argument('--defer', default=False, action='store_true',
114                         help='Defer execution until all executables are built')
115  argparser.add_argument('--no-compile', '-n', default=False,
116                         action='store_true',
117                         help="Don't build; reuse binaries from the last run")
118  argparser.add_argument('--dir', dest='dir', metavar='DIRECTORY',
119                         default=('{root}/toolchain_build/src/subzero/' +
120                                  'crosstest/Output').format(root=root),
121                         help='Output directory')
122  argparser.add_argument('--lit', default=False, action='store_true',
123                         help='Generate files for lit testing')
124  argparser.add_argument('--toolchain-root', dest='toolchain_root',
125                         default=(
126                           '{root}/toolchain/linux_x86/pnacl_newlib_raw/bin'
127                         ).format(root=root),
128                         help='Path to toolchain binaries.')
129  argparser.add_argument('--filetype', default=None, dest='filetype',
130                         help='File type override, one of {asm, iasm, obj}.')
131  args = argparser.parse_args()
132
133  # Run from the crosstest directory to make it easy to grab inputs.
134  crosstest_dir = '{root}/toolchain_build/src/subzero/crosstest'.format(
135    root=root)
136  os.chdir(crosstest_dir)
137
138  tests = ConfigParser.RawConfigParser()
139  tests.read('crosstest.cfg')
140
141  if args.print_tests:
142    print 'Test name attributes: ' + ' '.join(sorted(tests.sections()))
143    sys.exit(0)
144
145  # includes and excludes are both lists of sets.
146  includes = [ set(item.split(',')) for item in args.include ]
147  excludes = [ set(item.split(',')) for item in args.exclude ]
148  # If any --include args are provided, the default is to not match.
149  default_match = not args.include
150
151  # Delete and recreate the output directory, unless --no-compile was specified.
152  if not args.no_compile:
153    if os.path.exists(args.dir):
154      if os.path.isdir(args.dir):
155        shutil.rmtree(args.dir)
156      else:
157        os.remove(args.dir)
158    if not os.path.exists(args.dir):
159      os.makedirs(args.dir)
160
161  # If --defer is specified, collect the run commands into deferred_cmds for
162  # later execution.
163  deferred_cmds = []
164  for test in sorted(tests.sections()):
165    for target in targets:
166      for sb in sandboxing:
167        for opt in opt_levels:
168          for attr in arch_attrs[target]:
169            desc = [ test, target, sb, opt, attr ]
170            if Match(set(desc), includes, excludes, default_match):
171              exe = '{test}_{target}_{sb}_{opt}_{attr}'.format(
172                test=test, target=target, sb=sb, opt=opt,
173                attr=attr)
174              extra = (tests.get(test, 'flags').split(' ')
175                       if tests.has_option(test, 'flags') else [])
176              if args.filetype:
177                extra += ['--filetype={ftype}'.format(ftype=args.filetype)]
178              # Generate the compile command.
179              cmp_cmd = (
180                ['{path}/crosstest.py'.format(path=pypath),
181                 '-{opt}'.format(opt=opt),
182                 '--mattr={attr}'.format(attr=attr),
183                 '--prefix=Subzero_',
184                 '--target={target}'.format(target=target),
185                 '--nonsfi={nsfi}'.format(nsfi='1' if sb=='nonsfi' else '0'),
186                 '--sandbox={sb}'.format(sb='1' if sb=='sandbox' else '0'),
187                 '--dir={dir}'.format(dir=args.dir),
188                 '--output={exe}'.format(exe=exe),
189                 '--driver={drv}'.format(drv=tests.get(test, 'driver'))] +
190                extra +
191                ['--test=' + t
192                 for t in tests.get(test, 'test').split(' ')] +
193                arch_flags[target])
194              run_cmd_base = os.path.join(args.dir, exe)
195              # Generate the run command.
196              run_cmd = run_cmd_base
197              if sb == 'sandbox':
198                run_cmd = '{root}/run.py -q '.format(root=root) + run_cmd
199              elif sb == 'nonsfi':
200                run_cmd = (
201                    '{root}/scons-out/opt-linux-{arch}/obj/src/nonsfi/' +
202                    'loader/nonsfi_loader ').format(
203                        root=root, arch=NonsfiLoaderArch(target)) + run_cmd
204                run_cmd = RunNativePrefix(args.toolchain_root, target, attr,
205                                          run_cmd)
206              else:
207                run_cmd = RunNativePrefix(args.toolchain_root, target, attr,
208                                          run_cmd)
209              if args.lit:
210                # Create a file to drive the lit test.
211                with open(run_cmd_base + '.xtest', 'w') as f:
212                  f.write('# RUN: sh %s | FileCheck %s\n')
213                  f.write('cd ' + crosstest_dir + ' && \\\n')
214                  f.write(' '.join(cmp_cmd) + ' && \\\n')
215                  f.write(run_cmd + '\n')
216                  f.write('echo Recreate a failure using ' + __file__ +
217                          ' --toolchain-root=' + args.toolchain_root +
218                          (' --filetype=' + args.filetype
219                            if args.filetype else '') +
220                          ' --include=' + ','.join(desc) + '\n')
221                  f.write('# CHECK: Failures=0\n')
222              else:
223                if not args.no_compile:
224                  shellcmd(cmp_cmd,
225                           echo=args.verbose)
226                if (args.defer):
227                  deferred_cmds.append(run_cmd)
228                else:
229                  shellcmd(run_cmd, echo=True)
230  for run_cmd in deferred_cmds:
231    shellcmd(run_cmd, echo=True)
232
233if __name__ == '__main__':
234  main()
235