1#/usr/bin/python3 2# -*- coding: UTF-8 -*- 3import sys 4import re 5import copy 6import pprint 7 8COVERRED = "COVERRED" 9NOT_COVERRED = "NOT_COVERRED" 10DONTCARE = "DONTCARE" 11BEGIN = "BEGIN" 12END = "END" 13CHILDREN = "CHILDREN" 14MODULE = "MODULE" 15INSTANCE = "INSTANCE" 16TYPE="TYPE" 17ROOT="ROOT" 18NODE="NODE" 19SELFCOVERAGE="SELFCOVERAGE" 20TREECOVERAGE="TREECOVERAGE" 21 22def get_lines(input_file): 23 lines = [] 24 with open(input_file) as f: 25 for line in f: 26 lines.append(line) 27 return lines 28 29def get_line_annotation(lines): 30 line_annotations = [] 31 # pattern_1: 040192 if(array_0_MPORT_en & array_0_MPORT_mask) begin 32 # pattern_2: 2218110 end else if (_T_30) begin // @[Conditional.scala 40:58] 33 # pattern_2: 000417 end else begin 34 coverred_pattern_1 = re.compile('^\s*(\d+)\s+if') 35 coverred_pattern_2 = re.compile('^\s*(\d+)\s+end else') 36 not_coverred_pattern_1 = re.compile('^\s*(%0+)\s+if') 37 not_coverred_pattern_2 = re.compile('^\s*(%0+)\s+end else') 38 39 for line in lines: 40 coverred_match = coverred_pattern_1.search(line) or coverred_pattern_2.search(line) 41 not_coverred_match = not_coverred_pattern_1.search(line) or not_coverred_pattern_2.search(line) 42 43 assert not (coverred_match and not_coverred_match) 44 45 if coverred_match: 46 line_annotations.append(COVERRED) 47 elif not_coverred_match: 48 line_annotations.append(NOT_COVERRED) 49 else: 50 line_annotations.append(DONTCARE) 51 return line_annotations 52 53# get the line coverage statistics in line range [start, end) 54def get_coverage_statistics(line_annotations, start, end): 55 coverred = 0 56 not_coverred = 0 57 for i in range(start, end): 58 if line_annotations[i] == COVERRED: 59 coverred += 1 60 61 if line_annotations[i] == NOT_COVERRED: 62 not_coverred += 1 63 64 # deal with divide by zero 65 coverage = 1.0 66 if coverred + not_coverred != 0: 67 coverage = float(coverred) / (coverred + not_coverred) 68 return (coverred, not_coverred, coverage) 69 70# get modules and all it's submodules 71def get_modules(lines): 72 modules = {} 73 74 module_pattern = re.compile("module (\w+)\(") 75 endmodule_pattern = re.compile("endmodule") 76 submodule_pattern = re.compile("(\w+) (\w+) \( // @\[\w+.scala \d+:\d+\]") 77 78 line_count = 0 79 80 name = "ModuleName" 81 82 for line in lines: 83 module_match = module_pattern.search(line) 84 endmodule_match = endmodule_pattern.search(line) 85 submodule_match = submodule_pattern.search(line) 86 87 assert not (module_match and endmodule_match) 88 89 if module_match: 90 name = module_match.group(1) 91 # print("module_match: module: %s" % name) 92 assert name not in modules 93 # [begin 94 modules[name] = {} 95 modules[name][BEGIN] = line_count 96 # the first time we see a module, we treat as a root node 97 modules[name][TYPE] = ROOT 98 99 if endmodule_match: 100 # print("endmodule_match: module: %s" % name) 101 assert name in modules 102 assert END not in modules[name] 103 # end) 104 modules[name][END] = line_count + 1 105 # reset module name to invalid 106 name = "ModuleName" 107 108 if submodule_match: 109 # submodule must be inside hierarchy 110 assert name != "ModuleName" 111 submodule_type = submodule_match.group(1) 112 submodule_instance = submodule_match.group(2) 113 # print("submodule_match: type: %s instance: %s" % (submodule_type, submodule_instance)) 114 115 # submodules should be defined first 116 # if we can not find it's definition 117 # we consider it a black block module 118 if submodule_type not in modules: 119 print("Module %s is a Blackbox" % submodule_type) 120 else: 121 # mark submodule as a tree node 122 # it's no longer root any more 123 modules[submodule_type][TYPE] = NODE 124 125 if CHILDREN not in modules[name]: 126 modules[name][CHILDREN] = [] 127 submodule = {MODULE: submodule_type, INSTANCE: submodule_instance} 128 modules[name][CHILDREN].append(submodule) 129 130 line_count += 1 131 return modules 132 133# we define two coverage metrics: 134# self coverage: coverage results of this module(excluding submodules) 135# tree coverage: coverage results of this module(including submodules) 136def get_tree_coverage(modules, coverage): 137 def dfs(module): 138 if TREECOVERAGE not in modules[module]: 139 self_coverage = modules[module][SELFCOVERAGE] 140 if CHILDREN not in modules[module]: 141 modules[module][TREECOVERAGE] = self_coverage 142 else: 143 coverred = self_coverage[0] 144 not_coverred = self_coverage[1] 145 # the dfs part 146 for child in modules[module][CHILDREN]: 147 child_coverage = dfs(child[MODULE]) 148 coverred += child_coverage[0] 149 not_coverred += child_coverage[1] 150 # deal with divide by zero 151 coverage = 1.0 152 if coverred + not_coverred != 0: 153 coverage = float(coverred) / (coverred + not_coverred) 154 modules[module][TREECOVERAGE] = (coverred, not_coverred, coverage) 155 return modules[module][TREECOVERAGE] 156 157 for module in modules: 158 modules[module][SELFCOVERAGE] = coverage[module] 159 160 for module in modules: 161 modules[module][TREECOVERAGE] = dfs(module) 162 return modules 163 164# arg1: tree coverage results 165# arg2: coverage type 166def sort_coverage(coverage, coverage_type): 167 l = [(module, coverage[module][coverage_type])for module in coverage] 168 l.sort(key=lambda x:x[1][2]) 169 return l 170 171def print_tree_coverage(tree_coverage): 172 def dfs(module, level): 173 # print current node 174 tree = tree_coverage[module][TREECOVERAGE] 175 self = tree_coverage[module][SELFCOVERAGE] 176 print(" " * level + "- " + module) 177 print(" " * level + " tree", end="") 178 print("(%d, %d, %.2f)" % (tree[0], tree[1], tree[2] * 100.0)) 179 print(" " * level + " self", end="") 180 print("(%d, %d, %.2f)" % (self[0], self[1], self[2] * 100.0)) 181 182 # print children nodes 183 if CHILDREN in modules[module]: 184 # the dfs part 185 for child in modules[module][CHILDREN]: 186 dfs(child[MODULE], level + 1) 187 188 for module in tree_coverage: 189 if tree_coverage[module][TYPE] == ROOT: 190 dfs(module, 0) 191 192if __name__ == "__main__": 193 assert len(sys.argv) == 2, "Expect input_file" 194 input_file = sys.argv[1] 195 pp = pprint.PrettyPrinter(indent=4) 196 197 lines = get_lines(input_file) 198 # print("lines:") 199 # pp.pprint(lines) 200 201 annotations = get_line_annotation(lines) 202 # print("annotations:") 203 # pp.pprint(annotations) 204 205 modules = get_modules(lines) 206 # print("modules:") 207 # pp.pprint(modules) 208 209 self_coverage = {module: get_coverage_statistics(annotations, modules[module][BEGIN], modules[module][END]) 210 for module in modules} 211 # print("self_coverage:") 212 # pp.pprint(self_coverage) 213 214 tree_coverage = get_tree_coverage(modules, self_coverage) 215 # print("tree_coverage:") 216 # pp.pprint(tree_coverage) 217 218 print("SelfCoverage:") 219 pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE)) 220 221 print("TreeCoverage:") 222 pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE)) 223 224 print("AllCoverage:") 225 print_tree_coverage(tree_coverage) 226