Christophe Favergeon | 3b2a0ee | 2019-06-12 13:29:14 +0200 | [diff] [blame] | 1 | # Process the test results |
| 2 | # Test status (like passed, or failed with error code) |
| 3 | |
| 4 | import argparse |
| 5 | import re |
| 6 | import TestScripts.NewParser as parse |
| 7 | import TestScripts.CodeGen |
| 8 | from collections import deque |
| 9 | import os.path |
| 10 | import numpy as np |
| 11 | import pandas as pd |
| 12 | import statsmodels.api as sm |
| 13 | import statsmodels.formula.api as smf |
| 14 | import csv |
| 15 | |
| 16 | def findItem(root,path): |
| 17 | """ Find a node in a tree |
| 18 | |
| 19 | Args: |
| 20 | path (list) : A list of node ID |
| 21 | This list is describing a path in the tree. |
| 22 | By starting from the root and following this path, |
| 23 | we can find the node in the tree. |
| 24 | Raises: |
| 25 | Nothing |
| 26 | Returns: |
| 27 | TreeItem : A node |
| 28 | """ |
| 29 | # The list is converted into a queue. |
| 30 | q = deque(path) |
| 31 | q.popleft() |
| 32 | c = root |
| 33 | while q: |
| 34 | n = q.popleft() |
| 35 | # We get the children based on its ID and continue |
| 36 | c = c[n-1] |
| 37 | return(c) |
| 38 | |
| 39 | |
| 40 | |
| 41 | NORMAL = 1 |
| 42 | INTEST = 2 |
| 43 | TESTPARAM = 3 |
| 44 | |
| 45 | def joinit(iterable, delimiter): |
| 46 | it = iter(iterable) |
| 47 | yield next(it) |
| 48 | for x in it: |
| 49 | yield delimiter |
| 50 | yield x |
| 51 | |
| 52 | def formatProd(a,b): |
| 53 | if a == "Intercept": |
| 54 | return(str(b)) |
| 55 | return("%s * %s" % (a,b)) |
| 56 | |
| 57 | def summaryBenchmark(elem,path): |
| 58 | regressionPath=os.path.join(os.path.dirname(path),"regression.csv") |
| 59 | full=pd.read_csv(path) |
| 60 | |
| 61 | csvheaders = [] |
| 62 | with open('currentConfig.csv', 'r') as f: |
| 63 | reader = csv.reader(f) |
| 64 | csvheaders = next(reader, None) |
| 65 | |
| 66 | groupList = list(set(elem.params.full) - set(elem.params.summary)) |
| 67 | #grouped=full.groupby(list(elem.params.summary) + ['ID','CATEGORY']).max() |
| 68 | #grouped.reset_index(level=grouped.index.names, inplace=True) |
| 69 | #print(grouped) |
| 70 | #print(grouped.columns) |
| 71 | |
| 72 | |
| 73 | def reg(d): |
| 74 | m=d["CYCLES"].max() |
| 75 | results = smf.ols('CYCLES ~ ' + elem.params.formula, data=d).fit() |
| 76 | f=joinit([formatProd(a,b) for (a,b) in zip(results.params.index,results.params.values)]," + ") |
| 77 | f="".join(f) |
| 78 | f = re.sub(r':','*',f) |
| 79 | #print(results.summary()) |
| 80 | return(pd.Series({'Regression':"%s" % f,'MAX' : m})) |
| 81 | |
| 82 | regList = ['ID','CATEGORY','NAME'] + csvheaders + groupList |
| 83 | |
| 84 | regression=full.groupby(regList).apply(reg) |
| 85 | regression.reset_index(level=regression.index.names, inplace=True) |
| 86 | renamingDict = { a : b for (a,b) in zip(elem.params.full,elem.params.paramNames)} |
| 87 | regression = regression.rename(columns=renamingDict) |
| 88 | regression.to_csv(regressionPath,index=False,quoting=csv.QUOTE_NONNUMERIC) |
| 89 | |
| 90 | |
| 91 | |
| 92 | def analyseResult(root,results,embedded,benchmark): |
| 93 | path = [] |
| 94 | state = NORMAL |
| 95 | prefix="" |
| 96 | elem=None |
| 97 | theId=None |
| 98 | theError=None |
| 99 | theLine=None |
| 100 | passed=0 |
| 101 | cycles=None |
| 102 | benchFile = None |
| 103 | if embedded: |
| 104 | prefix = ".*S:[ ]" |
| 105 | |
| 106 | # Parse the result file. |
| 107 | # NORMAL mode is when we are parsing suite or group. |
| 108 | # Otherwise we are parsing a test and we need to analyse the |
| 109 | # test result. |
| 110 | # TESTPARAM is used to read parameters of the test. |
| 111 | # Format of output is: |
| 112 | #node ident : s id or g id or t or u |
| 113 | #test status : id error linenb status Y or N (Y when passing) |
| 114 | #param for this test b x,x,x,x or b alone if not param |
| 115 | #node end : p |
| 116 | # In FPGA mode: |
| 117 | #Prefix S:[ ] before driver dump |
| 118 | # D:[ ] before data dump (output patterns) |
| 119 | |
| 120 | for l in results: |
| 121 | l = l.strip() |
| 122 | if not re.match(r'^.*D:[ ].*$',l): |
| 123 | if state == NORMAL: |
| 124 | if len(l) > 0: |
| 125 | # Line starting with g or s is a suite or group. |
| 126 | # In FPGA mode, those line are prefixed with 'S: ' |
| 127 | # and data file with 'D: ' |
| 128 | if re.match(r'^%s[gs][ ]+[0-9]+.*$' % prefix,l): |
| 129 | # Extract the test id |
| 130 | theId=re.sub(r'^%s[gs][ ]+([0-9]+).*$' % prefix,r'\1',l) |
| 131 | theId=int(theId) |
| 132 | path.append(theId) |
| 133 | # From a list of id, find the TreeElem in the Parsed tree |
| 134 | # to know what is the node. |
| 135 | elem = findItem(root,path) |
| 136 | # Display formatted output for this node |
| 137 | if elem.params: |
| 138 | #print(elem.params.full) |
| 139 | benchPath = os.path.join(benchmark,elem.fullPath(),"fullBenchmark.csv") |
| 140 | summaryBenchmark(elem,benchPath) |
| 141 | |
| 142 | |
| 143 | # If we have detected a test, we switch to test mode |
| 144 | if re.match(r'^%s[t][ ]*$' % prefix,l): |
| 145 | state = INTEST |
| 146 | |
| 147 | |
| 148 | # Pop |
| 149 | # End of suite or group |
| 150 | if re.match(r'^%sp.*$' % prefix,l): |
| 151 | path.pop() |
| 152 | elif state == INTEST: |
| 153 | if len(l) > 0: |
| 154 | # In test mode, we are looking for test status. |
| 155 | # A line starting with S |
| 156 | # (There may be empty lines or line for data files) |
| 157 | passRe = r'^%s([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([YN]).*$' % prefix |
| 158 | if re.match(passRe,l): |
| 159 | # If we have found a test status then we will start again |
| 160 | # in normal mode after this. |
| 161 | |
| 162 | m = re.match(passRe,l) |
| 163 | |
| 164 | # Extract test ID, test error code, line number and status |
| 165 | theId=m.group(1) |
| 166 | theId=int(theId) |
| 167 | |
| 168 | status=m.group(5) |
| 169 | passed=0 |
| 170 | |
| 171 | # Convert status to number as used by formatter. |
| 172 | if status=="Y": |
| 173 | passed = 1 |
| 174 | if status=="N": |
| 175 | passed = 0 |
| 176 | # Compute path to this node |
| 177 | newPath=path.copy() |
| 178 | newPath.append(theId) |
| 179 | # Find the node in the Tree |
| 180 | elem = findItem(root,newPath) |
| 181 | |
| 182 | |
| 183 | state = TESTPARAM |
| 184 | else: |
| 185 | if re.match(r'^%sp.*$' % prefix,l): |
| 186 | path.pop() |
| 187 | if re.match(r'^%s[t][ ]*$' % prefix,l): |
| 188 | state = INTEST |
| 189 | else: |
| 190 | state = NORMAL |
| 191 | else: |
| 192 | if len(l) > 0: |
| 193 | state = INTEST |
| 194 | params="" |
| 195 | |
| 196 | |
| 197 | parser = argparse.ArgumentParser(description='Generate summary benchmarks') |
| 198 | |
| 199 | parser.add_argument('-f', nargs='?',type = str, default=None, help="Test description file path") |
| 200 | # Where the result file can be found |
| 201 | parser.add_argument('-r', nargs='?',type = str, default=None, help="Result file path") |
| 202 | |
| 203 | parser.add_argument('-b', nargs='?',type = str, default="FullBenchmark", help="Full Benchmark dir path") |
| 204 | parser.add_argument('-e', action='store_true', help="Embedded test") |
| 205 | |
| 206 | args = parser.parse_args() |
| 207 | |
| 208 | if args.f is not None: |
| 209 | p = parse.Parser() |
| 210 | # Parse the test description file |
| 211 | root = p.parse(args.f) |
| 212 | with open(args.r,"r") as results: |
| 213 | analyseResult(root,results,args.e,args.b) |
| 214 | |
| 215 | else: |
| 216 | parser.print_help() |