1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00
llvm-mirror/tools/sancov/coverage-report-server.py
Roman Lebedev 9ced3ef674 [sancov] coverage-report-server.py: ServerHandler(): open file as UTF8
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}&nbsp;</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
2017-09-12 18:59:21 +00:00

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}&nbsp;</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()