units.py revision 12142
111828Sjason@lowepower.com#!/usr/bin/env python2 211482Sandreas.sandberg@arm.com# 311482Sandreas.sandberg@arm.com# Copyright (c) 2016 ARM Limited 411482Sandreas.sandberg@arm.com# All rights reserved 511482Sandreas.sandberg@arm.com# 611482Sandreas.sandberg@arm.com# The license below extends only to copyright in the software and shall 711482Sandreas.sandberg@arm.com# not be construed as granting a license to any other intellectual 811482Sandreas.sandberg@arm.com# property including but not limited to intellectual property relating 911482Sandreas.sandberg@arm.com# to a hardware implementation of the functionality of the software 1011482Sandreas.sandberg@arm.com# licensed hereunder. You may use the software subject to the license 1111482Sandreas.sandberg@arm.com# terms below provided that you ensure that this notice is replicated 1211482Sandreas.sandberg@arm.com# unmodified and in its entirety in all distributions of the software, 1311482Sandreas.sandberg@arm.com# modified or unmodified, in source code or in binary form. 1411482Sandreas.sandberg@arm.com# 1511482Sandreas.sandberg@arm.com# Redistribution and use in source and binary forms, with or without 1611482Sandreas.sandberg@arm.com# modification, are permitted provided that the following conditions are 1711482Sandreas.sandberg@arm.com# met: redistributions of source code must retain the above copyright 1811482Sandreas.sandberg@arm.com# notice, this list of conditions and the following disclaimer; 1911482Sandreas.sandberg@arm.com# redistributions in binary form must reproduce the above copyright 2011482Sandreas.sandberg@arm.com# notice, this list of conditions and the following disclaimer in the 2111482Sandreas.sandberg@arm.com# documentation and/or other materials provided with the distribution; 2211482Sandreas.sandberg@arm.com# neither the name of the copyright holders nor the names of its 2311482Sandreas.sandberg@arm.com# contributors may be used to endorse or promote products derived from 2411482Sandreas.sandberg@arm.com# this software without specific prior written permission. 2511482Sandreas.sandberg@arm.com# 2611482Sandreas.sandberg@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2711482Sandreas.sandberg@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2811482Sandreas.sandberg@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2911482Sandreas.sandberg@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3011482Sandreas.sandberg@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3111482Sandreas.sandberg@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3211482Sandreas.sandberg@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3311482Sandreas.sandberg@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3411482Sandreas.sandberg@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3511482Sandreas.sandberg@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3611482Sandreas.sandberg@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3711482Sandreas.sandberg@arm.com# 3811482Sandreas.sandberg@arm.com# Authors: Andreas Sandberg 3911482Sandreas.sandberg@arm.com 4011482Sandreas.sandberg@arm.comfrom abc import ABCMeta, abstractmethod 4111482Sandreas.sandberg@arm.comfrom datetime import datetime 4211482Sandreas.sandberg@arm.comimport difflib 4311482Sandreas.sandberg@arm.comimport functools 4411482Sandreas.sandberg@arm.comimport os 4511482Sandreas.sandberg@arm.comimport re 4611482Sandreas.sandberg@arm.comimport subprocess 4711482Sandreas.sandberg@arm.comimport sys 4811482Sandreas.sandberg@arm.comimport traceback 4911482Sandreas.sandberg@arm.com 5011482Sandreas.sandberg@arm.comfrom results import UnitResult 5111482Sandreas.sandberg@arm.comfrom helpers import * 5211482Sandreas.sandberg@arm.com 5311482Sandreas.sandberg@arm.com_test_base = os.path.join(os.path.dirname(__file__), "..") 5411482Sandreas.sandberg@arm.com 5511482Sandreas.sandberg@arm.comclass TestUnit(object): 5611482Sandreas.sandberg@arm.com """Base class for all test units. 5711482Sandreas.sandberg@arm.com 5811482Sandreas.sandberg@arm.com A test unit is a part of a larger test case. Test cases usually 5911482Sandreas.sandberg@arm.com contain two types of units, run units (run gem5) and verify units 6011482Sandreas.sandberg@arm.com (diff output files). All unit implementations inherit from this 6111482Sandreas.sandberg@arm.com class. 6211482Sandreas.sandberg@arm.com 6311482Sandreas.sandberg@arm.com A unit implementation overrides the _run() method. The test runner 6411482Sandreas.sandberg@arm.com calls the run() method, which wraps _run() to protect against 6511482Sandreas.sandberg@arm.com exceptions. 6611482Sandreas.sandberg@arm.com 6711482Sandreas.sandberg@arm.com """ 6811482Sandreas.sandberg@arm.com 6911482Sandreas.sandberg@arm.com __metaclass__ = ABCMeta 7011482Sandreas.sandberg@arm.com 7111482Sandreas.sandberg@arm.com def __init__(self, name, ref_dir, test_dir, skip=False): 7211482Sandreas.sandberg@arm.com self.name = name 7311482Sandreas.sandberg@arm.com self.ref_dir = ref_dir 7411482Sandreas.sandberg@arm.com self.test_dir = test_dir 7511482Sandreas.sandberg@arm.com self.force_skip = skip 7611482Sandreas.sandberg@arm.com self.start_time = None 7711482Sandreas.sandberg@arm.com self.stop_time = None 7811482Sandreas.sandberg@arm.com 7911482Sandreas.sandberg@arm.com def result(self, state, **kwargs): 8011482Sandreas.sandberg@arm.com if self.start_time is not None and "runtime" not in kwargs: 8111482Sandreas.sandberg@arm.com self.stop_time = datetime.utcnow() 8211482Sandreas.sandberg@arm.com delta = self.stop_time - self.start_time 8311482Sandreas.sandberg@arm.com kwargs["runtime"] = delta.total_seconds() 8411482Sandreas.sandberg@arm.com 8511482Sandreas.sandberg@arm.com return UnitResult(self.name, state, **kwargs) 8611482Sandreas.sandberg@arm.com 8711482Sandreas.sandberg@arm.com def ok(self, **kwargs): 8811482Sandreas.sandberg@arm.com return self.result(UnitResult.STATE_OK, **kwargs) 8911482Sandreas.sandberg@arm.com 9011482Sandreas.sandberg@arm.com def skip(self, **kwargs): 9111482Sandreas.sandberg@arm.com return self.result(UnitResult.STATE_SKIPPED, **kwargs) 9211482Sandreas.sandberg@arm.com 9311482Sandreas.sandberg@arm.com def error(self, message, **kwargs): 9411482Sandreas.sandberg@arm.com return self.result(UnitResult.STATE_ERROR, message=message, **kwargs) 9511482Sandreas.sandberg@arm.com 9611482Sandreas.sandberg@arm.com def failure(self, message, **kwargs): 9711482Sandreas.sandberg@arm.com return self.result(UnitResult.STATE_FAILURE, message=message, **kwargs) 9811482Sandreas.sandberg@arm.com 9911482Sandreas.sandberg@arm.com def ref_file(self, fname): 10011482Sandreas.sandberg@arm.com return os.path.join(self.ref_dir, fname) 10111482Sandreas.sandberg@arm.com 10211482Sandreas.sandberg@arm.com def out_file(self, fname): 10311482Sandreas.sandberg@arm.com return os.path.join(self.test_dir, fname) 10411482Sandreas.sandberg@arm.com 10511482Sandreas.sandberg@arm.com def _read_output(self, fname, default=""): 10611482Sandreas.sandberg@arm.com try: 10711482Sandreas.sandberg@arm.com with open(self.out_file(fname), "r") as f: 10811482Sandreas.sandberg@arm.com return f.read() 10911482Sandreas.sandberg@arm.com except IOError: 11011482Sandreas.sandberg@arm.com return default 11111482Sandreas.sandberg@arm.com 11211482Sandreas.sandberg@arm.com def run(self): 11311482Sandreas.sandberg@arm.com self.start_time = datetime.utcnow() 11411482Sandreas.sandberg@arm.com try: 11511482Sandreas.sandberg@arm.com if self.force_skip: 11611482Sandreas.sandberg@arm.com return self.skip() 11711482Sandreas.sandberg@arm.com else: 11811482Sandreas.sandberg@arm.com return self._run() 11911482Sandreas.sandberg@arm.com except: 12011482Sandreas.sandberg@arm.com return self.error("Python exception:\n%s" % traceback.format_exc()) 12111482Sandreas.sandberg@arm.com 12211482Sandreas.sandberg@arm.com @abstractmethod 12311482Sandreas.sandberg@arm.com def _run(self): 12411482Sandreas.sandberg@arm.com pass 12511482Sandreas.sandberg@arm.com 12611482Sandreas.sandberg@arm.comclass RunGem5(TestUnit): 12711482Sandreas.sandberg@arm.com """Test unit representing a gem5 run. 12811482Sandreas.sandberg@arm.com 12911482Sandreas.sandberg@arm.com Possible failure modes: 13011482Sandreas.sandberg@arm.com - gem5 failed to run -> STATE_ERROR 13111482Sandreas.sandberg@arm.com - timeout -> STATE_ERROR 13211482Sandreas.sandberg@arm.com - non-zero exit code -> STATE_ERROR 13311482Sandreas.sandberg@arm.com 13411482Sandreas.sandberg@arm.com Possible non-failure results: 13511482Sandreas.sandberg@arm.com - exit code == 0 -> STATE_OK 13611482Sandreas.sandberg@arm.com - exit code == 2 -> STATE_SKIPPED 13711482Sandreas.sandberg@arm.com """ 13811482Sandreas.sandberg@arm.com 13911482Sandreas.sandberg@arm.com def __init__(self, gem5, gem5_args, timeout=0, **kwargs): 14011482Sandreas.sandberg@arm.com super(RunGem5, self).__init__("gem5", **kwargs) 14111482Sandreas.sandberg@arm.com self.gem5 = gem5 14211482Sandreas.sandberg@arm.com self.args = gem5_args 14311482Sandreas.sandberg@arm.com self.timeout = timeout 14411482Sandreas.sandberg@arm.com 14511482Sandreas.sandberg@arm.com def _run(self): 14611482Sandreas.sandberg@arm.com gem5_cmd = [ 14711482Sandreas.sandberg@arm.com self.gem5, 14811482Sandreas.sandberg@arm.com "-d", self.test_dir, 14911879Sandreas.sandberg@arm.com "--stats-file", "text://stats.txt?desc=False", 15011482Sandreas.sandberg@arm.com "-re", 15111482Sandreas.sandberg@arm.com ] + self.args 15211482Sandreas.sandberg@arm.com 15311482Sandreas.sandberg@arm.com try: 15411482Sandreas.sandberg@arm.com with ProcessHelper(gem5_cmd, stdout=subprocess.PIPE, 15511482Sandreas.sandberg@arm.com stderr=subprocess.PIPE) as p: 15611482Sandreas.sandberg@arm.com status, gem5_stdout, gem5_stderr = p.call(timeout=self.timeout) 15711482Sandreas.sandberg@arm.com except CallTimeoutException as te: 15811482Sandreas.sandberg@arm.com return self.error("Timeout", stdout=te.stdout, stderr=te.stderr) 15911482Sandreas.sandberg@arm.com except OSError as ose: 16011482Sandreas.sandberg@arm.com return self.error("Failed to launch gem5: %s" % ose) 16111482Sandreas.sandberg@arm.com 16211482Sandreas.sandberg@arm.com stderr = "\n".join([ 16311482Sandreas.sandberg@arm.com "*** gem5 stderr ***", 16411482Sandreas.sandberg@arm.com gem5_stderr, 16511482Sandreas.sandberg@arm.com "", 16611482Sandreas.sandberg@arm.com "*** m5out/simerr ***", 16711482Sandreas.sandberg@arm.com self._read_output("simerr"), 16811482Sandreas.sandberg@arm.com ]) 16911482Sandreas.sandberg@arm.com 17011482Sandreas.sandberg@arm.com stdout = "\n".join([ 17111482Sandreas.sandberg@arm.com "*** gem5 stdout ***", 17211482Sandreas.sandberg@arm.com gem5_stdout, 17311482Sandreas.sandberg@arm.com "", 17411482Sandreas.sandberg@arm.com "*** m5out/simout ***", 17511482Sandreas.sandberg@arm.com self._read_output("simout"), 17611482Sandreas.sandberg@arm.com ]) 17711482Sandreas.sandberg@arm.com 17811482Sandreas.sandberg@arm.com # Signal 17911482Sandreas.sandberg@arm.com if status < 0: 18011482Sandreas.sandberg@arm.com return self.error("gem5 terminated by signal %i" % (-status, ), 18111482Sandreas.sandberg@arm.com stdout=stdout, stderr=stderr) 18211482Sandreas.sandberg@arm.com elif status == 2: 18311482Sandreas.sandberg@arm.com return self.skip(stdout=stdout, stderr=stderr) 18411482Sandreas.sandberg@arm.com elif status > 0: 18511482Sandreas.sandberg@arm.com return self.error("gem5 exited with non-zero status: %i" % status, 18611482Sandreas.sandberg@arm.com stdout=stdout, stderr=stderr) 18711482Sandreas.sandberg@arm.com else: 18811482Sandreas.sandberg@arm.com return self.ok(stdout=stdout, stderr=stderr) 18911482Sandreas.sandberg@arm.com 19011482Sandreas.sandberg@arm.comclass DiffOutFile(TestUnit): 19111482Sandreas.sandberg@arm.com """Test unit comparing and output file and a reference file.""" 19211482Sandreas.sandberg@arm.com 19311482Sandreas.sandberg@arm.com # regular expressions of lines to ignore when diffing outputs 19411482Sandreas.sandberg@arm.com diff_ignore_regexes = { 19511482Sandreas.sandberg@arm.com "simout" : [ 19611482Sandreas.sandberg@arm.com re.compile('^Redirecting (stdout|stderr) to'), 19711482Sandreas.sandberg@arm.com re.compile('^gem5 compiled '), 19811482Sandreas.sandberg@arm.com re.compile('^gem5 started '), 19911482Sandreas.sandberg@arm.com re.compile('^gem5 executing on '), 20011482Sandreas.sandberg@arm.com re.compile('^command line:'), 20111482Sandreas.sandberg@arm.com re.compile("^Couldn't import dot_parser,"), 20211482Sandreas.sandberg@arm.com re.compile("^info: kernel located at:"), 20311482Sandreas.sandberg@arm.com re.compile("^Couldn't unlink "), 20411482Sandreas.sandberg@arm.com re.compile("^Using GPU kernel code file\(s\) "), 20511482Sandreas.sandberg@arm.com ], 20611482Sandreas.sandberg@arm.com "simerr" : [ 20711482Sandreas.sandberg@arm.com #re.compile('^Simulation complete at'), 20811482Sandreas.sandberg@arm.com ], 20911482Sandreas.sandberg@arm.com "config.ini" : [ 21011482Sandreas.sandberg@arm.com re.compile("^(executable|readfile|kernel|image_file)="), 21111482Sandreas.sandberg@arm.com re.compile("^(cwd|input|codefile)="), 21211482Sandreas.sandberg@arm.com ], 21311482Sandreas.sandberg@arm.com "config.json" : [ 21411482Sandreas.sandberg@arm.com re.compile(r'''^\s*"(executable|readfile|kernel|image_file)":'''), 21511482Sandreas.sandberg@arm.com re.compile(r'''^\s*"(cwd|input|codefile)":'''), 21611482Sandreas.sandberg@arm.com ], 21711482Sandreas.sandberg@arm.com } 21811482Sandreas.sandberg@arm.com 21911482Sandreas.sandberg@arm.com def __init__(self, fname, **kwargs): 22011482Sandreas.sandberg@arm.com super(DiffOutFile, self).__init__("diff[%s]" % fname, 22111482Sandreas.sandberg@arm.com **kwargs) 22211482Sandreas.sandberg@arm.com 22311482Sandreas.sandberg@arm.com self.fname = fname 22411482Sandreas.sandberg@arm.com self.line_filters = DiffOutFile.diff_ignore_regexes.get(fname, tuple()) 22511482Sandreas.sandberg@arm.com 22611482Sandreas.sandberg@arm.com def _filter_file(self, fname): 22711482Sandreas.sandberg@arm.com def match_line(l): 22811482Sandreas.sandberg@arm.com for r in self.line_filters: 22911482Sandreas.sandberg@arm.com if r.match(l): 23011482Sandreas.sandberg@arm.com return True 23111482Sandreas.sandberg@arm.com return False 23211482Sandreas.sandberg@arm.com 23311482Sandreas.sandberg@arm.com with open(fname, "r") as f: 23411482Sandreas.sandberg@arm.com for l in f: 23511482Sandreas.sandberg@arm.com if not match_line(l): 23611482Sandreas.sandberg@arm.com yield l 23711482Sandreas.sandberg@arm.com 23811482Sandreas.sandberg@arm.com 23911482Sandreas.sandberg@arm.com def _run(self): 24011482Sandreas.sandberg@arm.com fname = self.fname 24111482Sandreas.sandberg@arm.com ref = self.ref_file(fname) 24211482Sandreas.sandberg@arm.com out = self.out_file(fname) 24311482Sandreas.sandberg@arm.com 24411482Sandreas.sandberg@arm.com if not os.path.exists(ref): 24511482Sandreas.sandberg@arm.com return self.error("%s doesn't exist in reference directory" \ 24611482Sandreas.sandberg@arm.com % fname) 24711482Sandreas.sandberg@arm.com 24811482Sandreas.sandberg@arm.com if not os.path.exists(out): 24911482Sandreas.sandberg@arm.com return self.error("%s doesn't exist in output directory" % fname) 25011482Sandreas.sandberg@arm.com 25111482Sandreas.sandberg@arm.com diff = difflib.unified_diff( 25211482Sandreas.sandberg@arm.com tuple(self._filter_file(ref)), 25311482Sandreas.sandberg@arm.com tuple(self._filter_file(out)), 25411482Sandreas.sandberg@arm.com fromfile="ref/%s" % fname, tofile="out/%s" % fname) 25511482Sandreas.sandberg@arm.com 25611482Sandreas.sandberg@arm.com diff = list(diff) 25711482Sandreas.sandberg@arm.com if diff: 25811482Sandreas.sandberg@arm.com return self.error("ref/%s and out/%s differ" % (fname, fname), 25911482Sandreas.sandberg@arm.com stderr="".join(diff)) 26011482Sandreas.sandberg@arm.com else: 26111482Sandreas.sandberg@arm.com return self.ok(stdout="-- ref/%s and out/%s are identical --" \ 26211482Sandreas.sandberg@arm.com % (fname, fname)) 26311482Sandreas.sandberg@arm.com 26411482Sandreas.sandberg@arm.comclass DiffStatFile(TestUnit): 26511482Sandreas.sandberg@arm.com """Test unit comparing two gem5 stat files.""" 26611482Sandreas.sandberg@arm.com 26711482Sandreas.sandberg@arm.com def __init__(self, **kwargs): 26811482Sandreas.sandberg@arm.com super(DiffStatFile, self).__init__("stat_diff", **kwargs) 26911482Sandreas.sandberg@arm.com 27011482Sandreas.sandberg@arm.com self.stat_diff = os.path.join(_test_base, "diff-out") 27111482Sandreas.sandberg@arm.com 27211482Sandreas.sandberg@arm.com def _run(self): 27312142Sandreas.sandberg@arm.com STATUS_OK = 0 27412142Sandreas.sandberg@arm.com STATUS_NEW_STATS = 1 27512142Sandreas.sandberg@arm.com STATUS_FAILED = 2 27612142Sandreas.sandberg@arm.com 27711482Sandreas.sandberg@arm.com stats = "stats.txt" 27811482Sandreas.sandberg@arm.com 27911482Sandreas.sandberg@arm.com cmd = [ 28011482Sandreas.sandberg@arm.com self.stat_diff, 28111482Sandreas.sandberg@arm.com self.ref_file(stats), self.out_file(stats), 28211482Sandreas.sandberg@arm.com ] 28311482Sandreas.sandberg@arm.com with ProcessHelper(cmd, 28411482Sandreas.sandberg@arm.com stdout=subprocess.PIPE, 28511482Sandreas.sandberg@arm.com stderr=subprocess.PIPE) as p: 28611482Sandreas.sandberg@arm.com status, stdout, stderr = p.call() 28711482Sandreas.sandberg@arm.com 28812142Sandreas.sandberg@arm.com if status in (STATUS_OK, STATUS_NEW_STATS): 28911482Sandreas.sandberg@arm.com return self.ok(stdout=stdout, stderr=stderr) 29012142Sandreas.sandberg@arm.com elif status == STATUS_FAILED: 29111482Sandreas.sandberg@arm.com return self.failure("Statistics mismatch", 29211482Sandreas.sandberg@arm.com stdout=stdout, stderr=stderr) 29311482Sandreas.sandberg@arm.com else: 29411482Sandreas.sandberg@arm.com return self.error("diff-out returned an error: %i" % status, 29511482Sandreas.sandberg@arm.com stdout=stdout, stderr=stderr) 296