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