Index: utils/opt-viewer/opt-viewer.py
===================================================================
--- /dev/null
+++ utils/opt-viewer/opt-viewer.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+desc = '''Generate HTML output to visualize optimization records from the YAML files
+generated with -fsave-optimization-record and -fdiagnostics-show-hotness.'''
+
+import yaml
+import argparse
+import os.path
+import subprocess
+import shutil
+
+parser = argparse.ArgumentParser(description=desc)
+parser.add_argument('yaml_files', nargs='+')
+parser.add_argument('output_dir')
+args = parser.parse_args()
+
+p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+def demangle(name):
+ p.stdin.write(name + '\n')
+ return p.stdout.readline().rstrip()
+
+class Remark(yaml.YAMLObject):
+ @property
+ def File(self):
+ return self.DebugLoc['File']
+
+ @property
+ def Line(self):
+ return int(self.DebugLoc['Line'])
+
+ @property
+ def Column(self):
+ return self.DebugLoc['Column']
+
+ def getDebugLoc(self):
+ return "{}:{}:{}".format(self.File, self.Line, self.Column)
+
+ def getLink(self):
+ return "{}#L{}".format(SourceFileRenderer.html_file_name(self.File), self.Line)
+
+ def getArgString(self, pair):
+ if pair[0] == 'Callee' or pair[0] == 'Caller':
+ return demangle(pair[1])
+ return pair[1]
+
+ @property
+ def message(self):
+ # Args is a list of mappings (dictionaries) with each dictionary with
+ # exactly one key-value pair.
+ values = [self.getArgString(mapping.items()[0]) for mapping in self.Args]
+ return demangle("".join(values))
+
+class Analysis(Remark):
+ yaml_tag = '!Analysis'
+
+ @property
+ def color(self): return "white"
+
+class AnalysisFPCommute(Analysis):
+ yaml_tag = '!AnalysisFPCommute'
+
+class AnalysisAliasing(Analysis):
+ yaml_tag = '!AnalysisAliasing'
+
+class Passed(Remark):
+ yaml_tag = '!Passed'
+
+ @property
+ def color(self): return "green"
+
+class Missed(Remark):
+ yaml_tag = '!Missed'
+
+ @property
+ def color(self): return "red"
+
+class SourceFileRenderer:
+ def __init__(self, filename):
+ self.source_stream = open(filename)
+ self.stream = open(os.path.join(args.output_dir, SourceFileRenderer.html_file_name(filename)), 'w')
+
+ def render_source_line(self, linenum, line):
+ print("
+
+
+Line
+ | Hotness
+ | Optimization
+ | Source |
''', file=self.stream)
+ for (linenum, line) in enumerate(self.source_stream.readlines(), start=1):
+ self.render_source_line(linenum, line)
+ for remark in line_remarks.get(linenum, []):
+ self.render_inline_remarks(remark)
+ print('''
+
+
+''', file=self.stream)
+
+ @classmethod
+ def html_file_name(cls, filename):
+ return filename.replace('/', '_') + ".html"
+
+class IndexRenderer:
+ def __init__(self):
+ self.stream = open(os.path.join(args.output_dir, 'index.html'), 'w')
+
+ def render_entry(self, remark):
+ html = SourceFileRenderer.html_file_name(remark.File)
+ link = "
{}".format(remark.getLink(), remark.getDebugLoc())
+
+ dem_name = demangle(remark.Function)
+ print("
{} | {} | {} | {} |
".format(
+ link,
+ remark.Hotness, dem_name, remark.color, remark.Pass), file=self.stream)
+
+ def render(self, all_remarks):
+ print('''
+
+
+
+
+
+
+
+
+Source Location
+ | Hotness
+ | Function
+ | Pass |
''', file=self.stream)
+ for remark in all_remarks:
+ self.render_entry(remark)
+ print('''
+
+
+''', file=self.stream)
+
+
+all_remarks = []
+file_remarks = dict()
+
+for input_file in args.yaml_files:
+ f = open(input_file)
+ docs = yaml.load_all(f)
+ for remark in docs:
+ if hasattr(remark, 'Hotness'):
+ file_remarks.setdefault(remark.File, dict()).setdefault(remark.Line, []).append(remark);
+ all_remarks.append(remark)
+
+all_remarks = sorted(all_remarks, key=lambda r: r.Hotness, reverse=True)
+
+if not os.path.exists(args.output_dir):
+ os.mkdir(args.output_dir)
+
+for (filename, remarks) in file_remarks.iteritems():
+ SourceFileRenderer(filename).render(remarks)
+
+IndexRenderer().render(all_remarks)
+
+shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), "style.css"), args.output_dir)
Index: utils/opt-viewer/style.css
===================================================================
--- /dev/null
+++ utils/opt-viewer/style.css
@@ -0,0 +1,125 @@
+.red {
+ background-color: #ffd0d0;
+}
+.cyan {
+ background-color: cyan;
+}
+body {
+ font-family: -apple-system, sans-serif;
+}
+pre {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+}
+.source-name-title {
+ padding: 5px 10px;
+ border-bottom: 1px solid #dbdbdb;
+ background-color: #eee;
+ line-height: 35px;
+}
+.centered {
+ display: table;
+ margin-left: left;
+ margin-right: auto;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+.expansion-view {
+ background-color: rgba(0, 0, 0, 0);
+ margin-left: 0px;
+ margin-top: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+table {
+ border-collapse: collapse;
+}
+.light-row {
+ background: #ffffff;
+ border: 1px solid #dbdbdb;
+}
+.column-entry {
+ text-align: right;
+}
+.column-entry-left {
+ text-align: left;
+}
+.column-entry-white {
+ text-align: right;
+ background-color: #ffffff;
+}
+.column-entry-red {
+ text-align: right;
+ background-color: #ffd0d0;
+}
+.column-entry-green {
+ text-align: right;
+ background-color: #d0ffd0;
+}
+.column-entry-yellow {
+ text-align: left;
+ background-color: #ffe1a6;
+}
+.line-number {
+ text-align: right;
+ color: #aaa;
+}
+.covered-line {
+ text-align: right;
+ color: #0080ff;
+}
+.uncovered-line {
+ text-align: right;
+ color: #ff3300;
+}
+.tooltip {
+ position: relative;
+ display: inline;
+ background-color: #b3e6ff;
+ text-decoration: none;
+}
+.tooltip span.tooltip-content {
+ position: absolute;
+ width: 100px;
+ margin-left: -50px;
+ color: #FFFFFF;
+ background: #000000;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 6px;
+}
+.tooltip span.tooltip-content:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0; height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+}
+:hover.tooltip span.tooltip-content {
+ visibility: visible;
+ opacity: 0.8;
+ bottom: 30px;
+ left: 50%;
+ z-index: 999;
+}
+th, td {
+ vertical-align: top;
+ padding: 2px 5px;
+ border-collapse: collapse;
+ border-right: solid 1px #eee;
+ border-left: solid 1px #eee;
+}
+td:first-child {
+ border-left: none;
+}
+td:last-child {
+ border-right: none;
+}