1 |
2 |
jamieiles |
#!/usr/bin/env python3
|
2 |
|
|
|
3 |
|
|
# Copyright Jamie Iles, 2017
|
4 |
|
|
#
|
5 |
|
|
# This file is part of s80x86.
|
6 |
|
|
#
|
7 |
|
|
# s80x86 is free software: you can redistribute it and/or modify
|
8 |
|
|
# it under the terms of the GNU General Public License as published by
|
9 |
|
|
# the Free Software Foundation, either version 3 of the License, or
|
10 |
|
|
# (at your option) any later version.
|
11 |
|
|
#
|
12 |
|
|
# s80x86 is distributed in the hope that it will be useful,
|
13 |
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
|
|
# GNU General Public License for more details.
|
16 |
|
|
#
|
17 |
|
|
# You should have received a copy of the GNU General Public License
|
18 |
|
|
# along with s80x86. If not, see .
|
19 |
|
|
|
20 |
|
|
import argparse
|
21 |
|
|
import os
|
22 |
|
|
import re
|
23 |
|
|
import sys
|
24 |
|
|
import time
|
25 |
|
|
|
26 |
|
|
from collections import namedtuple, defaultdict
|
27 |
|
|
from xml.dom import minidom
|
28 |
|
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
29 |
|
|
|
30 |
|
|
def gather_coverage(coverage_root):
|
31 |
|
|
coverage = defaultdict(int)
|
32 |
|
|
for root, dirs, files in os.walk(coverage_root):
|
33 |
|
|
for f in files:
|
34 |
|
|
if not re.match(r'microcode_.*\.mcov', f):
|
35 |
|
|
continue
|
36 |
|
|
with open(os.path.join(root, f)) as covfile:
|
37 |
|
|
lines = covfile.readlines()
|
38 |
|
|
for l in lines:
|
39 |
|
|
loc, count = l.split(':')
|
40 |
|
|
coverage[int(loc, 16)] += int(count, 16)
|
41 |
|
|
|
42 |
|
|
return coverage
|
43 |
|
|
|
44 |
|
|
def coverage_dict(microcode, coverage_root):
|
45 |
|
|
cd = {}
|
46 |
|
|
|
47 |
|
|
coverage = gather_coverage(coverage_root)
|
48 |
|
|
with open(microcode) as microcode_bin:
|
49 |
|
|
lines = microcode_bin.readlines()
|
50 |
|
|
|
51 |
|
|
addr = 0
|
52 |
|
|
for linenum, l in enumerate(lines):
|
53 |
|
|
if l.startswith('//'):
|
54 |
|
|
continue
|
55 |
|
|
if l.startswith('@'):
|
56 |
|
|
addr = int(l.split()[1], 16)
|
57 |
|
|
continue
|
58 |
|
|
cd[linenum] = (coverage[addr], l)
|
59 |
|
|
addr += 1
|
60 |
|
|
|
61 |
|
|
return cd
|
62 |
|
|
|
63 |
|
|
def report(microcode, coverage_root):
|
64 |
|
|
cd = coverage_dict(microcode, coverage_root)
|
65 |
|
|
|
66 |
|
|
data = []
|
67 |
|
|
for linenum, (count, line) in cd.items():
|
68 |
|
|
data.append('%s %8d %s' % ('*' if count == 0 else ' ',
|
69 |
|
|
count, line))
|
70 |
|
|
return ''.join(data)
|
71 |
|
|
|
72 |
|
|
def report_xml(microcode, coverage_root, source_root):
|
73 |
|
|
cd = coverage_dict(microcode, coverage_root)
|
74 |
|
|
|
75 |
|
|
uncovered = len(list(filter(lambda x: x[0] == 0, cd.values())))
|
76 |
|
|
num_lines = len(cd.keys())
|
77 |
|
|
line_rate = (num_lines - uncovered) / num_lines
|
78 |
|
|
|
79 |
|
|
coverage = Element('coverage', attrib={
|
80 |
|
|
'branch-rate': '0.0',
|
81 |
|
|
'complexity': '0.0',
|
82 |
|
|
'line-rate': str(line_rate),
|
83 |
|
|
'timestamp': str(int(time.time())),
|
84 |
|
|
'version': 'verilator-cobertura-1',
|
85 |
|
|
})
|
86 |
|
|
sources = SubElement(coverage, 'sources')
|
87 |
|
|
source = SubElement(sources, 'source')
|
88 |
|
|
source.text = source_root
|
89 |
|
|
packages = SubElement(coverage, 'packages')
|
90 |
|
|
package = SubElement(packages, 'package', attrib={
|
91 |
|
|
'name': 'microcode',
|
92 |
|
|
'branch-rate': '0.0',
|
93 |
|
|
'complexity': '0.0',
|
94 |
|
|
'line-rate': str(line_rate),
|
95 |
|
|
})
|
96 |
|
|
classes = SubElement(package, 'classes')
|
97 |
|
|
f = SubElement(classes, 'class', attrib={
|
98 |
|
|
'filename': os.path.relpath(microcode, source_root),
|
99 |
|
|
'name': os.path.basename(microcode).replace('.', '_'),
|
100 |
|
|
'line-rate': str(line_rate),
|
101 |
|
|
'branch-rate': '0.0',
|
102 |
|
|
'complexity': '0.0',
|
103 |
|
|
})
|
104 |
|
|
lines_elem = SubElement(f, 'lines')
|
105 |
|
|
for linenum, (count, _) in cd.items():
|
106 |
|
|
l = SubElement(lines_elem, 'line',
|
107 |
|
|
attrib={'hits': str(count), 'number': str(linenum), 'branch': 'false'})
|
108 |
|
|
|
109 |
|
|
return minidom.parseString(tostring(coverage)).toprettyxml(indent=" ").replace('', '\n')
|
110 |
|
|
|
111 |
|
|
if __name__ == '__main__':
|
112 |
|
|
parser = argparse.ArgumentParser()
|
113 |
|
|
parser.add_argument('microcode_bin')
|
114 |
|
|
parser.add_argument('coverage_root')
|
115 |
|
|
parser.add_argument('source_root')
|
116 |
|
|
parser.add_argument('--xml', action='store_true')
|
117 |
|
|
parser.add_argument('--output')
|
118 |
|
|
args = parser.parse_args()
|
119 |
|
|
|
120 |
|
|
if args.xml:
|
121 |
|
|
data = report_xml(args.microcode_bin, args.coverage_root, args.source_root)
|
122 |
|
|
else:
|
123 |
|
|
data = report(args.microcode_bin, args.coverage_root)
|
124 |
|
|
|
125 |
|
|
if args.output:
|
126 |
|
|
with open(args.output, 'w') as f:
|
127 |
|
|
f.write(data)
|
128 |
|
|
else:
|
129 |
|
|
print(data, end='')
|