114140Snikos.nikoleris@arm.com# Copyright (c) 2019 ARM Limited 214140Snikos.nikoleris@arm.com# All rights reserved 314140Snikos.nikoleris@arm.com# 414140Snikos.nikoleris@arm.com# The license below extends only to copyright in the software and shall 514140Snikos.nikoleris@arm.com# not be construed as granting a license to any other intellectual 614140Snikos.nikoleris@arm.com# property including but not limited to intellectual property relating 714140Snikos.nikoleris@arm.com# to a hardware implementation of the functionality of the software 814140Snikos.nikoleris@arm.com# licensed hereunder. You may use the software subject to the license 914140Snikos.nikoleris@arm.com# terms below provided that you ensure that this notice is replicated 1014140Snikos.nikoleris@arm.com# unmodified and in its entirety in all distributions of the software, 1114140Snikos.nikoleris@arm.com# modified or unmodified, in source code or in binary form. 1214140Snikos.nikoleris@arm.com# 1312882Sspwilson2@wisc.edu# Copyright (c) 2017 Mark D. Hill and David A. Wood 1412882Sspwilson2@wisc.edu# All rights reserved. 1512882Sspwilson2@wisc.edu# 1612882Sspwilson2@wisc.edu# Redistribution and use in source and binary forms, with or without 1712882Sspwilson2@wisc.edu# modification, are permitted provided that the following conditions are 1812882Sspwilson2@wisc.edu# met: redistributions of source code must retain the above copyright 1912882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer; 2012882Sspwilson2@wisc.edu# redistributions in binary form must reproduce the above copyright 2112882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer in the 2212882Sspwilson2@wisc.edu# documentation and/or other materials provided with the distribution; 2312882Sspwilson2@wisc.edu# neither the name of the copyright holders nor the names of its 2412882Sspwilson2@wisc.edu# contributors may be used to endorse or promote products derived from 2512882Sspwilson2@wisc.edu# this software without specific prior written permission. 2612882Sspwilson2@wisc.edu# 2712882Sspwilson2@wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2812882Sspwilson2@wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2912882Sspwilson2@wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3012882Sspwilson2@wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3112882Sspwilson2@wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3212882Sspwilson2@wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3312882Sspwilson2@wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3412882Sspwilson2@wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3512882Sspwilson2@wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3612882Sspwilson2@wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3712882Sspwilson2@wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3812882Sspwilson2@wisc.edu# 3912882Sspwilson2@wisc.edu# Authors: Sean Wilson 4014142Snikos.nikoleris@arm.com# Nikos Nikoleris 4112882Sspwilson2@wisc.edu 4212882Sspwilson2@wisc.eduimport os 4312882Sspwilson2@wisc.eduimport tempfile 4412882Sspwilson2@wisc.eduimport shutil 4514140Snikos.nikoleris@arm.comimport threading 4614142Snikos.nikoleris@arm.comimport urllib 4714142Snikos.nikoleris@arm.comimport urllib2 4812882Sspwilson2@wisc.edu 4914141Snikos.nikoleris@arm.comfrom testlib.fixture import Fixture 5012882Sspwilson2@wisc.edufrom testlib.config import config, constants 5112882Sspwilson2@wisc.edufrom testlib.helper import log_call, cacheresult, joinpath, absdirpath 5212882Sspwilson2@wisc.eduimport testlib.log as log 5312882Sspwilson2@wisc.edu 5412882Sspwilson2@wisc.edu 5512882Sspwilson2@wisc.educlass VariableFixture(Fixture): 5612882Sspwilson2@wisc.edu def __init__(self, value=None, name=None): 5712882Sspwilson2@wisc.edu super(VariableFixture, self).__init__(name=name) 5812882Sspwilson2@wisc.edu self.value = value 5912882Sspwilson2@wisc.edu 6012882Sspwilson2@wisc.edu 6112882Sspwilson2@wisc.educlass TempdirFixture(Fixture): 6212882Sspwilson2@wisc.edu def __init__(self): 6312882Sspwilson2@wisc.edu self.path = None 6412882Sspwilson2@wisc.edu super(TempdirFixture, self).__init__( 6512882Sspwilson2@wisc.edu name=constants.tempdir_fixture_name) 6612882Sspwilson2@wisc.edu 6712882Sspwilson2@wisc.edu def setup(self, testitem): 6812882Sspwilson2@wisc.edu self.path = tempfile.mkdtemp(prefix='gem5out') 6912882Sspwilson2@wisc.edu 7012882Sspwilson2@wisc.edu def teardown(self, testitem): 7112882Sspwilson2@wisc.edu if self.path is not None: 7212882Sspwilson2@wisc.edu shutil.rmtree(self.path) 7312882Sspwilson2@wisc.edu 7413790Sjason@lowepower.com def skip_cleanup(self): 7513790Sjason@lowepower.com # Set path to none so it's not deleted 7613790Sjason@lowepower.com self.path = None 7713790Sjason@lowepower.com 7814140Snikos.nikoleris@arm.comclass UniqueFixture(Fixture): 7914140Snikos.nikoleris@arm.com ''' 8014140Snikos.nikoleris@arm.com Base class for fixtures that generate a target in the 8114140Snikos.nikoleris@arm.com filesystem. If the same fixture is used by more than one 8214140Snikos.nikoleris@arm.com test/suite, rather than creating a copy of the fixture, it returns 8314140Snikos.nikoleris@arm.com the same object and makes sure that setup is only executed 8414140Snikos.nikoleris@arm.com once. Devired classses should override the _init and _setup 8514140Snikos.nikoleris@arm.com functions. 8614140Snikos.nikoleris@arm.com 8714140Snikos.nikoleris@arm.com :param target: The absolute path of the target in the filesystem. 8814140Snikos.nikoleris@arm.com 8914140Snikos.nikoleris@arm.com ''' 9014140Snikos.nikoleris@arm.com fixtures = {} 9114140Snikos.nikoleris@arm.com 9214140Snikos.nikoleris@arm.com def __new__(cls, target): 9314140Snikos.nikoleris@arm.com if target in cls.fixtures: 9414140Snikos.nikoleris@arm.com obj = cls.fixtures[target] 9514140Snikos.nikoleris@arm.com else: 9614140Snikos.nikoleris@arm.com obj = super(UniqueFixture, cls).__new__(cls) 9714140Snikos.nikoleris@arm.com obj.lock = threading.Lock() 9814140Snikos.nikoleris@arm.com obj.target = target 9914140Snikos.nikoleris@arm.com cls.fixtures[target] = obj 10014140Snikos.nikoleris@arm.com return obj 10114140Snikos.nikoleris@arm.com 10214140Snikos.nikoleris@arm.com def __init__(self, *args, **kwargs): 10314140Snikos.nikoleris@arm.com with self.lock: 10414140Snikos.nikoleris@arm.com if hasattr(self, '_init_done'): 10514140Snikos.nikoleris@arm.com return 10614140Snikos.nikoleris@arm.com super(UniqueFixture, self).__init__(self, **kwargs) 10714140Snikos.nikoleris@arm.com self._init(*args, **kwargs) 10814140Snikos.nikoleris@arm.com self._init_done = True 10914140Snikos.nikoleris@arm.com 11014140Snikos.nikoleris@arm.com def setup(self, testitem): 11114140Snikos.nikoleris@arm.com with self.lock: 11214140Snikos.nikoleris@arm.com if hasattr(self, '_setup_done'): 11314140Snikos.nikoleris@arm.com return 11414140Snikos.nikoleris@arm.com self._setup_done = True 11514140Snikos.nikoleris@arm.com self._setup(testitem) 11614140Snikos.nikoleris@arm.com 11712882Sspwilson2@wisc.edu 11814141Snikos.nikoleris@arm.comclass SConsFixture(UniqueFixture): 11912882Sspwilson2@wisc.edu ''' 12012882Sspwilson2@wisc.edu Fixture will wait until all SCons targets are collected and tests are 12112882Sspwilson2@wisc.edu about to be ran, then will invocate a single instance of SCons for all 12212882Sspwilson2@wisc.edu targets. 12312882Sspwilson2@wisc.edu 12412882Sspwilson2@wisc.edu :param directory: The directory which scons will -C (cd) into before 12512882Sspwilson2@wisc.edu executing. If None is provided, will choose the config base_dir. 12612882Sspwilson2@wisc.edu ''' 12712882Sspwilson2@wisc.edu 12814141Snikos.nikoleris@arm.com def __new__(cls, target): 12914141Snikos.nikoleris@arm.com obj = super(SConsFixture, cls).__new__(cls, target) 13014141Snikos.nikoleris@arm.com return obj 13114141Snikos.nikoleris@arm.com 13214141Snikos.nikoleris@arm.com def _setup(self, testitem): 13312882Sspwilson2@wisc.edu if config.skip_build: 13412882Sspwilson2@wisc.edu return 13512882Sspwilson2@wisc.edu 13612882Sspwilson2@wisc.edu command = [ 13712882Sspwilson2@wisc.edu 'scons', '-C', self.directory, 13814141Snikos.nikoleris@arm.com '-j', str(config.threads), 13912882Sspwilson2@wisc.edu '--ignore-style' 14012882Sspwilson2@wisc.edu ] 14112882Sspwilson2@wisc.edu 14212882Sspwilson2@wisc.edu if not self.targets: 14312882Sspwilson2@wisc.edu log.test_log.warn( 14412882Sspwilson2@wisc.edu 'No SCons targets specified, this will' 14512882Sspwilson2@wisc.edu ' build the default all target.\n' 14612882Sspwilson2@wisc.edu 'This is likely unintended, and you' 14712882Sspwilson2@wisc.edu ' may wish to kill testlib and reconfigure.') 14812882Sspwilson2@wisc.edu else: 14912882Sspwilson2@wisc.edu log.test_log.message( 15012882Sspwilson2@wisc.edu 'Building the following targets.' 15112882Sspwilson2@wisc.edu ' This may take a while.') 15212882Sspwilson2@wisc.edu log.test_log.message('%s' % (', '.join(self.targets))) 15312882Sspwilson2@wisc.edu log.test_log.message( 15412882Sspwilson2@wisc.edu "You may want to run with only a single ISA" 15512882Sspwilson2@wisc.edu "(--isa=), use --skip-build, or use 'rerun'.") 15612882Sspwilson2@wisc.edu 15712882Sspwilson2@wisc.edu command.extend(self.targets) 15813851Sjason@lowepower.com if self.options: 15913851Sjason@lowepower.com command.extend(self.options) 16012882Sspwilson2@wisc.edu log_call(log.test_log, command) 16112882Sspwilson2@wisc.edu 16214141Snikos.nikoleris@arm.comclass Gem5Fixture(SConsFixture): 16314141Snikos.nikoleris@arm.com def __new__(cls, isa, variant, protocol=None): 16414141Snikos.nikoleris@arm.com target_dir = joinpath(config.build_dir, isa.upper()) 16514141Snikos.nikoleris@arm.com if protocol: 16614141Snikos.nikoleris@arm.com target_dir += '_' + protocol 16714141Snikos.nikoleris@arm.com target = joinpath(target_dir, 'gem5.%s' % variant) 16814141Snikos.nikoleris@arm.com obj = super(Gem5Fixture, cls).__new__(cls, target) 16914141Snikos.nikoleris@arm.com return obj 17012882Sspwilson2@wisc.edu 17114141Snikos.nikoleris@arm.com def _init(self, isa, variant, protocol=None): 17214141Snikos.nikoleris@arm.com self.name = constants.gem5_binary_fixture_name 17312882Sspwilson2@wisc.edu 17414141Snikos.nikoleris@arm.com self.targets = [self.target] 17514141Snikos.nikoleris@arm.com self.path = self.target 17614141Snikos.nikoleris@arm.com self.directory = config.base_dir 17712882Sspwilson2@wisc.edu 17814141Snikos.nikoleris@arm.com self.options = [] 17913851Sjason@lowepower.com if protocol: 18014141Snikos.nikoleris@arm.com self.options = [ '--default=' + isa.upper(), 18114141Snikos.nikoleris@arm.com 'PROTOCOL=' + protocol ] 18214141Snikos.nikoleris@arm.com self.set_global() 18312882Sspwilson2@wisc.edu 18412882Sspwilson2@wisc.educlass MakeFixture(Fixture): 18512882Sspwilson2@wisc.edu def __init__(self, directory, *args, **kwargs): 18612882Sspwilson2@wisc.edu name = 'make -C %s' % directory 18712882Sspwilson2@wisc.edu super(MakeFixture, self).__init__(build_once=True, lazy_init=False, 18812882Sspwilson2@wisc.edu name=name, 18912882Sspwilson2@wisc.edu *args, **kwargs) 19012882Sspwilson2@wisc.edu self.targets = [] 19112882Sspwilson2@wisc.edu self.directory = directory 19212882Sspwilson2@wisc.edu 19312882Sspwilson2@wisc.edu def setup(self): 19412882Sspwilson2@wisc.edu super(MakeFixture, self).setup() 19512882Sspwilson2@wisc.edu targets = set(self.required_by) 19612882Sspwilson2@wisc.edu command = ['make', '-C', self.directory] 19712882Sspwilson2@wisc.edu command.extend([target.target for target in targets]) 19812882Sspwilson2@wisc.edu log_call(command) 19912882Sspwilson2@wisc.edu 20012882Sspwilson2@wisc.edu 20112882Sspwilson2@wisc.educlass MakeTarget(Fixture): 20212882Sspwilson2@wisc.edu def __init__(self, target, make_fixture=None, *args, **kwargs): 20312882Sspwilson2@wisc.edu ''' 20412882Sspwilson2@wisc.edu :param make_fixture: The make invocation we will be attached to. 20512882Sspwilson2@wisc.edu Since we don't have a single global instance of make in gem5 like we do 20612882Sspwilson2@wisc.edu scons we need to know what invocation to attach to. If none given, 20712882Sspwilson2@wisc.edu creates its own. 20812882Sspwilson2@wisc.edu ''' 20912882Sspwilson2@wisc.edu super(MakeTarget, self).__init__(name=target, *args, **kwargs) 21012882Sspwilson2@wisc.edu self.target = self.name 21112882Sspwilson2@wisc.edu 21212882Sspwilson2@wisc.edu if make_fixture is None: 21312882Sspwilson2@wisc.edu make_fixture = MakeFixture( 21412882Sspwilson2@wisc.edu absdirpath(target), 21512882Sspwilson2@wisc.edu lazy_init=True, 21612882Sspwilson2@wisc.edu build_once=False) 21712882Sspwilson2@wisc.edu 21812882Sspwilson2@wisc.edu self.make_fixture = make_fixture 21912882Sspwilson2@wisc.edu 22012882Sspwilson2@wisc.edu # Add our self to the required targets of the main MakeFixture 22112882Sspwilson2@wisc.edu self.require(self.make_fixture) 22212882Sspwilson2@wisc.edu 22312882Sspwilson2@wisc.edu def setup(self, testitem): 22412882Sspwilson2@wisc.edu super(MakeTarget, self).setup() 22512882Sspwilson2@wisc.edu self.make_fixture.setup() 22612882Sspwilson2@wisc.edu return self 22712882Sspwilson2@wisc.edu 22812882Sspwilson2@wisc.educlass TestProgram(MakeTarget): 22912882Sspwilson2@wisc.edu def __init__(self, program, isa, os, recompile=False): 23012882Sspwilson2@wisc.edu make_dir = joinpath('test-progs', program) 23112882Sspwilson2@wisc.edu make_fixture = MakeFixture(make_dir) 23212882Sspwilson2@wisc.edu target = joinpath('bin', isa, os, program) 23312882Sspwilson2@wisc.edu super(TestProgram, self).__init__(target, make_fixture) 23412882Sspwilson2@wisc.edu self.path = joinpath(make_dir, target) 23512882Sspwilson2@wisc.edu self.recompile = recompile 23612882Sspwilson2@wisc.edu 23712882Sspwilson2@wisc.edu def setup(self, testitem): 23812882Sspwilson2@wisc.edu # Check if the program exists if it does then only compile if 23912882Sspwilson2@wisc.edu # recompile was given. 24012882Sspwilson2@wisc.edu if self.recompile: 24112882Sspwilson2@wisc.edu super(MakeTarget, self).setup() 24212882Sspwilson2@wisc.edu elif not os.path.exists(self.path): 24312882Sspwilson2@wisc.edu super(MakeTarget, self).setup() 24412882Sspwilson2@wisc.edu 24514142Snikos.nikoleris@arm.comclass DownloadedProgram(UniqueFixture): 24612882Sspwilson2@wisc.edu """ Like TestProgram, but checks the version in the gem5 binary repository 24712882Sspwilson2@wisc.edu and downloads an updated version if it is needed. 24812882Sspwilson2@wisc.edu """ 24912882Sspwilson2@wisc.edu 25014142Snikos.nikoleris@arm.com def __new__(cls, url, path, filename): 25114142Snikos.nikoleris@arm.com target = joinpath(path, filename) 25214142Snikos.nikoleris@arm.com return super(DownloadedProgram, cls).__new__(cls, target) 25314142Snikos.nikoleris@arm.com 25414142Snikos.nikoleris@arm.com def _init(self, url, path, filename, **kwargs): 25513793Sjason@lowepower.com """ 25614142Snikos.nikoleris@arm.com url: string 25714142Snikos.nikoleris@arm.com The url of the archive 25813793Sjason@lowepower.com path: string 25914142Snikos.nikoleris@arm.com The absolute path of the directory containing the archive 26014142Snikos.nikoleris@arm.com filename: string 26114142Snikos.nikoleris@arm.com The name of the archive 26213793Sjason@lowepower.com """ 26312882Sspwilson2@wisc.edu 26414142Snikos.nikoleris@arm.com self.url = url 26514142Snikos.nikoleris@arm.com self.path = path 26614142Snikos.nikoleris@arm.com self.filename = joinpath(path, filename) 26714142Snikos.nikoleris@arm.com self.name = "Downloaded:" + self.filename 26813793Sjason@lowepower.com 26912882Sspwilson2@wisc.edu def _download(self): 27013792Sjason@lowepower.com import errno 27112882Sspwilson2@wisc.edu log.test_log.debug("Downloading " + self.url + " to " + self.path) 27214142Snikos.nikoleris@arm.com if not os.path.exists(self.path): 27313792Sjason@lowepower.com try: 27414142Snikos.nikoleris@arm.com os.makedirs(self.path) 27513792Sjason@lowepower.com except OSError as e: 27613792Sjason@lowepower.com if e.errno != errno.EEXIST: 27713792Sjason@lowepower.com raise 27814142Snikos.nikoleris@arm.com urllib.urlretrieve(self.url, self.filename) 27912882Sspwilson2@wisc.edu 28012882Sspwilson2@wisc.edu def _getremotetime(self): 28114142Snikos.nikoleris@arm.com import datetime, time 28212882Sspwilson2@wisc.edu import _strptime # Needed for python threading bug 28312882Sspwilson2@wisc.edu 28412882Sspwilson2@wisc.edu u = urllib2.urlopen(self.url) 28512882Sspwilson2@wisc.edu return time.mktime(datetime.datetime.strptime( \ 28612882Sspwilson2@wisc.edu u.info().getheaders("Last-Modified")[0], 28712882Sspwilson2@wisc.edu "%a, %d %b %Y %X GMT").timetuple()) 28812882Sspwilson2@wisc.edu 28914142Snikos.nikoleris@arm.com def _setup(self, testitem): 29012882Sspwilson2@wisc.edu # Check to see if there is a file downloaded 29114142Snikos.nikoleris@arm.com if not os.path.exists(self.filename): 29212882Sspwilson2@wisc.edu self._download() 29312882Sspwilson2@wisc.edu else: 29412882Sspwilson2@wisc.edu try: 29512882Sspwilson2@wisc.edu t = self._getremotetime() 29612882Sspwilson2@wisc.edu except urllib2.URLError: 29712882Sspwilson2@wisc.edu # Problem checking the server, use the old files. 29814142Snikos.nikoleris@arm.com log.test_log.debug("Could not contact server. Binaries may be old.") 29912882Sspwilson2@wisc.edu return 30012882Sspwilson2@wisc.edu # If the server version is more recent, download it 30114142Snikos.nikoleris@arm.com if t > os.path.getmtime(self.filename): 30212882Sspwilson2@wisc.edu self._download() 30314142Snikos.nikoleris@arm.com 30414142Snikos.nikoleris@arm.comclass DownloadedArchive(DownloadedProgram): 30514142Snikos.nikoleris@arm.com """ Like TestProgram, but checks the version in the gem5 binary repository 30614142Snikos.nikoleris@arm.com and downloads an updated version if it is needed. 30714142Snikos.nikoleris@arm.com """ 30814142Snikos.nikoleris@arm.com 30914142Snikos.nikoleris@arm.com def _extract(self): 31014142Snikos.nikoleris@arm.com import tarfile 31114142Snikos.nikoleris@arm.com with tarfile.open(self.filename) as tf: 31214142Snikos.nikoleris@arm.com tf.extractall(self.path) 31314142Snikos.nikoleris@arm.com 31414142Snikos.nikoleris@arm.com def _setup(self, testitem): 31514142Snikos.nikoleris@arm.com # Check to see if there is a file downloaded 31614142Snikos.nikoleris@arm.com if not os.path.exists(self.filename): 31714142Snikos.nikoleris@arm.com self._download() 31814142Snikos.nikoleris@arm.com self._extract() 31914142Snikos.nikoleris@arm.com else: 32014142Snikos.nikoleris@arm.com try: 32114142Snikos.nikoleris@arm.com t = self._getremotetime() 32214142Snikos.nikoleris@arm.com except urllib2.URLError: 32314142Snikos.nikoleris@arm.com # Problem checking the server, use the old files. 32414142Snikos.nikoleris@arm.com log.test_log.debug("Could not contact server. Binaries may be old.") 32514142Snikos.nikoleris@arm.com return 32614142Snikos.nikoleris@arm.com # If the server version is more recent, download it 32714142Snikos.nikoleris@arm.com if t > os.path.getmtime(self.filename): 32814142Snikos.nikoleris@arm.com self._download() 32914142Snikos.nikoleris@arm.com self._extract() 330