blob: adc2016017cc02ce45013d768da17cc6aa81296a [file] [log] [blame]
Adam Nemetb7278af2017-03-01 21:35:00 +00001#!/usr/bin/env python2.7
2
3from __future__ import print_function
4
5import yaml
6# Try to use the C parser.
7try:
8 from yaml import CLoader as Loader
9except ImportError:
10 print("For faster parsing, you may want to install libYAML for PyYAML")
11 from yaml import Loader
12
Adam Nemetb7278af2017-03-01 21:35:00 +000013import cgi
Brian Gesiak5e0a9462017-06-29 18:56:25 +000014from collections import defaultdict
Adam Nemet659d7db2017-07-17 18:00:41 +000015import fnmatch
Brian Gesiak5e0a9462017-06-29 18:56:25 +000016import functools
17from multiprocessing import Lock
Adam Nemet659d7db2017-07-17 18:00:41 +000018import os, os.path
Adam Nemetb7278af2017-03-01 21:35:00 +000019import subprocess
20
Brian Gesiak5e0a9462017-06-29 18:56:25 +000021import optpmap
22
Adam Nemetb7278af2017-03-01 21:35:00 +000023
24p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
25p_lock = Lock()
26
27
Brian Gesiak9b4e8972017-06-26 16:51:24 +000028try:
29 dict.iteritems
30except AttributeError:
31 # Python 3
32 def itervalues(d):
33 return iter(d.values())
34 def iteritems(d):
35 return iter(d.items())
36else:
37 # Python 2
38 def itervalues(d):
39 return d.itervalues()
40 def iteritems(d):
41 return d.iteritems()
42
43
Adam Nemetb7278af2017-03-01 21:35:00 +000044def demangle(name):
45 with p_lock:
Brian Gesiakd3a05712017-06-29 18:47:31 +000046 p.stdin.write((name + '\n').encode('utf-8'))
47 p.stdin.flush()
48 return p.stdout.readline().rstrip().decode('utf-8')
Adam Nemetb7278af2017-03-01 21:35:00 +000049
Brian Gesiak9b4e8972017-06-26 16:51:24 +000050
Adam Nemetb7278af2017-03-01 21:35:00 +000051def html_file_name(filename):
Brian Gesiaka8869972017-07-18 19:25:34 +000052 return filename.replace('/', '_').replace('#', '_') + ".html"
Adam Nemetb7278af2017-03-01 21:35:00 +000053
Brian Gesiak9b4e8972017-06-26 16:51:24 +000054
Adam Nemetb7278af2017-03-01 21:35:00 +000055def make_link(File, Line):
Filipe Cabecinhascc075642017-06-07 14:57:20 +000056 return "\"{}#L{}\"".format(html_file_name(File), Line)
Adam Nemetb7278af2017-03-01 21:35:00 +000057
58
59class Remark(yaml.YAMLObject):
60 # Work-around for http://pyyaml.org/ticket/154.
61 yaml_loader = Loader
62
Adam Nemet1d5f5b32017-07-19 22:04:56 +000063 def _intern_strings(self):
64 self.Pass = intern(self.Pass)
65 self.Name = intern(self.Name)
66 self.Function = intern(self.Function)
67
68 # Intern key and value if string and recurse if value is a dictionary.
69 # This handles [{'Caller': ..., 'DebugLoc': { 'File': ... }}]
70 def _intern_dict(old_dict):
71 new_dict = dict()
72 for (k, v) in old_dict.iteritems():
73 if type(k) is str:
74 k = intern(k)
75
76 if type(v) is str:
77 v = intern(v)
78 elif type(v) is dict:
79 v = _intern_dict(v)
80 new_dict[k] = v
81 return new_dict
82
83 self.Args = [_intern_dict(arg_dict) for arg_dict in self.Args]
84
85 def canonicalize(self):
Adam Nemeta8b692a2017-03-02 17:00:53 +000086 if not hasattr(self, 'Hotness'):
87 self.Hotness = 0
88 if not hasattr(self, 'Args'):
89 self.Args = []
Adam Nemet1d5f5b32017-07-19 22:04:56 +000090 self._intern_strings()
Adam Nemetb7278af2017-03-01 21:35:00 +000091
92 @property
93 def File(self):
94 return self.DebugLoc['File']
95
96 @property
97 def Line(self):
98 return int(self.DebugLoc['Line'])
99
100 @property
101 def Column(self):
102 return self.DebugLoc['Column']
103
104 @property
105 def DebugLocString(self):
106 return "{}:{}:{}".format(self.File, self.Line, self.Column)
107
108 @property
109 def DemangledFunctionName(self):
110 return demangle(self.Function)
111
112 @property
113 def Link(self):
114 return make_link(self.File, self.Line)
115
116 def getArgString(self, mapping):
117 mapping = mapping.copy()
118 dl = mapping.get('DebugLoc')
119 if dl:
120 del mapping['DebugLoc']
121
122 assert(len(mapping) == 1)
123 (key, value) = mapping.items()[0]
124
125 if key == 'Caller' or key == 'Callee':
126 value = cgi.escape(demangle(value))
127
128 if dl and key != 'Caller':
129 return "<a href={}>{}</a>".format(
130 make_link(dl['File'], dl['Line']), value)
131 else:
132 return value
133
Adam Nemet6ab2d482017-03-02 17:00:59 +0000134 def getDiffPrefix(self):
135 if hasattr(self, 'Added'):
136 if self.Added:
137 return '+'
138 else:
139 return '-'
140 return ''
141
142 @property
143 def PassWithDiffPrefix(self):
144 return self.getDiffPrefix() + self.Pass
145
Adam Nemetb7278af2017-03-01 21:35:00 +0000146 @property
147 def message(self):
148 # Args is a list of mappings (dictionaries)
149 values = [self.getArgString(mapping) for mapping in self.Args]
150 return "".join(values)
151
152 @property
153 def RelativeHotness(self):
154 if self.max_hotness:
155 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
156 else:
157 return ''
158
159 @property
160 def key(self):
Adam Nemet6ab2d482017-03-02 17:00:59 +0000161 k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
Adam Nemet7370dad2017-03-02 17:00:56 +0000162 for arg in self.Args:
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000163 for (key, value) in iteritems(arg):
Adam Nemet7370dad2017-03-02 17:00:56 +0000164 if type(value) is dict:
165 value = tuple(value.items())
166 k += (key, value)
167 return k
168
169 def __hash__(self):
170 return hash(self.key)
171
172 def __eq__(self, other):
173 return self.key == other.key
174
175 def __repr__(self):
176 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000177
178
179class Analysis(Remark):
180 yaml_tag = '!Analysis'
181
182 @property
183 def color(self):
184 return "white"
185
186
187class AnalysisFPCommute(Analysis):
188 yaml_tag = '!AnalysisFPCommute'
189
190
191class AnalysisAliasing(Analysis):
192 yaml_tag = '!AnalysisAliasing'
193
194
195class Passed(Remark):
196 yaml_tag = '!Passed'
197
198 @property
199 def color(self):
200 return "green"
201
202
203class Missed(Remark):
204 yaml_tag = '!Missed'
205
206 @property
207 def color(self):
208 return "red"
209
210
211def get_remarks(input_file):
212 max_hotness = 0
213 all_remarks = dict()
214 file_remarks = defaultdict(functools.partial(defaultdict, list))
215
216 with open(input_file) as f:
217 docs = yaml.load_all(f, Loader=Loader)
218 for remark in docs:
Adam Nemet1d5f5b32017-07-19 22:04:56 +0000219 remark.canonicalize()
Adam Nemetb7278af2017-03-01 21:35:00 +0000220 # Avoid remarks withoug debug location or if they are duplicated
221 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
222 continue
223 all_remarks[remark.key] = remark
224
225 file_remarks[remark.File][remark.Line].append(remark)
226
Adam Nemet6ab2d482017-03-02 17:00:59 +0000227 # If we're reading a back a diff yaml file, max_hotness is already
228 # captured which may actually be less than the max hotness found
229 # in the file.
230 if hasattr(remark, 'max_hotness'):
231 max_hotness = remark.max_hotness
Adam Nemetb7278af2017-03-01 21:35:00 +0000232 max_hotness = max(max_hotness, remark.Hotness)
233
234 return max_hotness, all_remarks, file_remarks
235
236
Brian Gesiak5e0a9462017-06-29 18:56:25 +0000237def gather_results(filenames, num_jobs, should_print_progress):
238 if should_print_progress:
239 print('Reading YAML files...')
240 remarks = optpmap.pmap(
241 get_remarks, filenames, num_jobs, should_print_progress)
Adam Nemetb7278af2017-03-01 21:35:00 +0000242 max_hotness = max(entry[0] for entry in remarks)
243
244 def merge_file_remarks(file_remarks_job, all_remarks, merged):
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000245 for filename, d in iteritems(file_remarks_job):
246 for line, remarks in iteritems(d):
Adam Nemetb7278af2017-03-01 21:35:00 +0000247 for remark in remarks:
248 # Bring max_hotness into the remarks so that
249 # RelativeHotness does not depend on an external global.
250 remark.max_hotness = max_hotness
251 if remark.key not in all_remarks:
252 merged[filename][line].append(remark)
253
254 all_remarks = dict()
255 file_remarks = defaultdict(functools.partial(defaultdict, list))
256 for _, all_remarks_job, file_remarks_job in remarks:
257 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
258 all_remarks.update(all_remarks_job)
259
260 return all_remarks, file_remarks, max_hotness != 0
Adam Nemet659d7db2017-07-17 18:00:41 +0000261
262
263def find_opt_files(dirs_or_files):
264 all = []
265 for dir_or_file in dirs_or_files:
266 if os.path.isfile(dir_or_file):
267 all.append(dir_or_file)
268 else:
269 for dir, subdirs, files in os.walk(dir_or_file):
270 # Exclude mounted directories and symlinks (os.walk default).
271 subdirs[:] = [d for d in subdirs
272 if not os.path.ismount(os.path.join(dir, d))]
273 for file in files:
274 if fnmatch.fnmatch(file, "*.opt.yaml"):
275 all.append(os.path.join(dir, file))
276 return all