diff options
author | Mike Aizatsky <aizatsky@chromium.org> | 2016-10-04 19:18:23 +0000 |
---|---|---|
committer | Mike Aizatsky <aizatsky@chromium.org> | 2016-10-04 19:18:23 +0000 |
commit | 0ef7b1af4cbaff9fc6d0a22868d67d90a416b5f5 (patch) | |
tree | d9bf0b60afa613f76e97416f6cf156f61aff61ef /llvm/tools/sancov/coverage-report-server.py | |
parent | 11ee532c3a559dbd5c3103d49a623a1e636a49dc (diff) | |
download | bcm5719-llvm-0ef7b1af4cbaff9fc6d0a22868d67d90a416b5f5.tar.gz bcm5719-llvm-0ef7b1af4cbaff9fc6d0a22868d67d90a416b5f5.zip |
[sancov] renamed symcov-report-server to coverage-report-server
llvm-svn: 283241
Diffstat (limited to 'llvm/tools/sancov/coverage-report-server.py')
-rwxr-xr-x | llvm/tools/sancov/coverage-report-server.py | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/llvm/tools/sancov/coverage-report-server.py b/llvm/tools/sancov/coverage-report-server.py new file mode 100755 index 00000000000..ac3206cba39 --- /dev/null +++ b/llvm/tools/sancov/coverage-report-server.py @@ -0,0 +1,203 @@ +#!/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') 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)]) + + 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() |