[llvm] r282637 - [sancov] a simple .symcov coverage report server
Mike Aizatsky via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 28 14:27:58 PDT 2016
Author: aizatsky
Date: Wed Sep 28 16:27:58 2016
New Revision: 282637
URL: http://llvm.org/viewvc/llvm-project?rev=282637&view=rev
Log:
[sancov] a simple .symcov coverage report server
Coverage reports for gigabyte-sized binaries are huge. There's no
practical reason to generate them statically.
Implementing an experiment http coverage report server. The server
loads .symcov file and serves interactive coverage pages.
Added:
llvm/trunk/tools/sancov/symcov-report-server.py (with props)
Added: llvm/trunk/tools/sancov/symcov-report-server.py
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/sancov/symcov-report-server.py?rev=282637&view=auto
==============================================================================
--- llvm/trunk/tools/sancov/symcov-report-server.py (added)
+++ llvm/trunk/tools/sancov/symcov-report-server.py Wed Sep 28 16:27:58 2016
@@ -0,0 +1,203 @@
+#!/usr/bin/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()
Propchange: llvm/trunk/tools/sancov/symcov-report-server.py
------------------------------------------------------------------------------
svn:executable = *
More information about the llvm-commits
mailing list