blob: 072ae0991b25a82cac231f38d03510c07ab69033 [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
13import functools
14from collections import defaultdict
15import itertools
16from multiprocessing import Pool
17from multiprocessing import Lock, cpu_count
18import cgi
19import subprocess
20
21import traceback
22
23p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
24p_lock = Lock()
25
26
Brian Gesiak9b4e8972017-06-26 16:51:24 +000027try:
28 dict.iteritems
29except AttributeError:
30 # Python 3
31 def itervalues(d):
32 return iter(d.values())
33 def iteritems(d):
34 return iter(d.items())
35else:
36 # Python 2
37 def itervalues(d):
38 return d.itervalues()
39 def iteritems(d):
40 return d.iteritems()
41
42
Adam Nemetb7278af2017-03-01 21:35:00 +000043def demangle(name):
44 with p_lock:
Brian Gesiakd3a05712017-06-29 18:47:31 +000045 p.stdin.write((name + '\n').encode('utf-8'))
46 p.stdin.flush()
47 return p.stdout.readline().rstrip().decode('utf-8')
Adam Nemetb7278af2017-03-01 21:35:00 +000048
Brian Gesiak9b4e8972017-06-26 16:51:24 +000049
Adam Nemetb7278af2017-03-01 21:35:00 +000050def html_file_name(filename):
51 return filename.replace('/', '_') + ".html"
52
Brian Gesiak9b4e8972017-06-26 16:51:24 +000053
Adam Nemetb7278af2017-03-01 21:35:00 +000054def make_link(File, Line):
Filipe Cabecinhascc075642017-06-07 14:57:20 +000055 return "\"{}#L{}\"".format(html_file_name(File), Line)
Adam Nemetb7278af2017-03-01 21:35:00 +000056
57
58class Remark(yaml.YAMLObject):
59 # Work-around for http://pyyaml.org/ticket/154.
60 yaml_loader = Loader
61
Adam Nemeta8b692a2017-03-02 17:00:53 +000062 def initmissing(self):
63 if not hasattr(self, 'Hotness'):
64 self.Hotness = 0
65 if not hasattr(self, 'Args'):
66 self.Args = []
Adam Nemetb7278af2017-03-01 21:35:00 +000067
68 @property
69 def File(self):
70 return self.DebugLoc['File']
71
72 @property
73 def Line(self):
74 return int(self.DebugLoc['Line'])
75
76 @property
77 def Column(self):
78 return self.DebugLoc['Column']
79
80 @property
81 def DebugLocString(self):
82 return "{}:{}:{}".format(self.File, self.Line, self.Column)
83
84 @property
85 def DemangledFunctionName(self):
86 return demangle(self.Function)
87
88 @property
89 def Link(self):
90 return make_link(self.File, self.Line)
91
92 def getArgString(self, mapping):
93 mapping = mapping.copy()
94 dl = mapping.get('DebugLoc')
95 if dl:
96 del mapping['DebugLoc']
97
98 assert(len(mapping) == 1)
99 (key, value) = mapping.items()[0]
100
101 if key == 'Caller' or key == 'Callee':
102 value = cgi.escape(demangle(value))
103
104 if dl and key != 'Caller':
105 return "<a href={}>{}</a>".format(
106 make_link(dl['File'], dl['Line']), value)
107 else:
108 return value
109
Adam Nemet6ab2d482017-03-02 17:00:59 +0000110 def getDiffPrefix(self):
111 if hasattr(self, 'Added'):
112 if self.Added:
113 return '+'
114 else:
115 return '-'
116 return ''
117
118 @property
119 def PassWithDiffPrefix(self):
120 return self.getDiffPrefix() + self.Pass
121
Adam Nemetb7278af2017-03-01 21:35:00 +0000122 @property
123 def message(self):
124 # Args is a list of mappings (dictionaries)
125 values = [self.getArgString(mapping) for mapping in self.Args]
126 return "".join(values)
127
128 @property
129 def RelativeHotness(self):
130 if self.max_hotness:
131 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
132 else:
133 return ''
134
135 @property
136 def key(self):
Adam Nemet6ab2d482017-03-02 17:00:59 +0000137 k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
Adam Nemet7370dad2017-03-02 17:00:56 +0000138 for arg in self.Args:
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000139 for (key, value) in iteritems(arg):
Adam Nemet7370dad2017-03-02 17:00:56 +0000140 if type(value) is dict:
141 value = tuple(value.items())
142 k += (key, value)
143 return k
144
145 def __hash__(self):
146 return hash(self.key)
147
148 def __eq__(self, other):
149 return self.key == other.key
150
151 def __repr__(self):
152 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000153
154
155class Analysis(Remark):
156 yaml_tag = '!Analysis'
157
158 @property
159 def color(self):
160 return "white"
161
162
163class AnalysisFPCommute(Analysis):
164 yaml_tag = '!AnalysisFPCommute'
165
166
167class AnalysisAliasing(Analysis):
168 yaml_tag = '!AnalysisAliasing'
169
170
171class Passed(Remark):
172 yaml_tag = '!Passed'
173
174 @property
175 def color(self):
176 return "green"
177
178
179class Missed(Remark):
180 yaml_tag = '!Missed'
181
182 @property
183 def color(self):
184 return "red"
185
186
187def get_remarks(input_file):
188 max_hotness = 0
189 all_remarks = dict()
190 file_remarks = defaultdict(functools.partial(defaultdict, list))
191
192 with open(input_file) as f:
193 docs = yaml.load_all(f, Loader=Loader)
194 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000195 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000196 # Avoid remarks withoug debug location or if they are duplicated
197 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
198 continue
199 all_remarks[remark.key] = remark
200
201 file_remarks[remark.File][remark.Line].append(remark)
202
Adam Nemet6ab2d482017-03-02 17:00:59 +0000203 # If we're reading a back a diff yaml file, max_hotness is already
204 # captured which may actually be less than the max hotness found
205 # in the file.
206 if hasattr(remark, 'max_hotness'):
207 max_hotness = remark.max_hotness
Adam Nemetb7278af2017-03-01 21:35:00 +0000208 max_hotness = max(max_hotness, remark.Hotness)
209
210 return max_hotness, all_remarks, file_remarks
211
212
213def gather_results(pmap, filenames):
214 remarks = pmap(get_remarks, filenames)
215 max_hotness = max(entry[0] for entry in remarks)
216
217 def merge_file_remarks(file_remarks_job, all_remarks, merged):
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000218 for filename, d in iteritems(file_remarks_job):
219 for line, remarks in iteritems(d):
Adam Nemetb7278af2017-03-01 21:35:00 +0000220 for remark in remarks:
221 # Bring max_hotness into the remarks so that
222 # RelativeHotness does not depend on an external global.
223 remark.max_hotness = max_hotness
224 if remark.key not in all_remarks:
225 merged[filename][line].append(remark)
226
227 all_remarks = dict()
228 file_remarks = defaultdict(functools.partial(defaultdict, list))
229 for _, all_remarks_job, file_remarks_job in remarks:
230 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
231 all_remarks.update(all_remarks_job)
232
233 return all_remarks, file_remarks, max_hotness != 0