+
+''', file=self.stream)
+
+
+class IndexRenderer:
+ def __init__(self, output_dir):
+ self.stream = open(os.path.join(output_dir, 'index.html'), 'w')
+
+ def render_entry(self, r, odd):
+ escaped_name = cgi.escape(r.DemangledFunctionName)
+ print('''
+
'''.format(**locals()), file=self.stream)
+
+ def render(self, all_remarks):
+ print('''
+
+
+
+
+
+Source Location |
+Hotness |
+Function |
+Pass |
+
''', file=self.stream)
+ for i, remark in enumerate(all_remarks):
+ self.render_entry(remark, i % 2)
+ print('''
+
+
+''', file=self.stream)
+
+
+def _render_file(source_dir, output_dir, ctx, entry):
+ global context
+ context = ctx
+ filename, remarks = entry
+ SourceFileRenderer(source_dir, output_dir, filename).render(remarks)
+
+
+def map_remarks(all_remarks):
+ # Set up a map between function names and their source location for
+ # function where inlining happened
+ for remark in optrecord.itervalues(all_remarks):
+ if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined":
+ for arg in remark.Args:
+ caller = arg.get('Caller')
+ if caller:
+ context.caller_loc[caller] = arg['DebugLoc']
+
+
+def generate_report(all_remarks,
+ file_remarks,
+ source_dir,
+ output_dir,
+ should_display_hotness,
+ num_jobs,
+ should_print_progress):
+ try:
+ os.makedirs(output_dir)
+ except OSError as e:
+ if e.errno == errno.EEXIST and os.path.isdir(output_dir):
+ pass
+ else:
+ raise
+
+ _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context)
+ if should_print_progress:
+ print('Rendering HTML files...')
+ optpmap.pmap(_render_file_bound,
+ file_remarks.items(),
+ num_jobs,
+ should_print_progress)
+
+ if should_display_hotness:
+ sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True)
+ else:
+ sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function))
+ IndexRenderer(args.output_dir).render(sorted_remarks)
+
+ shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ "style.css"), output_dir)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('yaml_files', nargs='+')
+ parser.add_argument(
+ '--output-dir',
+ '-o',
+ default='html',
+ help='Path to a directory where generated HTML files will be output. '
+ 'If the directory does not already exist, it will be created. '
+ '"%(default)s" by default.')
+ parser.add_argument(
+ '--jobs',
+ '-j',
+ default=cpu_count(),
+ type=int,
+ help='Max job count (defaults to %(default)s, the current CPU count)')
+ parser.add_argument(
+ '-source-dir',
+ '-s',
+ default='',
+ help='set source directory')
+ parser.add_argument(
+ '--no-progress-indicator',
+ '-n',
+ action='store_true',
+ default=False,
+ help='Do not display any indicator of how many YAML files were read '
+ 'or rendered into HTML.')
+ args = parser.parse_args()
+
+ print_progress = not args.no_progress_indicator
+ all_remarks, file_remarks, should_display_hotness = \
+ optrecord.gather_results(args.yaml_files, args.jobs, print_progress)
+
+ map_remarks(all_remarks)
+
+ generate_report(all_remarks,
+ file_remarks,
+ args.source_dir,
+ args.output_dir,
+ should_display_hotness,
+ args.jobs,
+ print_progress)
Index: llvm/trunk/tools/opt-viewer/optpmap.py
===================================================================
--- llvm/trunk/tools/opt-viewer/optpmap.py
+++ llvm/trunk/tools/opt-viewer/optpmap.py
@@ -0,0 +1,53 @@
+import sys
+import multiprocessing
+
+
+_current = None
+_total = None
+
+
+def _init(current, total):
+ global _current
+ global _total
+ _current = current
+ _total = total
+
+
+def _wrapped_func(func_and_args):
+ func, argument, should_print_progress = func_and_args
+
+ if should_print_progress:
+ with _current.get_lock():
+ _current.value += 1
+ sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value))
+
+ return func(argument)
+
+
+def pmap(func, iterable, processes, should_print_progress, *args, **kwargs):
+ """
+ A parallel map function that reports on its progress.
+
+ Applies `func` to every item of `iterable` and return a list of the
+ results. If `processes` is greater than one, a process pool is used to run
+ the functions in parallel. `should_print_progress` is a boolean value that
+ indicates whether a string 'N of M' should be printed to indicate how many
+ of the functions have finished being run.
+ """
+ global _current
+ global _total
+ _current = multiprocessing.Value('i', 0)
+ _total = multiprocessing.Value('i', len(iterable))
+
+ func_and_args = [(func, arg, should_print_progress,) for arg in iterable]
+ if processes <= 1:
+ result = map(_wrapped_func, func_and_args, *args, **kwargs)
+ else:
+ pool = multiprocessing.Pool(initializer=_init,
+ initargs=(_current, _total,),
+ processes=processes)
+ result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
+
+ if should_print_progress:
+ sys.stdout.write('\r')
+ return result
Index: llvm/trunk/tools/opt-viewer/optrecord.py
===================================================================
--- llvm/trunk/tools/opt-viewer/optrecord.py
+++ llvm/trunk/tools/opt-viewer/optrecord.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+import yaml
+# Try to use the C parser.
+try:
+ from yaml import CLoader as Loader
+except ImportError:
+ print("For faster parsing, you may want to install libYAML for PyYAML")
+ from yaml import Loader
+
+import cgi
+from collections import defaultdict
+import functools
+from multiprocessing import Lock
+import subprocess
+
+import optpmap
+
+
+p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+p_lock = Lock()
+
+
+try:
+ dict.iteritems
+except AttributeError:
+ # Python 3
+ def itervalues(d):
+ return iter(d.values())
+ def iteritems(d):
+ return iter(d.items())
+else:
+ # Python 2
+ def itervalues(d):
+ return d.itervalues()
+ def iteritems(d):
+ return d.iteritems()
+
+
+def demangle(name):
+ with p_lock:
+ p.stdin.write((name + '\n').encode('utf-8'))
+ p.stdin.flush()
+ return p.stdout.readline().rstrip().decode('utf-8')
+
+
+def html_file_name(filename):
+ return filename.replace('/', '_') + ".html"
+
+
+def make_link(File, Line):
+ return "\"{}#L{}\"".format(html_file_name(File), Line)
+
+
+class Remark(yaml.YAMLObject):
+ # Work-around for http://pyyaml.org/ticket/154.
+ yaml_loader = Loader
+
+ def initmissing(self):
+ if not hasattr(self, 'Hotness'):
+ self.Hotness = 0
+ if not hasattr(self, 'Args'):
+ self.Args = []
+
+ @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']
+
+ @property
+ def DebugLocString(self):
+ return "{}:{}:{}".format(self.File, self.Line, self.Column)
+
+ @property
+ def DemangledFunctionName(self):
+ return demangle(self.Function)
+
+ @property
+ def Link(self):
+ return make_link(self.File, self.Line)
+
+ def getArgString(self, mapping):
+ mapping = mapping.copy()
+ dl = mapping.get('DebugLoc')
+ if dl:
+ del mapping['DebugLoc']
+
+ assert(len(mapping) == 1)
+ (key, value) = mapping.items()[0]
+
+ if key == 'Caller' or key == 'Callee':
+ value = cgi.escape(demangle(value))
+
+ if dl and key != 'Caller':
+ return "
{}".format(
+ make_link(dl['File'], dl['Line']), value)
+ else:
+ return value
+
+ def getDiffPrefix(self):
+ if hasattr(self, 'Added'):
+ if self.Added:
+ return '+'
+ else:
+ return '-'
+ return ''
+
+ @property
+ def PassWithDiffPrefix(self):
+ return self.getDiffPrefix() + self.Pass
+
+ @property
+ def message(self):
+ # Args is a list of mappings (dictionaries)
+ values = [self.getArgString(mapping) for mapping in self.Args]
+ return "".join(values)
+
+ @property
+ def RelativeHotness(self):
+ if self.max_hotness:
+ return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
+ else:
+ return ''
+
+ @property
+ def key(self):
+ k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
+ for arg in self.Args:
+ for (key, value) in iteritems(arg):
+ if type(value) is dict:
+ value = tuple(value.items())
+ k += (key, value)
+ return k
+
+ def __hash__(self):
+ return hash(self.key)
+
+ def __eq__(self, other):
+ return self.key == other.key
+
+ def __repr__(self):
+ return str(self.key)
+
+
+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"
+
+
+def get_remarks(input_file):
+ max_hotness = 0
+ all_remarks = dict()
+ file_remarks = defaultdict(functools.partial(defaultdict, list))
+
+ with open(input_file) as f:
+ docs = yaml.load_all(f, Loader=Loader)
+ for remark in docs:
+ remark.initmissing()
+ # Avoid remarks withoug debug location or if they are duplicated
+ if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
+ continue
+ all_remarks[remark.key] = remark
+
+ file_remarks[remark.File][remark.Line].append(remark)
+
+ # If we're reading a back a diff yaml file, max_hotness is already
+ # captured which may actually be less than the max hotness found
+ # in the file.
+ if hasattr(remark, 'max_hotness'):
+ max_hotness = remark.max_hotness
+ max_hotness = max(max_hotness, remark.Hotness)
+
+ return max_hotness, all_remarks, file_remarks
+
+
+def gather_results(filenames, num_jobs, should_print_progress):
+ if should_print_progress:
+ print('Reading YAML files...')
+ remarks = optpmap.pmap(
+ get_remarks, filenames, num_jobs, should_print_progress)
+ max_hotness = max(entry[0] for entry in remarks)
+
+ def merge_file_remarks(file_remarks_job, all_remarks, merged):
+ for filename, d in iteritems(file_remarks_job):
+ for line, remarks in iteritems(d):
+ for remark in remarks:
+ # Bring max_hotness into the remarks so that
+ # RelativeHotness does not depend on an external global.
+ remark.max_hotness = max_hotness
+ if remark.key not in all_remarks:
+ merged[filename][line].append(remark)
+
+ all_remarks = dict()
+ file_remarks = defaultdict(functools.partial(defaultdict, list))
+ for _, all_remarks_job, file_remarks_job in remarks:
+ merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
+ all_remarks.update(all_remarks_job)
+
+ return all_remarks, file_remarks, max_hotness != 0
Index: llvm/trunk/tools/opt-viewer/style.css
===================================================================
--- llvm/trunk/tools/opt-viewer/style.css
+++ llvm/trunk/tools/opt-viewer/style.css
@@ -0,0 +1,198 @@
+.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;
+}
+.column-entry-0 {
+ background-color: #ffffff;
+}
+.column-entry-1 {
+ background-color: #eeeeee;
+}
+.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;
+}
+
+/* Generated with pygmentize -S colorful -f html >> style.css */
+
+.hll { background-color: #ffffcc }
+.c { color: #888888 } /* Comment */
+.err { color: #FF0000; background-color: #FFAAAA } /* Error */
+.k { color: #008800; font-weight: bold } /* Keyword */
+.o { color: #333333 } /* Operator */
+.ch { color: #888888 } /* Comment.Hashbang */
+.cm { color: #888888 } /* Comment.Multiline */
+.cp { color: #557799 } /* Comment.Preproc */
+.cpf { color: #888888 } /* Comment.PreprocFile */
+.c1 { color: #888888 } /* Comment.Single */
+.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #888888 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0044DD } /* Generic.Traceback */
+.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
+.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
+.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
+.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
+.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #333399; font-weight: bold } /* Keyword.Type */
+.m { color: #6600EE; font-weight: bold } /* Literal.Number */
+.s { background-color: #fff0f0 } /* Literal.String */
+.na { color: #0000CC } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #BB0066; font-weight: bold } /* Name.Class */
+.no { color: #003366; font-weight: bold } /* Name.Constant */
+.nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.ni { color: #880000; font-weight: bold } /* Name.Entity */
+.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
+.nf { color: #0066BB; font-weight: bold } /* Name.Function */
+.nl { color: #997700; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #007700 } /* Name.Tag */
+.nv { color: #996633 } /* Name.Variable */
+.ow { color: #000000; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
+.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
+.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
+.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
+.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
+.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
+.sc { color: #0044DD } /* Literal.String.Char */
+.sd { color: #DD4422 } /* Literal.String.Doc */
+.s2 { background-color: #fff0f0 } /* Literal.String.Double */
+.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
+.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
+.si { background-color: #eeeeee } /* Literal.String.Interpol */
+.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
+.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
+.s1 { background-color: #fff0f0 } /* Literal.String.Single */
+.ss { color: #AA6600 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #336699 } /* Name.Variable.Class */
+.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
+.vi { color: #3333BB } /* Name.Variable.Instance */
+.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
Index: llvm/trunk/utils/opt-viewer/opt-diff.py
===================================================================
--- llvm/trunk/utils/opt-viewer/opt-diff.py
+++ llvm/trunk/utils/opt-viewer/opt-diff.py
@@ -1,71 +0,0 @@
-#!/usr/bin/env python2.7
-
-from __future__ import print_function
-
-desc = '''Generate the difference of two YAML files into a new YAML file (works on
-pair of directories too). A new attribute 'Added' is set to True or False
-depending whether the entry is added or removed from the first input to the
-next.
-
-The tools requires PyYAML.'''
-
-import yaml
-# Try to use the C parser.
-try:
- from yaml import CLoader as Loader
-except ImportError:
- from yaml import Loader
-
-import optrecord
-import argparse
-from collections import defaultdict
-from multiprocessing import cpu_count, Pool
-import os, os.path
-import fnmatch
-
-def find_files(dir_or_file):
- if os.path.isfile(dir_or_file):
- return [dir_or_file]
-
- all = []
- for dir, subdirs, files in os.walk(dir_or_file):
- for file in files:
- if fnmatch.fnmatch(file, "*.opt.yaml"):
- all.append( os.path.join(dir, file))
- return all
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=desc)
- parser.add_argument('yaml_dir_or_file_1')
- parser.add_argument('yaml_dir_or_file_2')
- parser.add_argument(
- '--jobs',
- '-j',
- default=cpu_count(),
- type=int,
- help='Max job count (defaults to %(default)s, the current CPU count)')
- parser.add_argument(
- '--no-progress-indicator',
- '-n',
- action='store_true',
- default=False,
- help='Do not display any indicator of how many YAML files were read.')
- parser.add_argument('--output', '-o', default='diff.opt.yaml')
- args = parser.parse_args()
-
- files1 = find_files(args.yaml_dir_or_file_1)
- files2 = find_files(args.yaml_dir_or_file_2)
-
- print_progress = not args.no_progress_indicator
- all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress)
- all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress)
-
- added = set(all_remarks2.values()) - set(all_remarks1.values())
- removed = set(all_remarks1.values()) - set(all_remarks2.values())
-
- for r in added:
- r.Added = True
- for r in removed:
- r.Added = False
- with open(args.output, 'w') as stream:
- yaml.dump_all(added | removed, stream)
Index: llvm/trunk/utils/opt-viewer/opt-stats.py
===================================================================
--- llvm/trunk/utils/opt-viewer/opt-stats.py
+++ llvm/trunk/utils/opt-viewer/opt-stats.py
@@ -1,56 +0,0 @@
-#!/usr/bin/env python2.7
-
-from __future__ import print_function
-
-desc = '''Generate statistics about optimization records from the YAML files
-generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
-
-The tools requires PyYAML and Pygments Python packages.'''
-
-import optrecord
-import argparse
-import operator
-from collections import defaultdict
-from multiprocessing import cpu_count, Pool
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=desc)
- parser.add_argument('yaml_files', nargs='+')
- parser.add_argument(
- '--jobs',
- '-j',
- default=cpu_count(),
- type=int,
- help='Max job count (defaults to %(default)s, the current CPU count)')
- parser.add_argument(
- '--no-progress-indicator',
- '-n',
- action='store_true',
- default=False,
- help='Do not display any indicator of how many YAML files were read.')
- args = parser.parse_args()
-
- print_progress = not args.no_progress_indicator
- all_remarks, file_remarks, _ = optrecord.gather_results(
- args.yaml_files, args.jobs, print_progress)
- if print_progress:
- print('\n')
-
- bypass = defaultdict(int)
- byname = defaultdict(int)
- for r in optrecord.itervalues(all_remarks):
- bypass[r.Pass] += 1
- byname[r.Pass + "/" + r.Name] += 1
-
- total = len(all_remarks)
- print("{:24s} {:10d}\n".format("Total number of remarks", total))
-
- print("Top 10 remarks by pass:")
- for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1),
- reverse=True)[:10]:
- print(" {:30s} {:2.0f}%". format(passname, count * 100. / total))
-
- print("\nTop 10 remarks:")
- for (name, count) in sorted(byname.items(), key=operator.itemgetter(1),
- reverse=True)[:10]:
- print(" {:30s} {:2.0f}%". format(name, count * 100. / total))
Index: llvm/trunk/utils/opt-viewer/opt-viewer.py
===================================================================
--- llvm/trunk/utils/opt-viewer/opt-viewer.py
+++ llvm/trunk/utils/opt-viewer/opt-viewer.py
@@ -1,259 +0,0 @@
-#!/usr/bin/env python2.7
-
-from __future__ import print_function
-
-import argparse
-import cgi
-import errno
-import functools
-from multiprocessing import cpu_count
-import os.path
-import re
-import shutil
-
-from pygments import highlight
-from pygments.lexers.c_cpp import CppLexer
-from pygments.formatters import HtmlFormatter
-
-import optpmap
-import optrecord
-
-
-desc = '''Generate HTML output to visualize optimization records from the YAML files
-generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
-
-The tools requires PyYAML and Pygments Python packages.'''
-
-
-# This allows passing the global context to the child processes.
-class Context:
- def __init__(self, caller_loc = dict()):
- # Map function names to their source location for function where inlining happened
- self.caller_loc = caller_loc
-
-context = Context()
-
-class SourceFileRenderer:
- def __init__(self, source_dir, output_dir, filename):
- existing_filename = None
- if os.path.exists(filename):
- existing_filename = filename
- else:
- fn = os.path.join(source_dir, filename)
- if os.path.exists(fn):
- existing_filename = fn
-
- self.stream = open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w')
- if existing_filename:
- self.source_stream = open(existing_filename)
- else:
- self.source_stream = None
- print('''
-
-
Unable to locate file {}
-
- '''.format(filename), file=self.stream)
-
- self.html_formatter = HtmlFormatter(encoding='utf-8')
- self.cpp_lexer = CppLexer(stripnl=False)
-
- def render_source_lines(self, stream, line_remarks):
- file_text = stream.read()
- html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter)
-
- # Take off the header and footer, these must be
- # reapplied line-wise, within the page structure
- html_highlighted = html_highlighted.replace('
', '')
- html_highlighted = html_highlighted.replace('
', '')
-
- for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1):
- print('''
-
-{linenum} |
- |
- |
- |
-
'''.format(**locals()), file=self.stream)
-
- for remark in line_remarks.get(linenum, []):
- self.render_inline_remarks(remark, html_line)
-
- def render_inline_remarks(self, r, line):
- inlining_context = r.DemangledFunctionName
- dl = context.caller_loc.get(r.Function)
- if dl:
- link = optrecord.make_link(dl['File'], dl['Line'] - 2)
- inlining_context = "
{r.DemangledFunctionName}".format(**locals())
-
- # Column is the number of characters *including* tabs, keep those and
- # replace everything else with spaces.
- indent = line[:max(r.Column, 1) - 1]
- indent = re.sub('\S', ' ', indent)
-
- print('''
-
- |
-{r.RelativeHotness} |
-{r.PassWithDiffPrefix} |
-{indent} {r.message} |
-{inlining_context} |
-
'''.format(**locals()), file=self.stream)
-
- def render(self, line_remarks):
- if not self.source_stream:
- return
-
- print('''
-
-
-
-
-
-
-
-
-Line |
-Hotness |
-Optimization |
-Source |
-Inline Context |
-
''', file=self.stream)
- self.render_source_lines(self.source_stream, line_remarks)
-
- print('''
-
-
-''', file=self.stream)
-
-
-class IndexRenderer:
- def __init__(self, output_dir):
- self.stream = open(os.path.join(output_dir, 'index.html'), 'w')
-
- def render_entry(self, r, odd):
- escaped_name = cgi.escape(r.DemangledFunctionName)
- print('''
-
-{r.DebugLocString} |
-{r.RelativeHotness} |
-{escaped_name} |
-{r.PassWithDiffPrefix} |
-
'''.format(**locals()), file=self.stream)
-
- def render(self, all_remarks):
- print('''
-
-
-
-
-
-
-
-
-Source Location |
-Hotness |
-Function |
-Pass |
-
''', file=self.stream)
- for i, remark in enumerate(all_remarks):
- self.render_entry(remark, i % 2)
- print('''
-
-
-''', file=self.stream)
-
-
-def _render_file(source_dir, output_dir, ctx, entry):
- global context
- context = ctx
- filename, remarks = entry
- SourceFileRenderer(source_dir, output_dir, filename).render(remarks)
-
-
-def map_remarks(all_remarks):
- # Set up a map between function names and their source location for
- # function where inlining happened
- for remark in optrecord.itervalues(all_remarks):
- if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined":
- for arg in remark.Args:
- caller = arg.get('Caller')
- if caller:
- context.caller_loc[caller] = arg['DebugLoc']
-
-
-def generate_report(all_remarks,
- file_remarks,
- source_dir,
- output_dir,
- should_display_hotness,
- num_jobs,
- should_print_progress):
- try:
- os.makedirs(output_dir)
- except OSError as e:
- if e.errno == errno.EEXIST and os.path.isdir(output_dir):
- pass
- else:
- raise
-
- _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context)
- if should_print_progress:
- print('Rendering HTML files...')
- optpmap.pmap(_render_file_bound,
- file_remarks.items(),
- num_jobs,
- should_print_progress)
-
- if should_display_hotness:
- sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True)
- else:
- sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function))
- IndexRenderer(args.output_dir).render(sorted_remarks)
-
- shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)),
- "style.css"), output_dir)
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=desc)
- parser.add_argument('yaml_files', nargs='+')
- parser.add_argument(
- '--output-dir',
- '-o',
- default='html',
- help='Path to a directory where generated HTML files will be output. '
- 'If the directory does not already exist, it will be created. '
- '"%(default)s" by default.')
- parser.add_argument(
- '--jobs',
- '-j',
- default=cpu_count(),
- type=int,
- help='Max job count (defaults to %(default)s, the current CPU count)')
- parser.add_argument(
- '-source-dir',
- '-s',
- default='',
- help='set source directory')
- parser.add_argument(
- '--no-progress-indicator',
- '-n',
- action='store_true',
- default=False,
- help='Do not display any indicator of how many YAML files were read '
- 'or rendered into HTML.')
- args = parser.parse_args()
-
- print_progress = not args.no_progress_indicator
- all_remarks, file_remarks, should_display_hotness = \
- optrecord.gather_results(args.yaml_files, args.jobs, print_progress)
-
- map_remarks(all_remarks)
-
- generate_report(all_remarks,
- file_remarks,
- args.source_dir,
- args.output_dir,
- should_display_hotness,
- args.jobs,
- print_progress)
Index: llvm/trunk/utils/opt-viewer/optpmap.py
===================================================================
--- llvm/trunk/utils/opt-viewer/optpmap.py
+++ llvm/trunk/utils/opt-viewer/optpmap.py
@@ -1,53 +0,0 @@
-import sys
-import multiprocessing
-
-
-_current = None
-_total = None
-
-
-def _init(current, total):
- global _current
- global _total
- _current = current
- _total = total
-
-
-def _wrapped_func(func_and_args):
- func, argument, should_print_progress = func_and_args
-
- if should_print_progress:
- with _current.get_lock():
- _current.value += 1
- sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value))
-
- return func(argument)
-
-
-def pmap(func, iterable, processes, should_print_progress, *args, **kwargs):
- """
- A parallel map function that reports on its progress.
-
- Applies `func` to every item of `iterable` and return a list of the
- results. If `processes` is greater than one, a process pool is used to run
- the functions in parallel. `should_print_progress` is a boolean value that
- indicates whether a string 'N of M' should be printed to indicate how many
- of the functions have finished being run.
- """
- global _current
- global _total
- _current = multiprocessing.Value('i', 0)
- _total = multiprocessing.Value('i', len(iterable))
-
- func_and_args = [(func, arg, should_print_progress,) for arg in iterable]
- if processes <= 1:
- result = map(_wrapped_func, func_and_args, *args, **kwargs)
- else:
- pool = multiprocessing.Pool(initializer=_init,
- initargs=(_current, _total,),
- processes=processes)
- result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
-
- if should_print_progress:
- sys.stdout.write('\r')
- return result
Index: llvm/trunk/utils/opt-viewer/optrecord.py
===================================================================
--- llvm/trunk/utils/opt-viewer/optrecord.py
+++ llvm/trunk/utils/opt-viewer/optrecord.py
@@ -1,235 +0,0 @@
-#!/usr/bin/env python2.7
-
-from __future__ import print_function
-
-import yaml
-# Try to use the C parser.
-try:
- from yaml import CLoader as Loader
-except ImportError:
- print("For faster parsing, you may want to install libYAML for PyYAML")
- from yaml import Loader
-
-import cgi
-from collections import defaultdict
-import functools
-from multiprocessing import Lock
-import subprocess
-
-import optpmap
-
-
-p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-p_lock = Lock()
-
-
-try:
- dict.iteritems
-except AttributeError:
- # Python 3
- def itervalues(d):
- return iter(d.values())
- def iteritems(d):
- return iter(d.items())
-else:
- # Python 2
- def itervalues(d):
- return d.itervalues()
- def iteritems(d):
- return d.iteritems()
-
-
-def demangle(name):
- with p_lock:
- p.stdin.write((name + '\n').encode('utf-8'))
- p.stdin.flush()
- return p.stdout.readline().rstrip().decode('utf-8')
-
-
-def html_file_name(filename):
- return filename.replace('/', '_') + ".html"
-
-
-def make_link(File, Line):
- return "\"{}#L{}\"".format(html_file_name(File), Line)
-
-
-class Remark(yaml.YAMLObject):
- # Work-around for http://pyyaml.org/ticket/154.
- yaml_loader = Loader
-
- def initmissing(self):
- if not hasattr(self, 'Hotness'):
- self.Hotness = 0
- if not hasattr(self, 'Args'):
- self.Args = []
-
- @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']
-
- @property
- def DebugLocString(self):
- return "{}:{}:{}".format(self.File, self.Line, self.Column)
-
- @property
- def DemangledFunctionName(self):
- return demangle(self.Function)
-
- @property
- def Link(self):
- return make_link(self.File, self.Line)
-
- def getArgString(self, mapping):
- mapping = mapping.copy()
- dl = mapping.get('DebugLoc')
- if dl:
- del mapping['DebugLoc']
-
- assert(len(mapping) == 1)
- (key, value) = mapping.items()[0]
-
- if key == 'Caller' or key == 'Callee':
- value = cgi.escape(demangle(value))
-
- if dl and key != 'Caller':
- return "
{}".format(
- make_link(dl['File'], dl['Line']), value)
- else:
- return value
-
- def getDiffPrefix(self):
- if hasattr(self, 'Added'):
- if self.Added:
- return '+'
- else:
- return '-'
- return ''
-
- @property
- def PassWithDiffPrefix(self):
- return self.getDiffPrefix() + self.Pass
-
- @property
- def message(self):
- # Args is a list of mappings (dictionaries)
- values = [self.getArgString(mapping) for mapping in self.Args]
- return "".join(values)
-
- @property
- def RelativeHotness(self):
- if self.max_hotness:
- return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
- else:
- return ''
-
- @property
- def key(self):
- k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
- for arg in self.Args:
- for (key, value) in iteritems(arg):
- if type(value) is dict:
- value = tuple(value.items())
- k += (key, value)
- return k
-
- def __hash__(self):
- return hash(self.key)
-
- def __eq__(self, other):
- return self.key == other.key
-
- def __repr__(self):
- return str(self.key)
-
-
-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"
-
-
-def get_remarks(input_file):
- max_hotness = 0
- all_remarks = dict()
- file_remarks = defaultdict(functools.partial(defaultdict, list))
-
- with open(input_file) as f:
- docs = yaml.load_all(f, Loader=Loader)
- for remark in docs:
- remark.initmissing()
- # Avoid remarks withoug debug location or if they are duplicated
- if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
- continue
- all_remarks[remark.key] = remark
-
- file_remarks[remark.File][remark.Line].append(remark)
-
- # If we're reading a back a diff yaml file, max_hotness is already
- # captured which may actually be less than the max hotness found
- # in the file.
- if hasattr(remark, 'max_hotness'):
- max_hotness = remark.max_hotness
- max_hotness = max(max_hotness, remark.Hotness)
-
- return max_hotness, all_remarks, file_remarks
-
-
-def gather_results(filenames, num_jobs, should_print_progress):
- if should_print_progress:
- print('Reading YAML files...')
- remarks = optpmap.pmap(
- get_remarks, filenames, num_jobs, should_print_progress)
- max_hotness = max(entry[0] for entry in remarks)
-
- def merge_file_remarks(file_remarks_job, all_remarks, merged):
- for filename, d in iteritems(file_remarks_job):
- for line, remarks in iteritems(d):
- for remark in remarks:
- # Bring max_hotness into the remarks so that
- # RelativeHotness does not depend on an external global.
- remark.max_hotness = max_hotness
- if remark.key not in all_remarks:
- merged[filename][line].append(remark)
-
- all_remarks = dict()
- file_remarks = defaultdict(functools.partial(defaultdict, list))
- for _, all_remarks_job, file_remarks_job in remarks:
- merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
- all_remarks.update(all_remarks_job)
-
- return all_remarks, file_remarks, max_hotness != 0
Index: llvm/trunk/utils/opt-viewer/style.css
===================================================================
--- llvm/trunk/utils/opt-viewer/style.css
+++ llvm/trunk/utils/opt-viewer/style.css
@@ -1,198 +0,0 @@
-.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;
-}
-.column-entry-0 {
- background-color: #ffffff;
-}
-.column-entry-1 {
- background-color: #eeeeee;
-}
-.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;
-}
-
-/* Generated with pygmentize -S colorful -f html >> style.css */
-
-.hll { background-color: #ffffcc }
-.c { color: #888888 } /* Comment */
-.err { color: #FF0000; background-color: #FFAAAA } /* Error */
-.k { color: #008800; font-weight: bold } /* Keyword */
-.o { color: #333333 } /* Operator */
-.ch { color: #888888 } /* Comment.Hashbang */
-.cm { color: #888888 } /* Comment.Multiline */
-.cp { color: #557799 } /* Comment.Preproc */
-.cpf { color: #888888 } /* Comment.PreprocFile */
-.c1 { color: #888888 } /* Comment.Single */
-.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
-.gd { color: #A00000 } /* Generic.Deleted */
-.ge { font-style: italic } /* Generic.Emph */
-.gr { color: #FF0000 } /* Generic.Error */
-.gh { color: #000080; font-weight: bold } /* Generic.Heading */
-.gi { color: #00A000 } /* Generic.Inserted */
-.go { color: #888888 } /* Generic.Output */
-.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
-.gs { font-weight: bold } /* Generic.Strong */
-.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
-.gt { color: #0044DD } /* Generic.Traceback */
-.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
-.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
-.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
-.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
-.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
-.kt { color: #333399; font-weight: bold } /* Keyword.Type */
-.m { color: #6600EE; font-weight: bold } /* Literal.Number */
-.s { background-color: #fff0f0 } /* Literal.String */
-.na { color: #0000CC } /* Name.Attribute */
-.nb { color: #007020 } /* Name.Builtin */
-.nc { color: #BB0066; font-weight: bold } /* Name.Class */
-.no { color: #003366; font-weight: bold } /* Name.Constant */
-.nd { color: #555555; font-weight: bold } /* Name.Decorator */
-.ni { color: #880000; font-weight: bold } /* Name.Entity */
-.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
-.nf { color: #0066BB; font-weight: bold } /* Name.Function */
-.nl { color: #997700; font-weight: bold } /* Name.Label */
-.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
-.nt { color: #007700 } /* Name.Tag */
-.nv { color: #996633 } /* Name.Variable */
-.ow { color: #000000; font-weight: bold } /* Operator.Word */
-.w { color: #bbbbbb } /* Text.Whitespace */
-.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
-.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
-.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
-.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
-.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
-.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
-.sc { color: #0044DD } /* Literal.String.Char */
-.sd { color: #DD4422 } /* Literal.String.Doc */
-.s2 { background-color: #fff0f0 } /* Literal.String.Double */
-.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
-.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
-.si { background-color: #eeeeee } /* Literal.String.Interpol */
-.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
-.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
-.s1 { background-color: #fff0f0 } /* Literal.String.Single */
-.ss { color: #AA6600 } /* Literal.String.Symbol */
-.bp { color: #007020 } /* Name.Builtin.Pseudo */
-.vc { color: #336699 } /* Name.Variable.Class */
-.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
-.vi { color: #3333BB } /* Name.Variable.Instance */
-.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */