diff options
author | Daniel Dunbar <daniel@zuster.org> | 2008-09-19 23:32:11 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2008-09-19 23:32:11 +0000 |
commit | e33d3682b6f4ba798b9d8d6f395ac8003827c03b (patch) | |
tree | 793121bd08034fcf129504b31486ba6c74f5c340 /tools/scan-view/ScanView.py | |
parent | e43038ec3d0ef187fcdddf101ef653bbbdb1069a (diff) |
Add initial implementation of scan-view
- Web based interface to static analyzer.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56375 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/scan-view/ScanView.py')
-rw-r--r-- | tools/scan-view/ScanView.py | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/tools/scan-view/ScanView.py b/tools/scan-view/ScanView.py new file mode 100644 index 0000000000..e340a9fa6c --- /dev/null +++ b/tools/scan-view/ScanView.py @@ -0,0 +1,388 @@ +import BaseHTTPServer +import SimpleHTTPServer +import os +import sys +import urllib, urlparse +import posixpath +import StringIO +import re +import shutil +import threading +import time +import socket + +from Reporter import BugReport + +# Keys replaced by server. + +kReportBugRE = re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->') +kReportBugRepl = '<a class="ReportBugLink" href="report/\\1">Report Bug</a>' +kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') + +### + +__version__ = "0.1" + +__all__ = ["create_server"] + +class ReporterThread(threading.Thread): + def __init__(self, report, reporter, parameters, server): + threading.Thread.__init__(self) + self.report = report + self.server = server + self.reporter = reporter + self.parameters = parameters + self.status = None + + def run(self): + result = None + try: + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],) + result = self.reporter.fileReport(self.report, self.parameters) + time.sleep(3) + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],) + except Exception,e: + s = StringIO.StringIO() + import traceback + print >>s,'Submission Failed<br><pre>' + traceback.print_exc(e,file=s) + print >>s,'</pre>' + self.status = s.getvalue() + return + + s = StringIO.StringIO() + print >>s, 'Submission Complete!' + print >>s, '<hr>' + if result is not None: + print >>s, result + self.status = s.getvalue() + +class ScanViewServer(BaseHTTPServer.HTTPServer): + def __init__(self, address, handler, root, reporters, options): + BaseHTTPServer.HTTPServer.__init__(self, address, handler) + self.root = root + self.reporters = reporters + self.options = options + self.halted = False + + def halt(self): + self.halted = True + if self.options.debug: + print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],) + + def serve_forever(self): + while not self.halted: + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],) + try: + self.handle_request() + except OSError,e: + print 'OSError',e.errno + + def handle_error(self, request, client_address): + # Ignore socket errors + info = sys.exc_info() + if info and isinstance(info[1], socket.error): + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],) + return + BaseHTTPServer.HTTPServer.handle_error(request, client_address) + +# Borrowed from Quixote, with simplifications. +def parse_query(qs, fields=None): + if fields is None: + fields = {} + for chunk in filter(None, qs.split('&')): + if '=' not in chunk: + name = chunk + value = '' + else: + name, value = chunk.split('=', 1) + name = urllib.unquote(name.replace('+', ' ')) + value = urllib.unquote(value.replace('+', ' ')) + fields[name] = value + return fields + +class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + server_version = "ScanViewServer/" + __version__ + + def do_HEAD(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self) + except Exception,e: + self.handle_exception(e) + + def do_GET(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + except Exception,e: + self.handle_exception(e) + + def do_POST(self): + """Serve a POST request.""" + try: + length = self.headers.getheader('content-length') or "0" + try: + length = int(length) + except: + length = 0 + content = self.rfile.read(length) + fields = parse_query(content) + f = self.send_head(fields) + if f: + self.copyfile(f, self.wfile) + f.close() + except Exception,e: + self.handle_exception(e) + + def log_message(self, format, *args): + if self.server.options.debug: + sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % + (sys.argv[0], + self.address_string(), + self.log_date_time_string(), + format%args)) + + def load_report(self, report): + path = os.path.join(self.server.root, 'report-%s.html'%report) + data = open(path).read() + keys = {} + for item in kBugKeyValueRE.finditer(data): + k,v = item.groups() + keys[k] = v + return keys + + def handle_exception(self, exc): + import traceback + s = StringIO.StringIO() + print >>s, "INTERNAL ERROR\n" + traceback.print_exc(exc, s) + f = self.send_string(s.getvalue(), 'text/plain') + if f: + self.copyfile(f, self.wfile) + f.close() + + def send_internal_error(self, message): + return self.send_string('ERROR: %s'%(message,), 'text/plain') + + def send_report_submit(self): + s = StringIO.StringIO() + report = self.fields.get('report') + reporter = self.fields.get('reporter') + title = self.fields.get('title') + description = self.fields.get('description') + + # Get the reporter and parameters. + reporter = self.server.reporters[int(reporter)] + parameters = {} + for o in reporter.getParameterNames(): + name = '%s_%s'%(reporter.getName(),o) + parameters[o] = self.fields.get(name) + + # Create the report. + path = os.path.join(self.server.root, 'report-%s.html'%report) + files = [path] + br = BugReport(title, description, files) + + # Send back an initial response and wait for the report to + # finish. + initial_response = """<html> +<head><title>Filing Report</title></head> +<body> +<h1>Filing Report</h1> +<b>Report</b>: %(report)s<br> +<b>Title</b>: %(title)s<br> +<b>Description</b>: %(description)s<br> +<hr> +Submission in progress."""%locals() + + self.send_response(200) + self.send_header("Content-type", 'text/html') + self.end_headers() + self.wfile.write(initial_response) + self.wfile.flush() + + # Kick off a reporting thread. + t = ReporterThread(br, reporter, parameters, self.server) + t.start() + + # Wait for thread to die... + while t.isAlive(): + self.wfile.write('.') + self.wfile.flush() + time.sleep(.25) + submitStatus = t.status + + end_response = """<br> +%(submitStatus)s +<hr> +<a href="/">Home</a> +</body> +</html> +"""%locals() + return self.send_string(end_response, headers=False) + + def send_report(self, report): + try: + keys = self.load_report(report) + except IOError: + return self.send_internal_error('Invalid report.') + + initialTitle = keys.get('DESC','') + initialDescription = 'Bug generated by the clang static analyzer.' + + keysAndValues = '\n'.join(['<b>%s</b>: %s<br>'%(k,v) for k,v in keys.items()]) + reporterSelections = [] + reporterOptions = [] + + for i,r in enumerate(self.server.reporters): + reporterSelections.append('<option value="%d">%s</option>'%(i,r.getName())) + options = '\n'.join(['%s: <input type="text" name="%s_%s"><br>'%(o,r.getName(),o) for o in r.getParameterNames()]) + if i==0: + display = 'inline' + else: + display = 'none' + reporterOptions.append('<div id="%sReporterOptions" style="display:%s">\n<h3>%s Options</h3>%s\n</div>'%(r.getName(),display,r.getName(),options)) + reporterSelections = '\n'.join(reporterSelections) + reporterOptionsDivs = '\n'.join(reporterOptions) + reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters])) + + result = """<html> +<head> + <title>File Report</title> +</head> +<script language="javascript" type="text/javascript"> +var reporters = %(reportersArray)s; +function updateReporterOptions() { + index = document.getElementById('reporter').selectedIndex; + for (var i=0; i < reporters.length; ++i) { + o = document.getElementById(reporters[i] + "ReporterOptions"); + if (i == index) { + o.style.display = "inline"; + } else { + o.style.display = "none"; + } + } +} +</script> +<body> +<h1>File Report</h1> +%(keysAndValues)s +<hr> +<form name="form" action="/report_submit" method="post"> +Title: +<input type="text" name="title" size="50" value="%(initialTitle)s"><br> +Description:<br> +<textarea rows="10" cols="80" name="description"> +%(initialDescription)s +</textarea><br> +<hr> +<input type="hidden" name="report" value="%(report)s"> +Method: <select id="reporter" name="reporter" onChange="updateReporterOptions()"> +%(reporterSelections)s +</select><br> +<hr> +%(reporterOptionsDivs)s +<hr> +<input type="submit" name="Submit" value="Submit"> +</form> +</body> +</html>"""%locals() + return self.send_string(result) + + def send_head(self, fields=None): + if fields is None: + fields = {} + self.fields = fields + + o = urlparse.urlparse(self.path) + self.fields = parse_query(o.query, fields) + path = posixpath.normpath(urllib.unquote(o.path)) + + # Split the components and strip the root prefix. + components = path.split('/')[1:] + + # Special case some top-level entries. + if components: + name = components[0] + if name=='quit': + self.server.halt() + return self.send_string('Goodbye.', 'text/plain') + elif name=='report': + if len(components)==2: + return self.send_report(components[1]) + else: + return self.send_404() + elif name=='report_submit': + if len(components)==1: + return self.send_report_submit() + else: + return self.send_404() + + # Match directory entries. + if components[-1] == '': + components[-1] = 'index.html' + + path = posixpath.join(self.server.root, '/'.join(components)) + if self.server.options.debug > 1: + print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0], + path) + return self.send_path(path) + + def send_404(self): + self.send_error(404, "File not found") + return None + + def send_path(self, path): + ctype = self.guess_type(path) + if ctype.startswith('text/'): + # Patch file instead + return self.send_patched_file(path, ctype) + else: + mode = 'rb' + try: + f = open(path, mode) + except IOError: + return self.send_404() + return self.send_file(f, ctype) + + def send_file(self, f, ctype): + # Patch files to add links, but skip binary files. + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return f + + def send_string(self, s, ctype='text/html', headers=True, mtime=None): + if headers: + self.send_response(200) + self.send_header("Content-type", ctype) + self.send_header("Content-Length", str(len(s))) + if mtime: + self.send_header("Last-Modified", self.date_time_string(mtime)) + self.end_headers() + return StringIO.StringIO(s) + + def send_patched_file(self, path, ctype): + f = open(path,'r') + fs = os.fstat(f.fileno()) + data = f.read() + data = kReportBugRE.sub(kReportBugRepl, data) + return self.send_string(data, ctype, mtime=fs.st_mtime) + + +def create_server(options, root): + import Reporter + + reporters = Reporter.getReporters() + + return ScanViewServer((options.host, options.port), + ScanViewRequestHandler, + root, + reporters, + options) |