|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | r""" | 
|  | Class describing simulation results | 
|  | """ | 
|  |  | 
|  | import collections | 
|  | import re | 
|  |  | 
|  | from Testplan import Result | 
|  |  | 
|  | _REGEX_REMOVE = [ | 
|  | # Remove UVM time. | 
|  | re.compile(r'@\s+[\d.]+\s+[np]s: '), | 
|  | re.compile(r'\[[\d.]+\s+[np]s\] '), | 
|  | # Remove assertion time. | 
|  | re.compile(r'\(time [\d.]+ [PF]S\) '), | 
|  | # Remove leading spaces. | 
|  | re.compile(r'^\s+'), | 
|  | # Remove extra white spaces. | 
|  | re.compile(r'\s+(?=\s)'), | 
|  | ] | 
|  |  | 
|  | _REGEX_STRIP = [ | 
|  | # Strip TB instance name. | 
|  | re.compile(r'[\w_]*top\.\S+\.(\w+)'), | 
|  | # Strip assertion. | 
|  | re.compile(r'(?<=Assertion )\S+\.(\w+)'), | 
|  | ] | 
|  |  | 
|  | # Regular expression for a separator: EOL or some of punctuation marks. | 
|  | _SEPARATOR_RE = '($|[ ,.:;])' | 
|  |  | 
|  | _REGEX_STAR = [ | 
|  | # Replace hex numbers with 0x (needs to be called before other numbers). | 
|  | re.compile(r'0x\s*[\da-fA-F]+'), | 
|  | # Replace hex numbers with 'h (needs to be called before other numbers). | 
|  | re.compile(r'\'h\s*[\da-fA-F]+'), | 
|  | # Floating point numbers at the beginning of a word, example "10.1ns". | 
|  | # (needs to be called before other numbers). | 
|  | re.compile(r'(?<=[^a-zA-Z0-9])\d+\.\d+'), | 
|  | # Replace all isolated numbers. Isolated numbers are numbers surrounded by | 
|  | # special symbols, for example ':' or '+' or '_', excluding parenthesis. | 
|  | # So a number with a letter or a round bracket on any one side, is | 
|  | # considered non-isolated number and is not starred by these expressions. | 
|  | re.compile(r'(?<=[^a-zA-Z0-9\(\)])\d+(?=($|[^a-zA-Z0-9\(\)]))'), | 
|  | # Replace numbers surrounded by parenthesis after a space and followed by a | 
|  | # separator. | 
|  | re.compile(r'(?<= \()\s*\d+\s*(?=\)%s)' % _SEPARATOR_RE), | 
|  | # Replace hex/decimal numbers after an equal sign or a semicolon and | 
|  | # followed by a separator. Uses look-behind pattern which need a | 
|  | # fixed width, thus the apparent redundancy. | 
|  | re.compile(r'(?<=[\w\]][=:])[\da-fA-F]+(?=%s)' % _SEPARATOR_RE), | 
|  | re.compile(r'(?<=[\w\]][=:] )[\da-fA-F]+(?=%s)' % _SEPARATOR_RE), | 
|  | re.compile(r'(?<=[\w\]] [=:])[\da-fA-F]+(?=%s)' % _SEPARATOR_RE), | 
|  | re.compile(r'(?<=[\w\]] [=:] )[\da-fA-F]+(?=%s)' % _SEPARATOR_RE), | 
|  | # Replace decimal number at the beginning of the word. | 
|  | re.compile(r'(?<= )\d+(?=\S)'), | 
|  | # Remove decimal number at end of the word and before '=' or '[' or | 
|  | # ',' or '.' or '('. | 
|  | re.compile(r'(?<=\S)\d+(?=($|[ =\[,\.\(]))'), | 
|  | # Replace the instance string. | 
|  | re.compile(r'(?<=instance)\s*=\s*\S+'), | 
|  | ] | 
|  |  | 
|  |  | 
|  | class SimResults: | 
|  | '''An object wrapping up a table of results for some tests | 
|  |  | 
|  | self.table is a list of Result objects, each of which | 
|  | corresponds to one or more runs of the test with a given name. | 
|  |  | 
|  | self.buckets contains a dictionary accessed by the failure signature, | 
|  | holding all failing tests with the same signature. | 
|  | ''' | 
|  |  | 
|  | def __init__(self, items, results): | 
|  | self.table = [] | 
|  | self.buckets = collections.defaultdict(list) | 
|  | self._name_to_row = {} | 
|  | for item in items: | 
|  | self._add_item(item, results) | 
|  |  | 
|  | def _add_item(self, item, results): | 
|  | '''Recursively add a single item to the table of results''' | 
|  | status = results[item] | 
|  | if status in ["F", "K"]: | 
|  | bucket = self._bucketize(item.launcher.fail_msg.message) | 
|  | self.buckets[bucket].append( | 
|  | (item, item.launcher.fail_msg.line_number, | 
|  | item.launcher.fail_msg.context)) | 
|  |  | 
|  | # Runs get added to the table directly | 
|  | if item.target == "run": | 
|  | self._add_run(item, status) | 
|  |  | 
|  | def _add_run(self, item, status): | 
|  | '''Add an entry to table for item''' | 
|  | row = self._name_to_row.get(item.name) | 
|  | if row is None: | 
|  | row = Result(item.name, | 
|  | job_runtime=item.job_runtime, | 
|  | simulated_time=item.simulated_time) | 
|  | self.table.append(row) | 
|  | self._name_to_row[item.name] = row | 
|  |  | 
|  | else: | 
|  | # Record the max job_runtime of all reseeds. | 
|  | if item.job_runtime > row.job_runtime: | 
|  | row.job_runtime = item.job_runtime | 
|  | row.simulated_time = item.simulated_time | 
|  |  | 
|  | if status == 'P': | 
|  | row.passing += 1 | 
|  | row.total += 1 | 
|  |  | 
|  | def _bucketize(self, fail_msg): | 
|  | bucket = fail_msg | 
|  | # Remove stuff. | 
|  | for regex in _REGEX_REMOVE: | 
|  | bucket = regex.sub('', bucket) | 
|  | # Strip stuff. | 
|  | for regex in _REGEX_STRIP: | 
|  | bucket = regex.sub(r'\g<1>', bucket) | 
|  | # Replace with '*'. | 
|  | for regex in _REGEX_STAR: | 
|  | bucket = regex.sub('*', bucket) | 
|  | return bucket |