blob: 1cafe0fe0364b24a13bd017ed56d56e30bb7acba [file] [log] [blame]
Adam Nemet848556a2016-10-07 17:06:34 +00001#!/usr/bin/env python2.7
2
3from __future__ import print_function
4
5desc = '''Generate HTML output to visualize optimization records from the YAML files
6generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
7
8The tools requires PyYAML to be installed.'''
9
10import yaml
11import argparse
12import os.path
13import subprocess
14import shutil
15
16parser = argparse.ArgumentParser(description=desc)
17parser.add_argument('yaml_files', nargs='+')
18parser.add_argument('output_dir')
19args = parser.parse_args()
20
21p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
22def demangle(name):
23 p.stdin.write(name + '\n')
24 return p.stdout.readline().rstrip()
25
26class Remark(yaml.YAMLObject):
Adam Nemete4375042016-10-11 16:20:38 +000027 max_hotness = 0
28
Adam Nemet848556a2016-10-07 17:06:34 +000029 @property
30 def File(self):
31 return self.DebugLoc['File']
32
33 @property
34 def Line(self):
35 return int(self.DebugLoc['Line'])
36
37 @property
38 def Column(self):
39 return self.DebugLoc['Column']
40
Adam Nemet4415e962016-10-11 16:19:06 +000041 @property
42 def DebugLocString(self):
Adam Nemet848556a2016-10-07 17:06:34 +000043 return "{}:{}:{}".format(self.File, self.Line, self.Column)
44
Adam Nemet4415e962016-10-11 16:19:06 +000045 @property
46 def DemangledFunctionName(self):
47 return demangle(self.Function)
48
49 @property
50 def Link(self):
Adam Nemet848556a2016-10-07 17:06:34 +000051 return "{}#L{}".format(SourceFileRenderer.html_file_name(self.File), self.Line)
52
53 def getArgString(self, pair):
54 if pair[0] == 'Callee' or pair[0] == 'Caller':
55 return demangle(pair[1])
56 return pair[1]
57
58 @property
59 def message(self):
60 # Args is a list of mappings (dictionaries) with each dictionary with
61 # exactly one key-value pair.
62 values = [self.getArgString(mapping.items()[0]) for mapping in self.Args]
Adam Nemet3996caa2016-10-11 16:20:40 +000063 return "".join(values)
Adam Nemet848556a2016-10-07 17:06:34 +000064
Adam Nemete4375042016-10-11 16:20:38 +000065 @property
66 def RelativeHotness(self):
67 return int(round(self.Hotness * 100 / Remark.max_hotness))
68
Adam Nemet848556a2016-10-07 17:06:34 +000069class Analysis(Remark):
70 yaml_tag = '!Analysis'
71
72 @property
73 def color(self): return "white"
74
75class AnalysisFPCommute(Analysis):
76 yaml_tag = '!AnalysisFPCommute'
77
78class AnalysisAliasing(Analysis):
79 yaml_tag = '!AnalysisAliasing'
80
81class Passed(Remark):
82 yaml_tag = '!Passed'
83
84 @property
85 def color(self): return "green"
86
87class Missed(Remark):
88 yaml_tag = '!Missed'
89
90 @property
91 def color(self): return "red"
92
93class SourceFileRenderer:
94 def __init__(self, filename):
95 self.source_stream = open(filename)
96 self.stream = open(os.path.join(args.output_dir, SourceFileRenderer.html_file_name(filename)), 'w')
97
98 def render_source_line(self, linenum, line):
99 print('''
100<tr>
101<td><a name=\"L{linenum}\">{linenum}</a></td>
102<td></td>
103<td></td>
104<td><pre>{line}</pre></td>
105</tr>'''.format(**locals()), file=self.stream)
106
107 def render_inline_remarks(self, r):
108 print('''
109<tr>
110<td></td>
Adam Nemete4375042016-10-11 16:20:38 +0000111<td>{r.RelativeHotness}%</td>
Adam Nemet848556a2016-10-07 17:06:34 +0000112<td class=\"column-entry-{r.color}\">{r.Pass}</td>
113<td class=\"column-entry-yellow\">{r.message}</td>
114</tr>'''.format(**locals()), file=self.stream)
115
116 def render(self, line_remarks):
117 print('''
118<html>
119<head>
120<link rel='stylesheet' type='text/css' href='style.css'>
121</head>
122<body>
123<div class="centered">
124<table>
125<tr>
126<td>Line</td>
127<td>Hotness</td>
128<td>Optimization</td>
129<td>Source</td>
130</tr>''', file=self.stream)
131 for (linenum, line) in enumerate(self.source_stream.readlines(), start=1):
132 self.render_source_line(linenum, line)
133 for remark in line_remarks.get(linenum, []):
134 self.render_inline_remarks(remark)
135 print('''
136</table>
137</body>
138</html>''', file=self.stream)
139
140 @classmethod
141 def html_file_name(cls, filename):
142 return filename.replace('/', '_') + ".html"
143
144class IndexRenderer:
145 def __init__(self):
146 self.stream = open(os.path.join(args.output_dir, 'index.html'), 'w')
147
Adam Nemet4415e962016-10-11 16:19:06 +0000148 def render_entry(self, r):
149 print('''
150<tr>
151<td><a href={r.Link}>{r.DebugLocString}</a></td>
Adam Nemete4375042016-10-11 16:20:38 +0000152<td>{r.RelativeHotness}%</td>
Adam Nemet4415e962016-10-11 16:19:06 +0000153<td>{r.DemangledFunctionName}</td>
154<td class=\"column-entry-{r.color}\">{r.Pass}</td>
155</tr>'''.format(**locals()), file=self.stream)
Adam Nemet848556a2016-10-07 17:06:34 +0000156
157 def render(self, all_remarks):
158 print('''
159<html>
160<head>
161<link rel='stylesheet' type='text/css' href='style.css'>
162</head>
163<body>
164<div class="centered">
165<table>
166<tr>
167<td>Source Location</td>
168<td>Hotness</td>
169<td>Function</td>
170<td>Pass</td>
171</tr>''', file=self.stream)
172 for remark in all_remarks:
173 self.render_entry(remark)
174 print('''
175</table>
176</body>
177</html>''', file=self.stream)
178
179
180all_remarks = []
181file_remarks = dict()
182
183for input_file in args.yaml_files:
184 f = open(input_file)
185 docs = yaml.load_all(f)
186 for remark in docs:
187 if hasattr(remark, 'Hotness'):
188 file_remarks.setdefault(remark.File, dict()).setdefault(remark.Line, []).append(remark);
189 all_remarks.append(remark)
Adam Nemete4375042016-10-11 16:20:38 +0000190 Remark.max_hotness = max(Remark.max_hotness, remark.Hotness)
Adam Nemet848556a2016-10-07 17:06:34 +0000191
192all_remarks = sorted(all_remarks, key=lambda r: r.Hotness, reverse=True)
193
194if not os.path.exists(args.output_dir):
195 os.mkdir(args.output_dir)
196
197for (filename, remarks) in file_remarks.iteritems():
198 SourceFileRenderer(filename).render(remarks)
199
200IndexRenderer().render(all_remarks)
201
202shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), "style.css"), args.output_dir)