SConscript revision 9242:256143419b40
1545SN/A# -*- mode:python -*-
211010Sandreas.sandberg@arm.com
38948SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan
48948SN/A# All rights reserved.
58948SN/A#
68948SN/A# Redistribution and use in source and binary forms, with or without
78948SN/A# modification, are permitted provided that the following conditions are
88948SN/A# met: redistributions of source code must retain the above copyright
98948SN/A# notice, this list of conditions and the following disclaimer;
108948SN/A# redistributions in binary form must reproduce the above copyright
118948SN/A# notice, this list of conditions and the following disclaimer in the
128948SN/A# documentation and/or other materials provided with the distribution;
138948SN/A# neither the name of the copyright holders nor the names of its
142512SN/A# contributors may be used to endorse or promote products derived from
15545SN/A# this software without specific prior written permission.
16545SN/A#
17545SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18545SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19545SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20545SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21545SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22545SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23545SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24545SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25545SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26545SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27545SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28545SN/A#
29545SN/A# Authors: Steve Reinhardt
30545SN/A#          Kevin Lim
31545SN/A
32545SN/Aimport os, signal
33545SN/Aimport sys, time
34545SN/Aimport glob
35545SN/Afrom SCons.Script.SConscript import SConsEnvironment
36545SN/A
37545SN/AImport('env')
38545SN/A
392665SN/Aenv['DIFFOUT'] = File('diff-out')
402665SN/A
412665SN/A# get the termcap from the environment
429166Sandreas.hansson@arm.comtermcap = env['TERMCAP']
4311010Sandreas.sandberg@arm.com
44545SN/A# Dict that accumulates lists of tests by category (quick, medium, long)
45545SN/Aenv.Tests = {}
4611010Sandreas.sandberg@arm.com
4711010Sandreas.sandberg@arm.comdef contents(node):
4811010Sandreas.sandberg@arm.com    return file(str(node)).read()
4911010Sandreas.sandberg@arm.com
503090SN/A# functions to parse return value from scons Execute()... not the same
518232SN/A# as wait() etc., so python built-in os funcs don't work.
529152Satgutier@umich.edudef signaled(status):
532901SN/A    return (status & 0x80) != 0;
54545SN/A
559165Sandreas.hansson@arm.comdef signum(status):
5611010Sandreas.sandberg@arm.com    return (status & 0x7f);
5711010Sandreas.sandberg@arm.com
5811010Sandreas.sandberg@arm.com# List of signals that indicate that we should retry the test rather
592489SN/A# than consider it failed.
602489SN/Aretry_signals = (signal.SIGTERM, signal.SIGKILL, signal.SIGINT,
619166Sandreas.hansson@arm.com                 signal.SIGQUIT, signal.SIGHUP)
629166Sandreas.hansson@arm.com
639166Sandreas.hansson@arm.com# regular expressions of lines to ignore when diffing outputs
649166Sandreas.hansson@arm.comoutput_ignore_regexes = (
659166Sandreas.hansson@arm.com    '^command line:',		# for stdout file
669166Sandreas.hansson@arm.com    '^M5 compiled ',		# for stderr file
679166Sandreas.hansson@arm.com    '^M5 started ',		# for stderr file
689166Sandreas.hansson@arm.com    '^M5 executing on ',	# for stderr file
699166Sandreas.hansson@arm.com    '^Simulation complete at',	# for stderr file
709166Sandreas.hansson@arm.com    '^Listening for',		# for stderr file
719166Sandreas.hansson@arm.com    'listening for remote gdb',	# for stderr file
729166Sandreas.hansson@arm.com    )
739166Sandreas.hansson@arm.com
749166Sandreas.hansson@arm.comoutput_ignore_args = ' '.join(["-I '"+s+"'" for s in output_ignore_regexes])
759166Sandreas.hansson@arm.com
769166Sandreas.hansson@arm.comoutput_ignore_args += ' --exclude=stats.txt --exclude=outdiff'
779166Sandreas.hansson@arm.com
789166Sandreas.hansson@arm.comdef run_test(target, source, env):
799166Sandreas.hansson@arm.com    """Check output from running test.
809166Sandreas.hansson@arm.com
819166Sandreas.hansson@arm.com    Targets are as follows:
829166Sandreas.hansson@arm.com    target[0] : status
839166Sandreas.hansson@arm.com
849166Sandreas.hansson@arm.com    Sources are:
859166Sandreas.hansson@arm.com    source[0] : M5 binary
869166Sandreas.hansson@arm.com    source[1] : tests/run.py script
879166Sandreas.hansson@arm.com    source[2] : reference stats file
889166Sandreas.hansson@arm.com
899166Sandreas.hansson@arm.com    """
909166Sandreas.hansson@arm.com    # make sure target files are all gone
919452SAndreas.Sandberg@ARM.com    for t in target:
929166Sandreas.hansson@arm.com        if os.path.exists(t.abspath):
939166Sandreas.hansson@arm.com            env.Execute(Delete(t.abspath))
949166Sandreas.hansson@arm.com
959166Sandreas.hansson@arm.com    tgt_dir = os.path.dirname(str(target[0]))
969166Sandreas.hansson@arm.com
979166Sandreas.hansson@arm.com    # Base command for running test.  We mess around with indirectly
989166Sandreas.hansson@arm.com    # referring to files via SOURCES and TARGETS so that scons can mess
999166Sandreas.hansson@arm.com    # with paths all it wants to and we still get the right files.
1009166Sandreas.hansson@arm.com    cmd = '${SOURCES[0]} -d %s -re ${SOURCES[1]} %s' % (tgt_dir, tgt_dir)
10110913Sandreas.sandberg@arm.com
10210913Sandreas.sandberg@arm.com    # Prefix test run with batch job submission command if appropriate.
1039166Sandreas.hansson@arm.com    # Batch command also supports timeout arg (in seconds, not minutes).
1049166Sandreas.hansson@arm.com    timeout = 15 * 60 # used to be a param, probably should be again
1052489SN/A    if env['BATCH']:
1068975SN/A        cmd = '%s -t %d %s' % (env['BATCH_CMD'], timeout, cmd)
1072384SN/A
10811284Sandreas.hansson@arm.com    pre_exec_time = time.time()
10910821Sandreas.hansson@arm.com    status = env.Execute(env.subst(cmd, target=target, source=source))
11011284Sandreas.hansson@arm.com    if status == 0:
1114435SN/A        # M5 terminated normally.
1129166Sandreas.hansson@arm.com        # Run diff on output & ref directories to find differences.
1132569SN/A        # Exclude the stats file since we will use diff-out on that.
1142657SN/A
1152384SN/A        # NFS file systems can be annoying and not have updated yet
116679SN/A        # wait until we see the file modified
1174762SN/A        statsdiff = os.path.join(tgt_dir, 'statsdiff')
1189165Sandreas.hansson@arm.com        m_time = 0
1192565SN/A        nap = 0
1202384SN/A        while m_time < pre_exec_time and nap < 10:
1218851SN/A            try:
1228851SN/A                m_time = os.stat(statsdiff).st_mtime
1238851SN/A            except OSError:
1248851SN/A                pass
1258851SN/A            time.sleep(1)
1268851SN/A            nap += 1
1278851SN/A
1288851SN/A        outdiff = os.path.join(tgt_dir, 'outdiff')
12910913Sandreas.sandberg@arm.com        # tack 'true' on the end so scons doesn't report diff's
13010913Sandreas.sandberg@arm.com        # non-zero exit code as a build error
1312901SN/A        diffcmd = 'diff -ubrs %s ${SOURCES[2].dir} %s > %s; true' \
13210913Sandreas.sandberg@arm.com                  % (output_ignore_args, tgt_dir, outdiff)
13310913Sandreas.sandberg@arm.com        env.Execute(env.subst(diffcmd, target=target, source=source))
13410913Sandreas.sandberg@arm.com        print "===== Output differences ====="
13510913Sandreas.sandberg@arm.com        print contents(outdiff)
13610913Sandreas.sandberg@arm.com        # Run diff-out on stats.txt file
13710913Sandreas.sandberg@arm.com        diffcmd = '$DIFFOUT ${SOURCES[2]} %s > %s' \
1382901SN/A                  % (os.path.join(tgt_dir, 'stats.txt'), statsdiff)
1392901SN/A        diffcmd = env.subst(diffcmd, target=target, source=source)
1402384SN/A        status = env.Execute(diffcmd, strfunction=None)
14110713Sandreas.hansson@arm.com        print "===== Statistics differences ====="
1422489SN/A        print contents(statsdiff)
1434435SN/A
1449307Sandreas.hansson@arm.com    else: # m5 exit status != 0
1452489SN/A        # M5 did not terminate properly, so no need to check the output
1462641SN/A        if signaled(status):
14710621SCurtis.Dunham@arm.com            print 'M5 terminated with signal', signum(status)
1482641SN/A            if signum(status) in retry_signals:
1497607SN/A                # Consider the test incomplete; don't create a 'status' output.
1502384SN/A                # Hand the return status to scons and let scons decide what
1519166Sandreas.hansson@arm.com                # to do about it (typically terminate unless run with -k).
1529166Sandreas.hansson@arm.com                return status
1539166Sandreas.hansson@arm.com        else:
1549016Sandreas.hansson@arm.com            print 'M5 exited with non-zero status', status
1552384SN/A        # complete but failed execution (call to exit() with non-zero
15610621SCurtis.Dunham@arm.com        # status, SIGABORT due to assertion failure, etc.)... fall through
15710621SCurtis.Dunham@arm.com        # and generate FAILED status as if output comparison had failed
15810621SCurtis.Dunham@arm.com
15910621SCurtis.Dunham@arm.com    # Generate status file contents based on exit status of m5 or diff-out
16010621SCurtis.Dunham@arm.com    if status == 0:
16110621SCurtis.Dunham@arm.com        status_str = "passed."
1624451SN/A    else:
1639166Sandreas.hansson@arm.com        status_str = "FAILED!"
1649814Sandreas.hansson@arm.com    f = file(str(target[0]), 'w')
1652406SN/A    print >>f, tgt_dir, status_str
16610621SCurtis.Dunham@arm.com    f.close()
16710024Sdam.sunwoo@arm.com    # done
1689166Sandreas.hansson@arm.com    return 0
1692641SN/A
1709166Sandreas.hansson@arm.comdef run_test_string(target, source, env):
1719166Sandreas.hansson@arm.com    return env.subst("Running test in ${TARGETS[0].dir}.",
1729166Sandreas.hansson@arm.com                     target=target, source=source)
1732641SN/A
1749166Sandreas.hansson@arm.comtestAction = env.Action(run_test, run_test_string)
1752641SN/A
1769166Sandreas.hansson@arm.comdef print_test(target, source, env):
1779166Sandreas.hansson@arm.com    # print the status with colours to make it easier to see what
1789166Sandreas.hansson@arm.com    # passed and what failed
1792384SN/A    line = contents(source[0])
1809307Sandreas.hansson@arm.com
1819307Sandreas.hansson@arm.com    # split the line to words and get the last one
1829307Sandreas.hansson@arm.com    words = line.split()
1839307Sandreas.hansson@arm.com    status = words[-1]
1849307Sandreas.hansson@arm.com
18510621SCurtis.Dunham@arm.com    # if the test failed make it red, if it passed make it green, and
18610621SCurtis.Dunham@arm.com    # skip the punctuation
1872384SN/A    if status == "FAILED!":
1882384SN/A        status = termcap.Red + status[:-1] + termcap.Normal + status[-1]
1894435SN/A    elif status == "passed.":
1909166Sandreas.hansson@arm.com        status = termcap.Green + status[:-1] + termcap.Normal + status[-1]
1914435SN/A
1929166Sandreas.hansson@arm.com    # put it back in the list and join with space
1934435SN/A    words[-1] = status
1949166Sandreas.hansson@arm.com    line = " ".join(words)
1959166Sandreas.hansson@arm.com
1969166Sandreas.hansson@arm.com    print '***** ' + line
1979307Sandreas.hansson@arm.com    return 0
1989166Sandreas.hansson@arm.com
1999307Sandreas.hansson@arm.comprintAction = env.Action(print_test, strfunction = None)
2009307Sandreas.hansson@arm.com
2019307Sandreas.hansson@arm.com# Static vars for update_test:
2029307Sandreas.hansson@arm.com# - long-winded message about ignored sources
2039307Sandreas.hansson@arm.comignore_msg = '''
2049307Sandreas.hansson@arm.comNote: The following file(s) will not be copied.  New non-standard
2059307Sandreas.hansson@arm.com      output files must be copied manually once before --update-ref will
2069307Sandreas.hansson@arm.com      recognize them as outputs.  Otherwise they are assumed to be
2079307Sandreas.hansson@arm.com      inputs and are ignored.
2089307Sandreas.hansson@arm.com'''
2099307Sandreas.hansson@arm.com# - reference files always needed
2109307Sandreas.hansson@arm.comneeded_files = set(['simout', 'simerr', 'stats.txt', 'config.ini'])
2119307Sandreas.hansson@arm.com# - source files we always want to ignore
2129307Sandreas.hansson@arm.comknown_ignores = set(['status', 'outdiff', 'statsdiff'])
2139307Sandreas.hansson@arm.com
2149307Sandreas.hansson@arm.comdef update_test(target, source, env):
2159307Sandreas.hansson@arm.com    """Update reference test outputs.
2169307Sandreas.hansson@arm.com
2179307Sandreas.hansson@arm.com    Target is phony.  First two sources are the ref & new stats.txt file
2189307Sandreas.hansson@arm.com    files, respectively.  We actually copy everything in the
2199307Sandreas.hansson@arm.com    respective directories except the status & diff output files.
2209307Sandreas.hansson@arm.com
2219307Sandreas.hansson@arm.com    """
2229307Sandreas.hansson@arm.com    dest_dir = str(source[0].get_dir())
2239307Sandreas.hansson@arm.com    src_dir = str(source[1].get_dir())
2249307Sandreas.hansson@arm.com    dest_files = set(os.listdir(dest_dir))
2259307Sandreas.hansson@arm.com    src_files = set(os.listdir(src_dir))
2264435SN/A    # Copy all of the required files plus any existing dest files.
2274435SN/A    wanted_files = needed_files | dest_files
2282384SN/A    missing_files = wanted_files - src_files
2294435SN/A    if len(missing_files) > 0:
2302384SN/A        print "  WARNING: the following file(s) are missing " \
2319166Sandreas.hansson@arm.com              "and will not be updated:"
2322901SN/A        print "    ", " ,".join(missing_files)
2332901SN/A    copy_files = wanted_files - missing_files
2344435SN/A    warn_ignored_files = (src_files - copy_files) - known_ignores
2352902SN/A    if len(warn_ignored_files) > 0:
2369524SAndreas.Sandberg@ARM.com        print ignore_msg,
2379307Sandreas.hansson@arm.com        print "       ", ", ".join(warn_ignored_files)
2389307Sandreas.hansson@arm.com    for f in copy_files:
2399307Sandreas.hansson@arm.com        if f in dest_files:
2409307Sandreas.hansson@arm.com            print "  Replacing file", f
2414435SN/A            dest_files.remove(f)
2424435SN/A        else:
2434435SN/A            print "  Creating new file", f
2449307Sandreas.hansson@arm.com        copyAction = Copy(os.path.join(dest_dir, f), os.path.join(src_dir, f))
2459524SAndreas.Sandberg@ARM.com        copyAction.strfunction = None
2469307Sandreas.hansson@arm.com        env.Execute(copyAction)
2479307Sandreas.hansson@arm.com    return 0
2489307Sandreas.hansson@arm.com
2499307Sandreas.hansson@arm.comdef update_test_string(target, source, env):
2504435SN/A    return env.subst("Updating ${SOURCES[0].dir} from ${SOURCES[1].dir}",
2519307Sandreas.hansson@arm.com                     target=target, source=source)
2529307Sandreas.hansson@arm.com
2539307Sandreas.hansson@arm.comupdateAction = env.Action(update_test, update_test_string)
2544435SN/A
2559307Sandreas.hansson@arm.comdef test_builder(env, ref_dir):
2569307Sandreas.hansson@arm.com    """Define a test."""
2579166Sandreas.hansson@arm.com
2589166Sandreas.hansson@arm.com    (category, mode, name, _ref, isa, opsys, config) = ref_dir.split('/')
259545SN/A    assert(_ref == 'ref')
2608598SN/A
2619294Sandreas.hansson@arm.com    # target path (where test output goes) is the same except without
2629294Sandreas.hansson@arm.com    # the 'ref' component
2638598SN/A    tgt_dir = os.path.join(category, mode, name, isa, opsys, config)
2648598SN/A
2658922SN/A    # prepend file name with tgt_dir
2668598SN/A    def tgt(f):
2678922SN/A        return os.path.join(tgt_dir, f)
2688598SN/A
26911010Sandreas.sandberg@arm.com    ref_stats = os.path.join(ref_dir, 'stats.txt')
27011010Sandreas.sandberg@arm.com    new_stats = tgt('stats.txt')
27111010Sandreas.sandberg@arm.com    status_file = tgt('status')
27211010Sandreas.sandberg@arm.com
27311010Sandreas.sandberg@arm.com    env.Command([status_file],
27411010Sandreas.sandberg@arm.com                [env.M5Binary, 'run.py', ref_stats],
27511010Sandreas.sandberg@arm.com                testAction)
27611010Sandreas.sandberg@arm.com
27711010Sandreas.sandberg@arm.com    # phony target to echo status
27811010Sandreas.sandberg@arm.com    if GetOption('update_ref'):
27911010Sandreas.sandberg@arm.com        p = env.Command(tgt('_update'),
28011010Sandreas.sandberg@arm.com                        [ref_stats, new_stats, status_file],
28111010Sandreas.sandberg@arm.com                        updateAction)
28211010Sandreas.sandberg@arm.com    else:
28311010Sandreas.sandberg@arm.com        p = env.Command(tgt('_print'), [status_file], printAction)
28411010Sandreas.sandberg@arm.com
28511010Sandreas.sandberg@arm.com    env.AlwaysBuild(p)
28611010Sandreas.sandberg@arm.com
28711010Sandreas.sandberg@arm.com
28811010Sandreas.sandberg@arm.com# Figure out applicable configs based on build type
28911010Sandreas.sandberg@arm.comconfigs = []
29011010Sandreas.sandberg@arm.comif env['TARGET_ISA'] == 'alpha':
29111010Sandreas.sandberg@arm.com    configs += ['tsunami-simple-atomic',
29211010Sandreas.sandberg@arm.com                'tsunami-simple-timing',
29311010Sandreas.sandberg@arm.com                'tsunami-simple-atomic-dual',
29411010Sandreas.sandberg@arm.com                'tsunami-simple-timing-dual',
29511010Sandreas.sandberg@arm.com                'twosys-tsunami-simple-atomic',
29611010Sandreas.sandberg@arm.com                'tsunami-o3', 'tsunami-o3-dual',
29711010Sandreas.sandberg@arm.com                'tsunami-inorder']
29811010Sandreas.sandberg@arm.comif env['TARGET_ISA'] == 'sparc':
29911010Sandreas.sandberg@arm.com    configs += ['t1000-simple-atomic',
30011010Sandreas.sandberg@arm.com                't1000-simple-timing']
30111010Sandreas.sandberg@arm.comif env['TARGET_ISA'] == 'arm':
30211010Sandreas.sandberg@arm.com    configs += ['simple-atomic-dummychecker',
30311010Sandreas.sandberg@arm.com                'o3-timing-checker',
30411010Sandreas.sandberg@arm.com                'realview-simple-atomic',
30511010Sandreas.sandberg@arm.com                'realview-simple-atomic-dual',
30611010Sandreas.sandberg@arm.com                'realview-simple-timing',
30711010Sandreas.sandberg@arm.com                'realview-simple-timing-dual',
30811010Sandreas.sandberg@arm.com                'realview-o3',
30911010Sandreas.sandberg@arm.com                'realview-o3-checker',
31011010Sandreas.sandberg@arm.com                'realview-o3-dual']
31111010Sandreas.sandberg@arm.comif env['TARGET_ISA'] == 'x86':
31211010Sandreas.sandberg@arm.com    configs += ['pc-simple-atomic',
31311010Sandreas.sandberg@arm.com                'pc-simple-timing',
31411010Sandreas.sandberg@arm.com                'pc-o3-timing']
31511010Sandreas.sandberg@arm.com
31611010Sandreas.sandberg@arm.comconfigs += ['simple-atomic', 'simple-timing', 'o3-timing', 'memtest',
31711010Sandreas.sandberg@arm.com            'simple-atomic-mp', 'simple-timing-mp', 'o3-timing-mp',
31811010Sandreas.sandberg@arm.com            'inorder-timing', 'rubytest', 'tgen-simple-mem']
31911010Sandreas.sandberg@arm.com
32011010Sandreas.sandberg@arm.comif env['PROTOCOL'] != 'None':
32111010Sandreas.sandberg@arm.com    if env['PROTOCOL'] == 'MI_example':
32211010Sandreas.sandberg@arm.com        configs += [c + "-ruby" for c in configs]
32311010Sandreas.sandberg@arm.com    else:
32411010Sandreas.sandberg@arm.com        configs = [c + "-ruby-" + env['PROTOCOL'] for c in configs]
32511010Sandreas.sandberg@arm.com
32611010Sandreas.sandberg@arm.comcwd = os.getcwd()
32711010Sandreas.sandberg@arm.comos.chdir(str(Dir('.').srcdir))
32811010Sandreas.sandberg@arm.comfor config in configs:
32911010Sandreas.sandberg@arm.com    dirs = glob.glob('*/*/*/ref/%s/*/%s' % (env['TARGET_ISA'], config))
33011010Sandreas.sandberg@arm.com    for d in dirs:
33111010Sandreas.sandberg@arm.com        if not os.path.exists(os.path.join(d, 'skip')):
33211010Sandreas.sandberg@arm.com            test_builder(env, d)
33311010Sandreas.sandberg@arm.comos.chdir(cwd)
33411010Sandreas.sandberg@arm.com