mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
9ced3ef674
Summary: This is nessesary in Python3. Everywhere else we assume that encoding is UTF8. If we don't specify it here, the defaults from the environment will be used, which may result in ASCII decoder being used. And if the file is non-ASCII, then it will crash: ``` File "/usr/local/bin/coverage-report-server.py", line 168, in do_GET for line_no, line in enumerate(f, start=1)]) File "/usr/local/bin/coverage-report-server.py", line 165, in <listcomp> ["<span class='{cls}'>{line} </span>".format( File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 106: ordinal not in range(128) ``` Fixes https://bugs.llvm.org/show_bug.cgi?id=33548 Now, how would i add a testcase here? Reviewers: m.ostapenko, kcc Reviewed By: kcc Subscribers: kcc, llvm-commits Tags: #sanitizers Differential Revision: https://reviews.llvm.org/D37661 llvm-svn: 313063
204 lines
6.4 KiB
Python
Executable File
204 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#===- symcov-report-server.py - Coverage Reports HTTP Serve --*- python -*--===#
|
|
#
|
|
# The LLVM Compiler Infrastructure
|
|
#
|
|
# This file is distributed under the University of Illinois Open Source
|
|
# License. See LICENSE.TXT for details.
|
|
#
|
|
#===------------------------------------------------------------------------===#
|
|
'''(EXPERIMENTAL) HTTP server to browse coverage reports from .symcov files.
|
|
|
|
Coverage reports for big binaries are too huge, generating them statically
|
|
makes no sense. Start the server and go to localhost:8001 instead.
|
|
|
|
Usage:
|
|
./tools/sancov/symcov-report-server.py \
|
|
--symcov coverage_data.symcov \
|
|
--srcpath root_src_dir
|
|
|
|
Other options:
|
|
--port port_number - specifies the port to use (8001)
|
|
--host host_name - host name to bind server to (127.0.0.1)
|
|
'''
|
|
|
|
import argparse
|
|
import http.server
|
|
import json
|
|
import socketserver
|
|
import time
|
|
import html
|
|
import os
|
|
import string
|
|
import math
|
|
|
|
INDEX_PAGE_TMPL = """
|
|
<html>
|
|
<head>
|
|
<title>Coverage Report</title>
|
|
<style>
|
|
.lz { color: lightgray; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<table>
|
|
<tr><th>File</th><th>Coverage</th></tr>
|
|
<tr><td><em>Files with 0 coverage are not shown.</em></td></tr>
|
|
$filenames
|
|
</table>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
CONTENT_PAGE_TMPL = """
|
|
<html>
|
|
<head>
|
|
<title>$path</title>
|
|
<style>
|
|
.covered { background: lightgreen; }
|
|
.not-covered { background: lightcoral; }
|
|
.partially-covered { background: navajowhite; }
|
|
.lz { color: lightgray; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<pre>
|
|
$content
|
|
</pre>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
class SymcovData:
|
|
def __init__(self, symcov_json):
|
|
self.covered_points = frozenset(symcov_json['covered-points'])
|
|
self.point_symbol_info = symcov_json['point-symbol-info']
|
|
self.file_coverage = self.compute_filecoverage()
|
|
|
|
def filenames(self):
|
|
return self.point_symbol_info.keys()
|
|
|
|
def has_file(self, filename):
|
|
return filename in self.point_symbol_info
|
|
|
|
def compute_linemap(self, filename):
|
|
"""Build a line_number->css_class map."""
|
|
points = self.point_symbol_info.get(filename, dict())
|
|
|
|
line_to_points = dict()
|
|
for fn, points in points.items():
|
|
for point, loc in points.items():
|
|
line = int(loc.split(":")[0])
|
|
line_to_points.setdefault(line, []).append(point)
|
|
|
|
result = dict()
|
|
for line, points in line_to_points.items():
|
|
status = "covered"
|
|
covered_points = self.covered_points & set(points)
|
|
if not len(covered_points):
|
|
status = "not-covered"
|
|
elif len(covered_points) != len(points):
|
|
status = "partially-covered"
|
|
result[line] = status
|
|
return result
|
|
|
|
def compute_filecoverage(self):
|
|
"""Build a filename->pct coverage."""
|
|
result = dict()
|
|
for filename, fns in self.point_symbol_info.items():
|
|
file_points = []
|
|
for fn, points in fns.items():
|
|
file_points.extend(points.keys())
|
|
covered_points = self.covered_points & set(file_points)
|
|
result[filename] = int(math.ceil(
|
|
len(covered_points) * 100 / len(file_points)))
|
|
return result
|
|
|
|
|
|
def format_pct(pct):
|
|
pct_str = str(max(0, min(100, pct)))
|
|
zeroes = '0' * (3 - len(pct_str))
|
|
if zeroes:
|
|
zeroes = '<span class="lz">{0}</span>'.format(zeroes)
|
|
return zeroes + pct_str
|
|
|
|
class ServerHandler(http.server.BaseHTTPRequestHandler):
|
|
symcov_data = None
|
|
src_path = None
|
|
|
|
def do_GET(self):
|
|
if self.path == '/':
|
|
self.send_response(200)
|
|
self.send_header("Content-type", "text/html; charset=utf-8")
|
|
self.end_headers()
|
|
|
|
filelist = []
|
|
for filename in sorted(self.symcov_data.filenames()):
|
|
file_coverage = self.symcov_data.file_coverage[filename]
|
|
if not file_coverage:
|
|
continue
|
|
filelist.append(
|
|
"<tr><td><a href=\"./{name}\">{name}</a></td>"
|
|
"<td>{coverage}%</td></tr>".format(
|
|
name=html.escape(filename, quote=True),
|
|
coverage=format_pct(file_coverage)))
|
|
|
|
response = string.Template(INDEX_PAGE_TMPL).safe_substitute(
|
|
filenames='\n'.join(filelist))
|
|
self.wfile.write(response.encode('UTF-8', 'replace'))
|
|
elif self.symcov_data.has_file(self.path[1:]):
|
|
filename = self.path[1:]
|
|
filepath = os.path.join(self.src_path, filename)
|
|
if not os.path.exists(filepath):
|
|
self.send_response(404)
|
|
self.end_headers()
|
|
return
|
|
|
|
self.send_response(200)
|
|
self.send_header("Content-type", "text/html; charset=utf-8")
|
|
self.end_headers()
|
|
|
|
linemap = self.symcov_data.compute_linemap(filename)
|
|
|
|
with open(filepath, 'r', encoding='utf8') as f:
|
|
content = "\n".join(
|
|
["<span class='{cls}'>{line} </span>".format(
|
|
line=html.escape(line.rstrip()),
|
|
cls=linemap.get(line_no, ""))
|
|
for line_no, line in enumerate(f, start=1)])
|
|
|
|
response = string.Template(CONTENT_PAGE_TMPL).safe_substitute(
|
|
path=self.path[1:],
|
|
content=content)
|
|
|
|
self.wfile.write(response.encode('UTF-8', 'replace'))
|
|
else:
|
|
self.send_response(404)
|
|
self.end_headers()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="symcov report http server.")
|
|
parser.add_argument('--host', default='127.0.0.1')
|
|
parser.add_argument('--port', default=8001)
|
|
parser.add_argument('--symcov', required=True, type=argparse.FileType('r'))
|
|
parser.add_argument('--srcpath', required=True)
|
|
args = parser.parse_args()
|
|
|
|
print("Loading coverage...")
|
|
symcov_json = json.load(args.symcov)
|
|
ServerHandler.symcov_data = SymcovData(symcov_json)
|
|
ServerHandler.src_path = args.srcpath
|
|
|
|
socketserver.TCPServer.allow_reuse_address = True
|
|
httpd = socketserver.TCPServer((args.host, args.port), ServerHandler)
|
|
print("Serving at {host}:{port}".format(host=args.host, port=args.port))
|
|
try:
|
|
httpd.serve_forever()
|
|
except KeyboardInterrupt:
|
|
pass
|
|
httpd.server_close()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|