tests.py revision 13540
16691Stjones1@inf.ed.ac.uk#!/usr/bin/env python2.7
26691Stjones1@inf.ed.ac.uk#
36691Stjones1@inf.ed.ac.uk# Copyright (c) 2016-2017 ARM Limited
46691Stjones1@inf.ed.ac.uk# All rights reserved
56691Stjones1@inf.ed.ac.uk#
66691Stjones1@inf.ed.ac.uk# The license below extends only to copyright in the software and shall
76691Stjones1@inf.ed.ac.uk# not be construed as granting a license to any other intellectual
86691Stjones1@inf.ed.ac.uk# property including but not limited to intellectual property relating
96691Stjones1@inf.ed.ac.uk# to a hardware implementation of the functionality of the software
106691Stjones1@inf.ed.ac.uk# licensed hereunder.  You may use the software subject to the license
116691Stjones1@inf.ed.ac.uk# terms below provided that you ensure that this notice is replicated
126691Stjones1@inf.ed.ac.uk# unmodified and in its entirety in all distributions of the software,
136691Stjones1@inf.ed.ac.uk# modified or unmodified, in source code or in binary form.
146691Stjones1@inf.ed.ac.uk#
156691Stjones1@inf.ed.ac.uk# Redistribution and use in source and binary forms, with or without
166691Stjones1@inf.ed.ac.uk# modification, are permitted provided that the following conditions are
176691Stjones1@inf.ed.ac.uk# met: redistributions of source code must retain the above copyright
186691Stjones1@inf.ed.ac.uk# notice, this list of conditions and the following disclaimer;
196691Stjones1@inf.ed.ac.uk# redistributions in binary form must reproduce the above copyright
206691Stjones1@inf.ed.ac.uk# notice, this list of conditions and the following disclaimer in the
216691Stjones1@inf.ed.ac.uk# documentation and/or other materials provided with the distribution;
226691Stjones1@inf.ed.ac.uk# neither the name of the copyright holders nor the names of its
236691Stjones1@inf.ed.ac.uk# contributors may be used to endorse or promote products derived from
246691Stjones1@inf.ed.ac.uk# this software without specific prior written permission.
256691Stjones1@inf.ed.ac.uk#
266691Stjones1@inf.ed.ac.uk# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
276691Stjones1@inf.ed.ac.uk# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
286691Stjones1@inf.ed.ac.uk# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
296691Stjones1@inf.ed.ac.uk# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
306691Stjones1@inf.ed.ac.uk# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
316691Stjones1@inf.ed.ac.uk# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
326691Stjones1@inf.ed.ac.uk# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
336691Stjones1@inf.ed.ac.uk# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
346691Stjones1@inf.ed.ac.uk# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
356691Stjones1@inf.ed.ac.uk# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
366691Stjones1@inf.ed.ac.uk# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
376691Stjones1@inf.ed.ac.uk#
386691Stjones1@inf.ed.ac.uk# Authors: Andreas Sandberg
396691Stjones1@inf.ed.ac.uk
406691Stjones1@inf.ed.ac.ukfrom abc import ABCMeta, abstractmethod
416691Stjones1@inf.ed.ac.ukimport os
426691Stjones1@inf.ed.ac.ukfrom collections import namedtuple
436691Stjones1@inf.ed.ac.ukfrom units import *
446691Stjones1@inf.ed.ac.ukfrom results import TestResult
456691Stjones1@inf.ed.ac.ukimport shutil
466691Stjones1@inf.ed.ac.uk
476691Stjones1@inf.ed.ac.uk_test_base = os.path.join(os.path.dirname(__file__), "..")
486691Stjones1@inf.ed.ac.uk
496691Stjones1@inf.ed.ac.ukClassicConfig = namedtuple("ClassicConfig", (
506691Stjones1@inf.ed.ac.uk    "category",
516691Stjones1@inf.ed.ac.uk    "mode",
526691Stjones1@inf.ed.ac.uk    "workload",
536691Stjones1@inf.ed.ac.uk    "isa",
546691Stjones1@inf.ed.ac.uk    "os",
556691Stjones1@inf.ed.ac.uk    "config",
566691Stjones1@inf.ed.ac.uk))
576691Stjones1@inf.ed.ac.uk
586691Stjones1@inf.ed.ac.uk# There are currently two "classes" of test
596691Stjones1@inf.ed.ac.uk# configurations. Architecture-specific ones and generic ones
606691Stjones1@inf.ed.ac.uk# (typically SE mode tests). In both cases, the configuration name
616691Stjones1@inf.ed.ac.uk# matches a file in tests/configs/ that will be picked up by the test
626691Stjones1@inf.ed.ac.uk# runner (run.py).
636691Stjones1@inf.ed.ac.uk#
646691Stjones1@inf.ed.ac.uk# Architecture specific configurations are listed in the arch_configs
656691Stjones1@inf.ed.ac.uk# dictionary. This is indexed by a (cpu architecture, gpu
666691Stjones1@inf.ed.ac.uk# architecture) tuple. GPU architecture is optional and may be None.
676691Stjones1@inf.ed.ac.uk#
686691Stjones1@inf.ed.ac.uk# Generic configurations are listed in the generic_configs tuple.
696691Stjones1@inf.ed.ac.uk#
706691Stjones1@inf.ed.ac.uk# When discovering available test cases, this script look uses the
716691Stjones1@inf.ed.ac.uk# test list as a list of /candidate/ configurations. A configuration
726691Stjones1@inf.ed.ac.uk# is only used if a test has a reference output for that
736691Stjones1@inf.ed.ac.uk# configuration. In addition to the base configurations from
746691Stjones1@inf.ed.ac.uk# arch_configs and generic_configs, a Ruby configuration may be
756691Stjones1@inf.ed.ac.uk# appended to the base name (this is probed /in addition/ to the
766691Stjones1@inf.ed.ac.uk# original name. See get_tests() for details.
776691Stjones1@inf.ed.ac.uk#
786691Stjones1@inf.ed.ac.ukarch_configs = {
796691Stjones1@inf.ed.ac.uk    ("alpha", None) : (
806691Stjones1@inf.ed.ac.uk        'tsunami-simple-atomic',
816691Stjones1@inf.ed.ac.uk        'tsunami-simple-timing',
826691Stjones1@inf.ed.ac.uk        'tsunami-simple-atomic-dual',
836691Stjones1@inf.ed.ac.uk        'tsunami-simple-timing-dual',
846691Stjones1@inf.ed.ac.uk        'twosys-tsunami-simple-atomic',
856691Stjones1@inf.ed.ac.uk        'tsunami-o3', 'tsunami-o3-dual',
866691Stjones1@inf.ed.ac.uk        'tsunami-minor', 'tsunami-minor-dual',
876691Stjones1@inf.ed.ac.uk        'tsunami-switcheroo-full',
886691Stjones1@inf.ed.ac.uk    ),
896691Stjones1@inf.ed.ac.uk
906691Stjones1@inf.ed.ac.uk    ("arm", None) : (
916691Stjones1@inf.ed.ac.uk        'simple-atomic-dummychecker',
926691Stjones1@inf.ed.ac.uk        'o3-timing-checker',
936691Stjones1@inf.ed.ac.uk        'realview-simple-atomic',
946691Stjones1@inf.ed.ac.uk        'realview-simple-atomic-dual',
956691Stjones1@inf.ed.ac.uk        'realview-simple-atomic-checkpoint',
966691Stjones1@inf.ed.ac.uk        'realview-simple-timing',
976691Stjones1@inf.ed.ac.uk        'realview-simple-timing-dual',
986691Stjones1@inf.ed.ac.uk        'realview-o3',
996691Stjones1@inf.ed.ac.uk        'realview-o3-checker',
1006691Stjones1@inf.ed.ac.uk        'realview-o3-dual',
1016691Stjones1@inf.ed.ac.uk        'realview-minor',
1026691Stjones1@inf.ed.ac.uk        'realview-minor-dual',
1036691Stjones1@inf.ed.ac.uk        'realview-switcheroo-atomic',
1046691Stjones1@inf.ed.ac.uk        'realview-switcheroo-timing',
1056691Stjones1@inf.ed.ac.uk        'realview-switcheroo-noncaching-timing',
1066691Stjones1@inf.ed.ac.uk        'realview-switcheroo-o3',
1076691Stjones1@inf.ed.ac.uk        'realview-switcheroo-full',
1086691Stjones1@inf.ed.ac.uk        'realview64-simple-atomic',
1096691Stjones1@inf.ed.ac.uk        'realview64-simple-atomic-checkpoint',
1106691Stjones1@inf.ed.ac.uk        'realview64-simple-atomic-dual',
1116691Stjones1@inf.ed.ac.uk        'realview64-simple-timing',
1126691Stjones1@inf.ed.ac.uk        'realview64-simple-timing-dual',
1136691Stjones1@inf.ed.ac.uk        'realview64-o3',
1146691Stjones1@inf.ed.ac.uk        'realview64-o3-checker',
1156691Stjones1@inf.ed.ac.uk        'realview64-o3-dual',
1166691Stjones1@inf.ed.ac.uk        'realview64-minor',
1176691Stjones1@inf.ed.ac.uk        'realview64-minor-dual',
1186691Stjones1@inf.ed.ac.uk        'realview64-switcheroo-atomic',
1196691Stjones1@inf.ed.ac.uk        'realview64-switcheroo-timing',
1206691Stjones1@inf.ed.ac.uk        'realview64-switcheroo-o3',
1216691Stjones1@inf.ed.ac.uk        'realview64-switcheroo-full',
1226691Stjones1@inf.ed.ac.uk    ),
1236691Stjones1@inf.ed.ac.uk
1246691Stjones1@inf.ed.ac.uk    ("sparc", None) : (
1256691Stjones1@inf.ed.ac.uk        't1000-simple-atomic',
1266691Stjones1@inf.ed.ac.uk        't1000-simple-x86',
1276691Stjones1@inf.ed.ac.uk    ),
1286691Stjones1@inf.ed.ac.uk
1296691Stjones1@inf.ed.ac.uk    ("timing", None) : (
1306691Stjones1@inf.ed.ac.uk        'pc-simple-atomic',
1316691Stjones1@inf.ed.ac.uk        'pc-simple-timing',
1326691Stjones1@inf.ed.ac.uk        'pc-o3-timing',
1336691Stjones1@inf.ed.ac.uk        'pc-switcheroo-full',
1346691Stjones1@inf.ed.ac.uk    ),
1356691Stjones1@inf.ed.ac.uk
1366691Stjones1@inf.ed.ac.uk    ("x86", "hsail") : (
1376691Stjones1@inf.ed.ac.uk        'gpu',
1386691Stjones1@inf.ed.ac.uk    ),
1396691Stjones1@inf.ed.ac.uk}
1406691Stjones1@inf.ed.ac.uk
1416691Stjones1@inf.ed.ac.ukgeneric_configs = (
1426691Stjones1@inf.ed.ac.uk    'simple-atomic',
1436691Stjones1@inf.ed.ac.uk    'simple-atomic-mp',
1446691Stjones1@inf.ed.ac.uk    'simple-timing',
1456691Stjones1@inf.ed.ac.uk    'simple-timing-mp',
1466691Stjones1@inf.ed.ac.uk
1476691Stjones1@inf.ed.ac.uk    'minor-timing',
1486691Stjones1@inf.ed.ac.uk    'minor-timing-mp',
149
150    'o3-timing',
151    'o3-timing-mt',
152    'o3-timing-mp',
153
154    'rubytest',
155    'memcheck',
156    'memtest',
157    'memtest-filter',
158    'tgen-simple-mem',
159    'tgen-dram-ctrl',
160    'dram-lowp',
161
162    'learning-gem5-p1-simple',
163    'learning-gem5-p1-two-level',
164)
165
166default_ruby_protocol = {
167    "arm" : "MOESI_CMP_directory",
168}
169
170def get_default_protocol(arch):
171    return default_ruby_protocol.get(arch, 'MI_example')
172
173all_categories = ("quick", "long")
174all_modes = ("fs", "se")
175
176class Test(object):
177    """Test case base class.
178
179    Test cases consists of one or more test units that are run in two
180    phases. A run phase (units produced by run_units() and a verify
181    phase (units from verify_units()). The verify phase is skipped if
182    the run phase fails.
183
184    """
185
186    __metaclass__ = ABCMeta
187
188    def __init__(self, name):
189        self.test_name = name
190
191    @abstractmethod
192    def ref_files(self):
193        """Get a list of reference files used by this test case"""
194        pass
195
196    @abstractmethod
197    def run_units(self):
198        """Units (typically RunGem5 instances) that describe the run phase of
199        this test.
200
201        """
202        pass
203
204    @abstractmethod
205    def verify_units(self):
206        """Verify the output from the run phase (see run_units())."""
207        pass
208
209    @abstractmethod
210    def update_ref(self):
211        """Update reference files with files from a test run"""
212        pass
213
214    def run(self):
215        """Run this test case and return a list of results"""
216
217        run_results = [ u.run() for u in self.run_units() ]
218        run_ok = all([not r.skipped() and r for r in run_results ])
219
220        verify_results = [
221            u.run() if run_ok else u.skip()
222            for u in self.verify_units()
223        ]
224
225        return TestResult(self.test_name,
226                          run_results=run_results,
227                          verify_results=verify_results)
228
229    def __str__(self):
230        return self.test_name
231
232class ClassicTest(Test):
233    # The diff ignore list contains all files that shouldn't be diffed
234    # using DiffOutFile. These files typically use special-purpose
235    # diff tools (e.g., DiffStatFile).
236    diff_ignore_files = FileIgnoreList(
237        names=(
238            # Stat files use a special stat differ
239            "stats.txt",
240        ), rex=(
241        ))
242
243    # These files should never be included in the list of
244    # reference files. This list should include temporary files
245    # and other files that we don't care about.
246    ref_ignore_files = FileIgnoreList(
247        names=(
248            "EMPTY",
249        ), rex=(
250            # Mercurial sometimes leaves backups when applying MQ patches
251            r"\.orig$",
252            r"\.rej$",
253        ))
254
255    def __init__(self, gem5, output_dir, config_tuple,
256                 timeout=None,
257                 skip=False, skip_diff_out=False, skip_diff_stat=False):
258
259        super(ClassicTest, self).__init__("/".join(config_tuple))
260
261        ct = config_tuple
262
263        self.gem5 = os.path.abspath(gem5)
264        self.script = os.path.join(_test_base, "run.py")
265        self.config_tuple = ct
266        self.timeout = timeout
267
268        self.output_dir = output_dir
269        self.ref_dir = os.path.join(_test_base,
270                                    ct.category, ct.mode, ct.workload,
271                                    "ref", ct.isa, ct.os, ct.config)
272        self.skip_run = skip
273        self.skip_diff_out = skip or skip_diff_out
274        self.skip_diff_stat = skip or skip_diff_stat
275
276    def ref_files(self):
277        ref_dir = os.path.abspath(self.ref_dir)
278        for root, dirs, files in os.walk(ref_dir, topdown=False):
279            for f in files:
280                fpath = os.path.join(root[len(ref_dir) + 1:], f)
281                if fpath not in ClassicTest.ref_ignore_files:
282                    yield fpath
283
284    def run_units(self):
285        args = [
286            self.script,
287            "/".join(self.config_tuple),
288        ]
289
290        return [
291            RunGem5(self.gem5, args,
292                    ref_dir=self.ref_dir, test_dir=self.output_dir,
293                    skip=self.skip_run),
294        ]
295
296    def verify_units(self):
297        ref_files = set(self.ref_files())
298        units = []
299        if "stats.txt" in ref_files:
300            units.append(
301                DiffStatFile(ref_dir=self.ref_dir, test_dir=self.output_dir,
302                             skip=self.skip_diff_stat))
303        units += [
304            DiffOutFile(f,
305                        ref_dir=self.ref_dir, test_dir=self.output_dir,
306                        skip=self.skip_diff_out)
307            for f in ref_files if f not in ClassicTest.diff_ignore_files
308        ]
309
310        return units
311
312    def update_ref(self):
313        for fname in self.ref_files():
314            shutil.copy(
315                os.path.join(self.output_dir, fname),
316                os.path.join(self.ref_dir, fname))
317
318def parse_test_filter(test_filter):
319    wildcards = ("", "*")
320
321    _filter = list(test_filter.split("/"))
322    if len(_filter) > 3:
323        raise RuntimeError("Illegal test filter string")
324    _filter += [ "", ] * (3 - len(_filter))
325
326    isa, cat, mode = _filter
327
328    if isa in wildcards:
329        raise RuntimeError("No ISA specified")
330
331    cat = all_categories if cat in wildcards else (cat, )
332    mode = all_modes if mode in wildcards else (mode, )
333
334    return isa, cat, mode
335
336def get_tests(isa,
337              categories=all_categories, modes=all_modes,
338              ruby_protocol=None, gpu_isa=None):
339
340    # Generate a list of candidate configs
341    configs = list(arch_configs.get((isa, gpu_isa), []))
342
343    if (isa, gpu_isa) == ("x86", "hsail"):
344        if ruby_protocol == "GPU_RfO":
345            configs += ['gpu-randomtest']
346    else:
347        configs += generic_configs
348
349    if ruby_protocol == get_default_protocol(isa):
350        if ruby_protocol == 'MI_example':
351            configs += [ "%s-ruby" % (c, ) for c in configs ]
352        else:
353            configs += [ "%s-ruby-%s" % (c, ruby_protocol) for c in configs ]
354    elif ruby_protocol is not None:
355        # Override generic ISA configs when using Ruby (excluding
356        # MI_example which is included in all ISAs by default). This
357        # reduces the number of generic tests we re-run for when
358        # compiling Ruby targets.
359        configs = [ "%s-ruby-%s" % (c, ruby_protocol) for c in configs ]
360
361    # /(quick|long)/(fs|se)/workload/ref/arch/guest/config/
362    for conf_script in configs:
363        for cat in categories:
364            for mode in modes:
365                mode_dir = os.path.join(_test_base, cat, mode)
366                if not os.path.exists(mode_dir):
367                    continue
368
369                for workload in os.listdir(mode_dir):
370                    isa_dir = os.path.join(mode_dir, workload, "ref", isa)
371                    if not os.path.isdir(isa_dir):
372                        continue
373
374                    for _os in os.listdir(isa_dir):
375                        test_dir = os.path.join(isa_dir, _os, conf_script)
376                        if not os.path.exists(test_dir) or \
377                           os.path.exists(os.path.join(test_dir, "skip")):
378                            continue
379
380                        yield ClassicConfig(cat, mode, workload, isa, _os,
381                                            conf_script)
382