Restore the LoopInstSimplify pass, reverting r327329 that removed it.
[llvm-complete.git] / tools / sancov / coverage-report-server.py
bloba2e161d0de58b43a580e9609ef3e4de9aae2a164
1 #!/usr/bin/env python3
2 #===- symcov-report-server.py - Coverage Reports HTTP Serve --*- python -*--===#
4 # The LLVM Compiler Infrastructure
6 # This file is distributed under the University of Illinois Open Source
7 # License. See LICENSE.TXT for details.
9 #===------------------------------------------------------------------------===#
10 '''(EXPERIMENTAL) HTTP server to browse coverage reports from .symcov files.
12 Coverage reports for big binaries are too huge, generating them statically
13 makes no sense. Start the server and go to localhost:8001 instead.
15 Usage:
16 ./tools/sancov/symcov-report-server.py \
17 --symcov coverage_data.symcov \
18 --srcpath root_src_dir
20 Other options:
21 --port port_number - specifies the port to use (8001)
22 --host host_name - host name to bind server to (127.0.0.1)
23 '''
25 import argparse
26 import http.server
27 import json
28 import socketserver
29 import time
30 import html
31 import os
32 import string
33 import math
35 INDEX_PAGE_TMPL = """
36 <html>
37 <head>
38 <title>Coverage Report</title>
39 <style>
40 .lz { color: lightgray; }
41 </style>
42 </head>
43 <body>
44 <table>
45 <tr><th>File</th><th>Coverage</th></tr>
46 <tr><td><em>Files with 0 coverage are not shown.</em></td></tr>
47 $filenames
48 </table>
49 </body>
50 </html>
51 """
53 CONTENT_PAGE_TMPL = """
54 <html>
55 <head>
56 <title>$path</title>
57 <style>
58 .covered { background: lightgreen; }
59 .not-covered { background: lightcoral; }
60 .partially-covered { background: navajowhite; }
61 .lz { color: lightgray; }
62 </style>
63 </head>
64 <body>
65 <pre>
66 $content
67 </pre>
68 </body>
69 </html>
70 """
72 class SymcovData:
73 def __init__(self, symcov_json):
74 self.covered_points = frozenset(symcov_json['covered-points'])
75 self.point_symbol_info = symcov_json['point-symbol-info']
76 self.file_coverage = self.compute_filecoverage()
78 def filenames(self):
79 return self.point_symbol_info.keys()
81 def has_file(self, filename):
82 return filename in self.point_symbol_info
84 def compute_linemap(self, filename):
85 """Build a line_number->css_class map."""
86 points = self.point_symbol_info.get(filename, dict())
88 line_to_points = dict()
89 for fn, points in points.items():
90 for point, loc in points.items():
91 line = int(loc.split(":")[0])
92 line_to_points.setdefault(line, []).append(point)
94 result = dict()
95 for line, points in line_to_points.items():
96 status = "covered"
97 covered_points = self.covered_points & set(points)
98 if not len(covered_points):
99 status = "not-covered"
100 elif len(covered_points) != len(points):
101 status = "partially-covered"
102 result[line] = status
103 return result
105 def compute_filecoverage(self):
106 """Build a filename->pct coverage."""
107 result = dict()
108 for filename, fns in self.point_symbol_info.items():
109 file_points = []
110 for fn, points in fns.items():
111 file_points.extend(points.keys())
112 covered_points = self.covered_points & set(file_points)
113 result[filename] = int(math.ceil(
114 len(covered_points) * 100 / len(file_points)))
115 return result
118 def format_pct(pct):
119 pct_str = str(max(0, min(100, pct)))
120 zeroes = '0' * (3 - len(pct_str))
121 if zeroes:
122 zeroes = '<span class="lz">{0}</span>'.format(zeroes)
123 return zeroes + pct_str
125 class ServerHandler(http.server.BaseHTTPRequestHandler):
126 symcov_data = None
127 src_path = None
129 def do_GET(self):
130 if self.path == '/':
131 self.send_response(200)
132 self.send_header("Content-type", "text/html; charset=utf-8")
133 self.end_headers()
135 filelist = []
136 for filename in sorted(self.symcov_data.filenames()):
137 file_coverage = self.symcov_data.file_coverage[filename]
138 if not file_coverage:
139 continue
140 filelist.append(
141 "<tr><td><a href=\"./{name}\">{name}</a></td>"
142 "<td>{coverage}%</td></tr>".format(
143 name=html.escape(filename, quote=True),
144 coverage=format_pct(file_coverage)))
146 response = string.Template(INDEX_PAGE_TMPL).safe_substitute(
147 filenames='\n'.join(filelist))
148 self.wfile.write(response.encode('UTF-8', 'replace'))
149 elif self.symcov_data.has_file(self.path[1:]):
150 filename = self.path[1:]
151 filepath = os.path.join(self.src_path, filename)
152 if not os.path.exists(filepath):
153 self.send_response(404)
154 self.end_headers()
155 return
157 self.send_response(200)
158 self.send_header("Content-type", "text/html; charset=utf-8")
159 self.end_headers()
161 linemap = self.symcov_data.compute_linemap(filename)
163 with open(filepath, 'r', encoding='utf8') as f:
164 content = "\n".join(
165 ["<span class='{cls}'>{line}&nbsp;</span>".format(
166 line=html.escape(line.rstrip()),
167 cls=linemap.get(line_no, ""))
168 for line_no, line in enumerate(f, start=1)])
170 response = string.Template(CONTENT_PAGE_TMPL).safe_substitute(
171 path=self.path[1:],
172 content=content)
174 self.wfile.write(response.encode('UTF-8', 'replace'))
175 else:
176 self.send_response(404)
177 self.end_headers()
180 def main():
181 parser = argparse.ArgumentParser(description="symcov report http server.")
182 parser.add_argument('--host', default='127.0.0.1')
183 parser.add_argument('--port', default=8001)
184 parser.add_argument('--symcov', required=True, type=argparse.FileType('r'))
185 parser.add_argument('--srcpath', required=True)
186 args = parser.parse_args()
188 print("Loading coverage...")
189 symcov_json = json.load(args.symcov)
190 ServerHandler.symcov_data = SymcovData(symcov_json)
191 ServerHandler.src_path = args.srcpath
193 socketserver.TCPServer.allow_reuse_address = True
194 httpd = socketserver.TCPServer((args.host, args.port), ServerHandler)
195 print("Serving at {host}:{port}".format(host=args.host, port=args.port))
196 try:
197 httpd.serve_forever()
198 except KeyboardInterrupt:
199 pass
200 httpd.server_close()
202 if __name__ == '__main__':
203 main()