blob: 4599e12d7e68844223ff62199e9952c7cc394ae6 [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 Nemeta8b692a2017-03-02 17:00:53 +000063 def initmissing(self):
64 if not hasattr(self, 'Hotness'):
65 self.Hotness = 0
66 if not hasattr(self, 'Args'):
67 self.Args = []
Adam Nemetb7278af2017-03-01 21:35:00 +000068
69 @property
70 def File(self):
71 return self.DebugLoc['File']
72
73 @property
74 def Line(self):
75 return int(self.DebugLoc['Line'])
76
77 @property
78 def Column(self):
79 return self.DebugLoc['Column']
80
81 @property
82 def DebugLocString(self):
83 return "{}:{}:{}".format(self.File, self.Line, self.Column)
84
85 @property
86 def DemangledFunctionName(self):
87 return demangle(self.Function)
88
89 @property
90 def Link(self):
91 return make_link(self.File, self.Line)
92
93 def getArgString(self, mapping):
94 mapping = mapping.copy()
95 dl = mapping.get('DebugLoc')
96 if dl:
97 del mapping['DebugLoc']
98
99 assert(len(mapping) == 1)
100 (key, value) = mapping.items()[0]
101
102 if key == 'Caller' or key == 'Callee':
103 value = cgi.escape(demangle(value))
104
105 if dl and key != 'Caller':
106 return "<a href={}>{}</a>".format(
107 make_link(dl['File'], dl['Line']), value)
108 else:
109 return value
110
Adam Nemet6ab2d482017-03-02 17:00:59 +0000111 def getDiffPrefix(self):
112 if hasattr(self, 'Added'):
113 if self.Added:
114 return '+'
115 else:
116 return '-'
117 return ''
118
119 @property
120 def PassWithDiffPrefix(self):
121 return self.getDiffPrefix() + self.Pass
122
Adam Nemetb7278af2017-03-01 21:35:00 +0000123 @property
124 def message(self):
125 # Args is a list of mappings (dictionaries)
126 values = [self.getArgString(mapping) for mapping in self.Args]
127 return "".join(values)
128
129 @property
130 def RelativeHotness(self):
131 if self.max_hotness:
132 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
133 else:
134 return ''
135
136 @property
137 def key(self):
Adam Nemet6ab2d482017-03-02 17:00:59 +0000138 k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
Adam Nemet7370dad2017-03-02 17:00:56 +0000139 for arg in self.Args:
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000140 for (key, value) in iteritems(arg):
Adam Nemet7370dad2017-03-02 17:00:56 +0000141 if type(value) is dict:
142 value = tuple(value.items())
143 k += (key, value)
144 return k
145
146 def __hash__(self):
147 return hash(self.key)
148
149 def __eq__(self, other):
150 return self.key == other.key
151
152 def __repr__(self):
153 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000154
155
156class Analysis(Remark):
157 yaml_tag = '!Analysis'
158
159 @property
160 def color(self):
161 return "white"
162
163
164class AnalysisFPCommute(Analysis):
165 yaml_tag = '!AnalysisFPCommute'
166
167
168class AnalysisAliasing(Analysis):
169 yaml_tag = '!AnalysisAliasing'
170
171
172class Passed(Remark):
173 yaml_tag = '!Passed'
174
175 @property
176 def color(self):
177 return "green"
178
179
180class Missed(Remark):
181 yaml_tag = '!Missed'
182
183 @property
184 def color(self):
185 return "red"
186
187
188def get_remarks(input_file):
189 max_hotness = 0
190 all_remarks = dict()
191 file_remarks = defaultdict(functools.partial(defaultdict, list))
192
193 with open(input_file) as f:
194 docs = yaml.load_all(f, Loader=Loader)
195 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000196 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000197 # Avoid remarks withoug debug location or if they are duplicated
198 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
199 continue
200 all_remarks[remark.key] = remark
201
202 file_remarks[remark.File][remark.Line].append(remark)
203
Adam Nemet6ab2d482017-03-02 17:00:59 +0000204 # If we're reading a back a diff yaml file, max_hotness is already
205 # captured which may actually be less than the max hotness found
206 # in the file.
207 if hasattr(remark, 'max_hotness'):
208 max_hotness = remark.max_hotness
Adam Nemetb7278af2017-03-01 21:35:00 +0000209 max_hotness = max(max_hotness, remark.Hotness)
210
211 return max_hotness, all_remarks, file_remarks
212
213
Brian Gesiak5e0a9462017-06-29 18:56:25 +0000214def gather_results(filenames, num_jobs, should_print_progress):
215 if should_print_progress:
216 print('Reading YAML files...')
217 remarks = optpmap.pmap(
218 get_remarks, filenames, num_jobs, should_print_progress)
Adam Nemetb7278af2017-03-01 21:35:00 +0000219 max_hotness = max(entry[0] for entry in remarks)
220
221 def merge_file_remarks(file_remarks_job, all_remarks, merged):
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000222 for filename, d in iteritems(file_remarks_job):
223 for line, remarks in iteritems(d):
Adam Nemetb7278af2017-03-01 21:35:00 +0000224 for remark in remarks:
225 # Bring max_hotness into the remarks so that
226 # RelativeHotness does not depend on an external global.
227 remark.max_hotness = max_hotness
228 if remark.key not in all_remarks:
229 merged[filename][line].append(remark)
230
231 all_remarks = dict()
232 file_remarks = defaultdict(functools.partial(defaultdict, list))
233 for _, all_remarks_job, file_remarks_job in remarks:
234 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
235 all_remarks.update(all_remarks_job)
236
237 return all_remarks, file_remarks, max_hotness != 0
Adam Nemet659d7db2017-07-17 18:00:41 +0000238
239
240def find_opt_files(dirs_or_files):
241 all = []
242 for dir_or_file in dirs_or_files:
243 if os.path.isfile(dir_or_file):
244 all.append(dir_or_file)
245 else:
246 for dir, subdirs, files in os.walk(dir_or_file):
247 # Exclude mounted directories and symlinks (os.walk default).
248 subdirs[:] = [d for d in subdirs
249 if not os.path.ismount(os.path.join(dir, d))]
250 for file in files:
251 if fnmatch.fnmatch(file, "*.opt.yaml"):
252 all.append(os.path.join(dir, file))
253 return all