SConscript revision 8335
19480Snilay@cs.wisc.edu# -*- mode:python -*- 210785Sgope@wisc.edu 39480Snilay@cs.wisc.edu# Copyright (c) 2004-2006 The Regents of The University of Michigan 49480Snilay@cs.wisc.edu# All rights reserved. 59480Snilay@cs.wisc.edu# 69480Snilay@cs.wisc.edu# Redistribution and use in source and binary forms, with or without 79480Snilay@cs.wisc.edu# modification, are permitted provided that the following conditions are 89480Snilay@cs.wisc.edu# met: redistributions of source code must retain the above copyright 99480Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer; 109480Snilay@cs.wisc.edu# redistributions in binary form must reproduce the above copyright 119480Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer in the 129480Snilay@cs.wisc.edu# documentation and/or other materials provided with the distribution; 139480Snilay@cs.wisc.edu# neither the name of the copyright holders nor the names of its 149480Snilay@cs.wisc.edu# contributors may be used to endorse or promote products derived from 159480Snilay@cs.wisc.edu# this software without specific prior written permission. 169480Snilay@cs.wisc.edu# 179480Snilay@cs.wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 189480Snilay@cs.wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 199480Snilay@cs.wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 209480Snilay@cs.wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 219480Snilay@cs.wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 229480Snilay@cs.wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 239480Snilay@cs.wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 249480Snilay@cs.wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 259480Snilay@cs.wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 269480Snilay@cs.wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 279480Snilay@cs.wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2810785Sgope@wisc.edu# 299480Snilay@cs.wisc.edu# Authors: Steve Reinhardt 309480Snilay@cs.wisc.edu# Kevin Lim 319480Snilay@cs.wisc.edu 3213432Spau.cabre@metempsy.comimport os, signal 339480Snilay@cs.wisc.eduimport sys, time 3413957Sjairo.balart@metempsy.comimport glob 3513957Sjairo.balart@metempsy.comfrom SCons.Script.SConscript import SConsEnvironment 3613957Sjairo.balart@metempsy.com 3713957Sjairo.balart@metempsy.comImport('env') 3813957Sjairo.balart@metempsy.com 3913957Sjairo.balart@metempsy.comenv['DIFFOUT'] = File('diff-out') 4013957Sjairo.balart@metempsy.com 4113957Sjairo.balart@metempsy.com# Dict that accumulates lists of tests by category (quick, medium, long) 4213957Sjairo.balart@metempsy.comenv.Tests = {} 4313957Sjairo.balart@metempsy.com 4413957Sjairo.balart@metempsy.comdef contents(node): 4513957Sjairo.balart@metempsy.com return file(str(node)).read() 4613957Sjairo.balart@metempsy.com 4713957Sjairo.balart@metempsy.com# functions to parse return value from scons Execute()... not the same 4813957Sjairo.balart@metempsy.com# as wait() etc., so python built-in os funcs don't work. 4913957Sjairo.balart@metempsy.comdef signaled(status): 5013957Sjairo.balart@metempsy.com return (status & 0x80) != 0; 5113957Sjairo.balart@metempsy.com 5213957Sjairo.balart@metempsy.comdef signum(status): 5313957Sjairo.balart@metempsy.com return (status & 0x7f); 5413957Sjairo.balart@metempsy.com 5513957Sjairo.balart@metempsy.com# List of signals that indicate that we should retry the test rather 5613957Sjairo.balart@metempsy.com# than consider it failed. 579480Snilay@cs.wisc.eduretry_signals = (signal.SIGTERM, signal.SIGKILL, signal.SIGINT, 589480Snilay@cs.wisc.edu signal.SIGQUIT, signal.SIGHUP) 599480Snilay@cs.wisc.edu 609480Snilay@cs.wisc.edu# regular expressions of lines to ignore when diffing outputs 6110785Sgope@wisc.eduoutput_ignore_regexes = ( 629480Snilay@cs.wisc.edu '^command line:', # for stdout file 6313432Spau.cabre@metempsy.com '^M5 compiled ', # for stderr file 6410785Sgope@wisc.edu '^M5 started ', # for stderr file 6510785Sgope@wisc.edu '^M5 executing on ', # for stderr file 6610785Sgope@wisc.edu '^Simulation complete at', # for stderr file 6710785Sgope@wisc.edu '^Listening for', # for stderr file 6810785Sgope@wisc.edu 'listening for remote gdb', # for stderr file 6913957Sjairo.balart@metempsy.com ) 7013957Sjairo.balart@metempsy.com 7110785Sgope@wisc.eduoutput_ignore_args = ' '.join(["-I '"+s+"'" for s in output_ignore_regexes]) 7210785Sgope@wisc.edu 7310785Sgope@wisc.eduoutput_ignore_args += ' --exclude=stats.txt --exclude=outdiff' 7410785Sgope@wisc.edu 7510785Sgope@wisc.edudef run_test(target, source, env): 7610785Sgope@wisc.edu """Check output from running test. 779480Snilay@cs.wisc.edu 789480Snilay@cs.wisc.edu Targets are as follows: 7910785Sgope@wisc.edu target[0] : status 8010785Sgope@wisc.edu 8110785Sgope@wisc.edu Sources are: 8210785Sgope@wisc.edu source[0] : M5 binary 8310785Sgope@wisc.edu source[1] : tests/run.py script 8410785Sgope@wisc.edu source[2] : reference stats file 8510785Sgope@wisc.edu 8610785Sgope@wisc.edu """ 8710785Sgope@wisc.edu # make sure target files are all gone 8810785Sgope@wisc.edu for t in target: 899480Snilay@cs.wisc.edu if os.path.exists(t.abspath): 909480Snilay@cs.wisc.edu env.Execute(Delete(t.abspath)) 919480Snilay@cs.wisc.edu 929480Snilay@cs.wisc.edu tgt_dir = os.path.dirname(str(target[0])) 939480Snilay@cs.wisc.edu 949480Snilay@cs.wisc.edu # Base command for running test. We mess around with indirectly 9510785Sgope@wisc.edu # referring to files via SOURCES and TARGETS so that scons can mess 9610785Sgope@wisc.edu # with paths all it wants to and we still get the right files. 9710785Sgope@wisc.edu cmd = '${SOURCES[0]} -d %s -re ${SOURCES[1]} %s' % (tgt_dir, tgt_dir) 9810785Sgope@wisc.edu 9910785Sgope@wisc.edu # Prefix test run with batch job submission command if appropriate. 10010785Sgope@wisc.edu # Batch command also supports timeout arg (in seconds, not minutes). 10110785Sgope@wisc.edu timeout = 15 * 60 # used to be a param, probably should be again 10210785Sgope@wisc.edu if env['BATCH']: 10310785Sgope@wisc.edu cmd = '%s -t %d %s' % (env['BATCH_CMD'], timeout, cmd) 10410785Sgope@wisc.edu 10513626Sjairo.balart@metempsy.com pre_exec_time = time.time() 10613626Sjairo.balart@metempsy.com status = env.Execute(env.subst(cmd, target=target, source=source)) 10713626Sjairo.balart@metempsy.com if status == 0: 10813626Sjairo.balart@metempsy.com # M5 terminated normally. 10913626Sjairo.balart@metempsy.com # Run diff on output & ref directories to find differences. 11013626Sjairo.balart@metempsy.com # Exclude the stats file since we will use diff-out on that. 11113626Sjairo.balart@metempsy.com 11213626Sjairo.balart@metempsy.com # NFS file systems can be annoying and not have updated yet 11313454Spau.cabre@metempsy.com # wait until we see the file modified 11413454Spau.cabre@metempsy.com statsdiff = os.path.join(tgt_dir, 'statsdiff') 11513494Spau.cabre@metempsy.com m_time = 0 11613494Spau.cabre@metempsy.com nap = 0 11713454Spau.cabre@metempsy.com while m_time < pre_exec_time and nap < 10: 11813454Spau.cabre@metempsy.com try: 11913454Spau.cabre@metempsy.com m_time = os.stat(statsdiff).st_mtime 12013454Spau.cabre@metempsy.com except OSError: 12113454Spau.cabre@metempsy.com pass 12213454Spau.cabre@metempsy.com time.sleep(1) 12313454Spau.cabre@metempsy.com nap += 1 12413454Spau.cabre@metempsy.com 12513454Spau.cabre@metempsy.com outdiff = os.path.join(tgt_dir, 'outdiff') 12613454Spau.cabre@metempsy.com diffcmd = 'diff -ubrs %s ${SOURCES[2].dir} %s > %s' \ 12713454Spau.cabre@metempsy.com % (output_ignore_args, tgt_dir, outdiff) 12813454Spau.cabre@metempsy.com env.Execute(env.subst(diffcmd, target=target, source=source)) 12913454Spau.cabre@metempsy.com print "===== Output differences =====" 13013454Spau.cabre@metempsy.com print contents(outdiff) 13113454Spau.cabre@metempsy.com # Run diff-out on stats.txt file 13213454Spau.cabre@metempsy.com diffcmd = '$DIFFOUT ${SOURCES[2]} %s > %s' \ 13313454Spau.cabre@metempsy.com % (os.path.join(tgt_dir, 'stats.txt'), statsdiff) 13413454Spau.cabre@metempsy.com diffcmd = env.subst(diffcmd, target=target, source=source) 13513626Sjairo.balart@metempsy.com status = env.Execute(diffcmd, strfunction=None) 13613685Sjavier.bueno@metempsy.com print "===== Statistics differences =====" 13713454Spau.cabre@metempsy.com print contents(statsdiff) 13813626Sjairo.balart@metempsy.com 13913626Sjairo.balart@metempsy.com else: # m5 exit status != 0 14013626Sjairo.balart@metempsy.com # M5 did not terminate properly, so no need to check the output 14113626Sjairo.balart@metempsy.com if signaled(status): 14213626Sjairo.balart@metempsy.com print 'M5 terminated with signal', signum(status) 14313626Sjairo.balart@metempsy.com if signum(status) in retry_signals: 14413626Sjairo.balart@metempsy.com # Consider the test incomplete; don't create a 'status' output. 14513626Sjairo.balart@metempsy.com # Hand the return status to scons and let scons decide what 14613626Sjairo.balart@metempsy.com # to do about it (typically terminate unless run with -k). 14713626Sjairo.balart@metempsy.com return status 14813626Sjairo.balart@metempsy.com else: 14913626Sjairo.balart@metempsy.com print 'M5 exited with non-zero status', status 15013626Sjairo.balart@metempsy.com # complete but failed execution (call to exit() with non-zero 15113626Sjairo.balart@metempsy.com # status, SIGABORT due to assertion failure, etc.)... fall through 15213626Sjairo.balart@metempsy.com # and generate FAILED status as if output comparison had failed 15313626Sjairo.balart@metempsy.com 15413626Sjairo.balart@metempsy.com # Generate status file contents based on exit status of m5 or diff-out 15513626Sjairo.balart@metempsy.com if status == 0: 15613626Sjairo.balart@metempsy.com status_str = "passed." 15713626Sjairo.balart@metempsy.com else: 15813626Sjairo.balart@metempsy.com status_str = "FAILED!" 15913626Sjairo.balart@metempsy.com f = file(str(target[0]), 'w') 16013626Sjairo.balart@metempsy.com print >>f, tgt_dir, status_str 16113626Sjairo.balart@metempsy.com f.close() 16213454Spau.cabre@metempsy.com # done 16313627Sjavier.bueno@metempsy.com return 0 16413627Sjavier.bueno@metempsy.com 16513627Sjavier.bueno@metempsy.comdef run_test_string(target, source, env): 16613627Sjavier.bueno@metempsy.com return env.subst("Running test in ${TARGETS[0].dir}.", 16713454Spau.cabre@metempsy.com target=target, source=source) 16811784Sarthur.perais@inria.fr 16913444Spau.cabre@metempsy.comtestAction = env.Action(run_test, run_test_string) 17013442Spau.cabre@metempsy.com 17113442Spau.cabre@metempsy.comdef print_test(target, source, env): 17213442Spau.cabre@metempsy.com print '***** ' + contents(source[0]) 17313442Spau.cabre@metempsy.com return 0 17413442Spau.cabre@metempsy.com 17513444Spau.cabre@metempsy.comprintAction = env.Action(print_test, strfunction = None) 17613442Spau.cabre@metempsy.com 17713493Spau.cabre@metempsy.com# Static vars for update_test: 17813627Sjavier.bueno@metempsy.com# - long-winded message about ignored sources 17913627Sjavier.bueno@metempsy.comignore_msg = ''' 18013493Spau.cabre@metempsy.comNote: The following file(s) will not be copied. New non-standard 18113493Spau.cabre@metempsy.com output files must be copied manually once before --update-ref will 18213493Spau.cabre@metempsy.com recognize them as outputs. Otherwise they are assumed to be 18313493Spau.cabre@metempsy.com inputs and are ignored. 18413493Spau.cabre@metempsy.com''' 18513493Spau.cabre@metempsy.com# - reference files always needed 18613493Spau.cabre@metempsy.comneeded_files = set(['simout', 'simerr', 'stats.txt', 'config.ini']) 18713493Spau.cabre@metempsy.com# - source files we always want to ignore 18813493Spau.cabre@metempsy.comknown_ignores = set(['status', 'outdiff', 'statsdiff']) 18913493Spau.cabre@metempsy.com 19013493Spau.cabre@metempsy.comdef update_test(target, source, env): 19113493Spau.cabre@metempsy.com """Update reference test outputs. 19213493Spau.cabre@metempsy.com 19313627Sjavier.bueno@metempsy.com Target is phony. First two sources are the ref & new stats.txt file 19413627Sjavier.bueno@metempsy.com files, respectively. We actually copy everything in the 19513627Sjavier.bueno@metempsy.com respective directories except the status & diff output files. 19613627Sjavier.bueno@metempsy.com 19713627Sjavier.bueno@metempsy.com """ 19813627Sjavier.bueno@metempsy.com dest_dir = str(source[0].get_dir()) 19913627Sjavier.bueno@metempsy.com src_dir = str(source[1].get_dir()) 20013627Sjavier.bueno@metempsy.com dest_files = set(os.listdir(dest_dir)) 20113627Sjavier.bueno@metempsy.com src_files = set(os.listdir(src_dir)) 20213627Sjavier.bueno@metempsy.com # Copy all of the required files plus any existing dest files. 20313685Sjavier.bueno@metempsy.com wanted_files = needed_files | dest_files 20413685Sjavier.bueno@metempsy.com missing_files = wanted_files - src_files 20513685Sjavier.bueno@metempsy.com if len(missing_files) > 0: 20613685Sjavier.bueno@metempsy.com print " WARNING: the following file(s) are missing " \ 20713685Sjavier.bueno@metempsy.com "and will not be updated:" 20813685Sjavier.bueno@metempsy.com print " ", " ,".join(missing_files) 20913685Sjavier.bueno@metempsy.com copy_files = wanted_files - missing_files 21013685Sjavier.bueno@metempsy.com warn_ignored_files = (src_files - copy_files) - known_ignores 21113685Sjavier.bueno@metempsy.com if len(warn_ignored_files) > 0: 21213685Sjavier.bueno@metempsy.com print ignore_msg, 21313685Sjavier.bueno@metempsy.com print " ", ", ".join(warn_ignored_files) 21413685Sjavier.bueno@metempsy.com for f in copy_files: 21513685Sjavier.bueno@metempsy.com if f in dest_files: 21613685Sjavier.bueno@metempsy.com print " Replacing file", f 21713685Sjavier.bueno@metempsy.com dest_files.remove(f) 21813685Sjavier.bueno@metempsy.com else: 21913685Sjavier.bueno@metempsy.com print " Creating new file", f 22013685Sjavier.bueno@metempsy.com copyAction = Copy(os.path.join(dest_dir, f), os.path.join(src_dir, f)) 22113685Sjavier.bueno@metempsy.com copyAction.strfunction = None 22213685Sjavier.bueno@metempsy.com env.Execute(copyAction) 22313685Sjavier.bueno@metempsy.com return 0 22413685Sjavier.bueno@metempsy.com 22513685Sjavier.bueno@metempsy.comdef update_test_string(target, source, env): 22613685Sjavier.bueno@metempsy.com return env.subst("Updating ${SOURCES[0].dir} from ${SOURCES[1].dir}", 22713685Sjavier.bueno@metempsy.com target=target, source=source) 22813685Sjavier.bueno@metempsy.com 22913685Sjavier.bueno@metempsy.comupdateAction = env.Action(update_test, update_test_string) 23013685Sjavier.bueno@metempsy.com 23113685Sjavier.bueno@metempsy.comdef test_builder(env, ref_dir): 23213685Sjavier.bueno@metempsy.com """Define a test.""" 23313685Sjavier.bueno@metempsy.com 23413685Sjavier.bueno@metempsy.com (category, name, _ref, isa, opsys, config) = ref_dir.split('/') 23513685Sjavier.bueno@metempsy.com assert(_ref == 'ref') 23613685Sjavier.bueno@metempsy.com 23713685Sjavier.bueno@metempsy.com # target path (where test output goes) is the same except without 23813685Sjavier.bueno@metempsy.com # the 'ref' component 23913685Sjavier.bueno@metempsy.com tgt_dir = os.path.join(category, name, isa, opsys, config) 24013685Sjavier.bueno@metempsy.com 24113685Sjavier.bueno@metempsy.com # prepend file name with tgt_dir 24213685Sjavier.bueno@metempsy.com def tgt(f): 24313685Sjavier.bueno@metempsy.com return os.path.join(tgt_dir, f) 24413685Sjavier.bueno@metempsy.com 24513685Sjavier.bueno@metempsy.com ref_stats = os.path.join(ref_dir, 'stats.txt') 24613685Sjavier.bueno@metempsy.com new_stats = tgt('stats.txt') 24713685Sjavier.bueno@metempsy.com status_file = tgt('status') 24813685Sjavier.bueno@metempsy.com 24913685Sjavier.bueno@metempsy.com env.Command([status_file], 25013685Sjavier.bueno@metempsy.com [env.M5Binary, 'run.py', ref_stats], 25113685Sjavier.bueno@metempsy.com testAction) 25213685Sjavier.bueno@metempsy.com 25313685Sjavier.bueno@metempsy.com # phony target to echo status 25413685Sjavier.bueno@metempsy.com if GetOption('update_ref'): 25513685Sjavier.bueno@metempsy.com p = env.Command(tgt('_update'), 25613685Sjavier.bueno@metempsy.com [ref_stats, new_stats, status_file], 25713685Sjavier.bueno@metempsy.com updateAction) 25813685Sjavier.bueno@metempsy.com else: 25913685Sjavier.bueno@metempsy.com p = env.Command(tgt('_print'), [status_file], printAction) 26013685Sjavier.bueno@metempsy.com 26113685Sjavier.bueno@metempsy.com env.AlwaysBuild(p) 26213685Sjavier.bueno@metempsy.com 26313685Sjavier.bueno@metempsy.com 26413685Sjavier.bueno@metempsy.com# Figure out applicable configs based on build type 26513685Sjavier.bueno@metempsy.comconfigs = [] 26613685Sjavier.bueno@metempsy.comif env['FULL_SYSTEM']: 26713685Sjavier.bueno@metempsy.com if env['TARGET_ISA'] == 'alpha': 26813685Sjavier.bueno@metempsy.com configs += ['tsunami-simple-atomic', 26913685Sjavier.bueno@metempsy.com 'tsunami-simple-timing', 27013685Sjavier.bueno@metempsy.com 'tsunami-simple-atomic-dual', 27113685Sjavier.bueno@metempsy.com 'tsunami-simple-timing-dual', 27213685Sjavier.bueno@metempsy.com 'twosys-tsunami-simple-atomic', 27313685Sjavier.bueno@metempsy.com 'tsunami-o3', 'tsunami-o3-dual'] 27413685Sjavier.bueno@metempsy.com if env['TARGET_ISA'] == 'sparc': 27513685Sjavier.bueno@metempsy.com configs += ['t1000-simple-atomic', 27613685Sjavier.bueno@metempsy.com 't1000-simple-timing'] 27713685Sjavier.bueno@metempsy.com if env['TARGET_ISA'] == 'arm': 27813685Sjavier.bueno@metempsy.com configs += ['realview-simple-atomic', 27913685Sjavier.bueno@metempsy.com 'realview-simple-timing', 28013685Sjavier.bueno@metempsy.com 'realview-o3'] 28113685Sjavier.bueno@metempsy.com if env['TARGET_ISA'] == 'x86': 28213685Sjavier.bueno@metempsy.com configs += ['pc-simple-atomic', 28313685Sjavier.bueno@metempsy.com 'pc-simple-timing'] 28413685Sjavier.bueno@metempsy.com 28513685Sjavier.bueno@metempsy.comelse: 28613685Sjavier.bueno@metempsy.com configs += ['simple-atomic', 'simple-timing', 'o3-timing', 'memtest', 28713685Sjavier.bueno@metempsy.com 'simple-atomic-mp', 'simple-timing-mp', 'o3-timing-mp', 28813685Sjavier.bueno@metempsy.com 'inorder-timing', 'rubytest'] 28913685Sjavier.bueno@metempsy.com 29013685Sjavier.bueno@metempsy.comif env['RUBY']: 29113685Sjavier.bueno@metempsy.com # With Ruby, A protocol must be specified in the environment 29213685Sjavier.bueno@metempsy.com assert(env['PROTOCOL']) 29313627Sjavier.bueno@metempsy.com 29413627Sjavier.bueno@metempsy.com # 29513627Sjavier.bueno@metempsy.com # Is there a way to determine what is Protocol EnumVariable 29613627Sjavier.bueno@metempsy.com # default and eliminate the need to hard code the default protocol below? 29713627Sjavier.bueno@metempsy.com # 29813627Sjavier.bueno@metempsy.com # If the binary includes the default ruby protocol, run both ruby and 29913627Sjavier.bueno@metempsy.com # non-ruby versions of the tests. Otherwise just run the ruby versions. 30013627Sjavier.bueno@metempsy.com # 30113627Sjavier.bueno@metempsy.com if env['PROTOCOL'] == 'MI_example': 30213627Sjavier.bueno@metempsy.com configs += [c + "-ruby" for c in configs] 30313627Sjavier.bueno@metempsy.com else: 30413685Sjavier.bueno@metempsy.com configs = [c + "-ruby-" + env['PROTOCOL'] for c in configs] 30513627Sjavier.bueno@metempsy.com 30613685Sjavier.bueno@metempsy.comcwd = os.getcwd() 30713685Sjavier.bueno@metempsy.comos.chdir(str(Dir('.').srcdir)) 30813685Sjavier.bueno@metempsy.comfor config in configs: 30913685Sjavier.bueno@metempsy.com dirs = glob.glob('*/*/ref/%s/*/%s' % (env['TARGET_ISA'], config)) 31013685Sjavier.bueno@metempsy.com for d in dirs: 31113685Sjavier.bueno@metempsy.com if not os.path.exists(os.path.join(d, 'skip')): 31213685Sjavier.bueno@metempsy.com test_builder(env, d) 31313685Sjavier.bueno@metempsy.comos.chdir(cwd) 31413685Sjavier.bueno@metempsy.com