1# Copyright (c) 2012 ARM Limited
2# All rights reserved
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2006-2007 The Regents of The University of Michigan
14# All rights reserved.
15#
16# Redistribution and use in source and binary forms, with or without
17# modification, are permitted provided that the following conditions are
18# met: redistributions of source code must retain the above copyright
19# notice, this list of conditions and the following disclaimer;
20# redistributions in binary form must reproduce the above copyright
21# notice, this list of conditions and the following disclaimer in the
22# documentation and/or other materials provided with the distribution;
23# neither the name of the copyright holders nor the names of its
24# contributors may be used to endorse or promote products derived from
25# this software without specific prior written permission.
26#
27# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38#
39# Authors: Steve Reinhardt
40
41from __future__ import print_function
42
43import os
44import sys
45import re
46import string
47
48from os.path import join as joinpath
49import os.path
50import os
51
52import m5
53
54def skip_test(reason=""):
55    """Signal that a test should be skipped and optionally print why.
56
57    Keyword arguments:
58      reason -- Reason why the test failed. Output is omitted if empty.
59    """
60
61    if reason:
62        print("Skipping test: %s" % reason)
63    sys.exit(2)
64
65def has_sim_object(name):
66    """Test if a SimObject exists in the simulator.
67
68    Arguments:
69      name -- Name of SimObject (string)
70
71    Returns: True if the object exists, False otherwise.
72    """
73
74    try:
75        cls = getattr(m5.objects, name)
76        return issubclass(cls, m5.objects.SimObject)
77    except AttributeError:
78        return False
79
80def require_sim_object(name, fatal=False):
81    """Test if a SimObject exists and abort/skip test if not.
82
83    Arguments:
84      name -- Name of SimObject (string)
85
86    Keyword arguments:
87      fatal -- Set to True to indicate that the test should fail
88               instead of being skipped.
89    """
90
91    if has_sim_object(name):
92        return
93    else:
94        msg = "Test requires the '%s' SimObject." % name
95        if fatal:
96            m5.fatal(msg)
97        else:
98            skip_test(msg)
99
100
101def require_file(path, fatal=False, mode=os.F_OK):
102    """Test if a file exists and abort/skip test if not.
103
104    Arguments:
105      path -- File to test for.
106
107    Keyword arguments:
108      fatal -- Set to True to indicate that the test should fail
109               instead of being skipped.
110      modes -- Mode to test for, default to existence. See the
111               Python documentation for os.access().
112    """
113
114    if os.access(path, mode):
115        return
116    else:
117        msg = "Test requires '%s'" % path
118        if not os.path.exists(path):
119            msg += " which does not exist."
120        else:
121            msg += " which has incorrect permissions."
122
123        if fatal:
124            m5.fatal(msg)
125        else:
126            skip_test(msg)
127
128def require_kvm(kvm_dev="/dev/kvm", fatal=False):
129    """Test if KVM is available.
130
131    Keyword arguments:
132      kvm_dev -- Device to test (normally /dev/kvm)
133      fatal -- Set to True to indicate that the test should fail
134               instead of being skipped.
135    """
136
137    require_sim_object("BaseKvmCPU", fatal=fatal)
138    require_file(kvm_dev, fatal=fatal, mode=os.R_OK | os.W_OK)
139
140def run_test(root):
141    """Default run_test implementations. Scripts can override it."""
142
143    # instantiate configuration
144    m5.instantiate()
145
146    # simulate until program terminates
147    exit_event = m5.simulate(maxtick)
148    print('Exiting @ tick', m5.curTick(), 'because', exit_event.getCause())
149
150# Since we're in batch mode, dont allow tcp socket connections
151m5.disableAllListeners()
152
153# single "path" arg encodes everything we need to know about test
154(category, mode, name, isa, opsys, config) = sys.argv[1].split('/')[-6:]
155
156# find path to directory containing this file
157tests_root = os.path.dirname(__file__)
158test_progs = os.environ.get('M5_TEST_PROGS', '/dist/m5/regression/test-progs')
159if not os.path.isdir(test_progs):
160    test_progs = joinpath(tests_root, 'test-progs')
161
162# generate path to binary file
163def binpath(app, file=None):
164    # executable has same name as app unless specified otherwise
165    if not file:
166        file = app
167    return joinpath(test_progs, app, 'bin', isa, opsys, file)
168
169# generate path to input file
170def inputpath(app, file=None):
171    # input file has same name as app unless specified otherwise
172    if not file:
173        file = app
174    return joinpath(test_progs, app, 'input', file)
175
176def srcpath(path):
177    """Path to file in gem5's source tree"""
178    return joinpath(os.path.dirname(__file__), "..", path)
179
180def run_config(config, argv=None):
181    """Execute a configuration script that is external to the test system"""
182
183    src_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
184    abs_path = joinpath(src_root, config)
185
186    code = compile(open(abs_path, 'r').read(), abs_path, 'exec')
187    scope = {
188        '__file__' : config,
189        '__name__' : '__m5_main__',
190    }
191
192    # Set the working directory in case we are executing from
193    # outside gem5's source tree
194    os.chdir(src_root)
195
196    # gem5 normally adds the script's directory to the path to make
197    # script-relative imports work.
198    sys.path = [ os.path.dirname(abs_path), ] + sys.path
199
200    if argv is None:
201        sys.argv = [ config, ]
202    else:
203        sys.argv = argv
204    exec(code, scope)
205
206# build configuration
207sys.path.append(joinpath(tests_root, 'configs'))
208test_filename = config
209# for ruby configurations, remove the protocol name from the test filename
210if re.search('-ruby', test_filename):
211    test_filename = test_filename.split('-ruby')[0]+'-ruby'
212exec(compile( \
213    open(joinpath(tests_root, 'configs', test_filename + '.py')).read(), \
214    joinpath(tests_root, 'configs', test_filename + '.py'), 'exec'))
215
216# set default maxtick... script can override
217# -1 means run forever
218maxtick = m5.MaxTick
219
220# tweak configuration for specific test
221sys.path.append(joinpath(tests_root, category, mode, name))
222exec(compile( \
223    open(joinpath(tests_root, category, mode, name, 'test.py')).read(), \
224    joinpath(tests_root, category, mode, name, 'test.py'), 'exec'))
225
226# Initialize all CPUs in a system
227def initCPUs(sys):
228    def initCPU(cpu):
229        # We might actually have a MemTest object or something similar
230        # here that just pretends to be a CPU.
231        try:
232            cpu.createThreads()
233        except:
234            pass
235
236    # The CPU attribute doesn't exist in some cases, e.g. the Ruby
237    # testers.
238    if not hasattr(sys, "cpu"):
239        return
240
241    # The CPU can either be a list of CPUs or a single object.
242    if isinstance(sys.cpu, list):
243        [ initCPU(cpu) for cpu in sys.cpu ]
244    else:
245        initCPU(sys.cpu)
246
247# We might be creating a single system or a dual system. Try
248# initializing the CPUs in all known system attributes.
249for sysattr in [ "system", "testsys", "drivesys" ]:
250    if hasattr(root, sysattr):
251        initCPUs(getattr(root, sysattr))
252
253run_test(root)
254