blob: 6dc1a32e536a24e4e262d76aaa7d00a3ce07a0a5 [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:
45 p.stdin.write(name + '\n')
46 return p.stdout.readline().rstrip()
47
Brian Gesiak9b4e8972017-06-26 16:51:24 +000048
Adam Nemetb7278af2017-03-01 21:35:00 +000049def html_file_name(filename):
50 return filename.replace('/', '_') + ".html"
51
Brian Gesiak9b4e8972017-06-26 16:51:24 +000052
Adam Nemetb7278af2017-03-01 21:35:00 +000053def make_link(File, Line):
Filipe Cabecinhascc075642017-06-07 14:57:20 +000054 return "\"{}#L{}\"".format(html_file_name(File), Line)
Adam Nemetb7278af2017-03-01 21:35:00 +000055
56
57class Remark(yaml.YAMLObject):
58 # Work-around for http://pyyaml.org/ticket/154.
59 yaml_loader = Loader
60
Adam Nemeta8b692a2017-03-02 17:00:53 +000061 def initmissing(self):
62 if not hasattr(self, 'Hotness'):
63 self.Hotness = 0
64 if not hasattr(self, 'Args'):
65 self.Args = []
Adam Nemetb7278af2017-03-01 21:35:00 +000066
67 @property
68 def File(self):
69 return self.DebugLoc['File']
70
71 @property
72 def Line(self):
73 return int(self.DebugLoc['Line'])
74
75 @property
76 def Column(self):
77 return self.DebugLoc['Column']
78
79 @property
80 def DebugLocString(self):
81 return "{}:{}:{}".format(self.File, self.Line, self.Column)
82
83 @property
84 def DemangledFunctionName(self):
85 return demangle(self.Function)
86
87 @property
88 def Link(self):
89 return make_link(self.File, self.Line)
90
91 def getArgString(self, mapping):
92 mapping = mapping.copy()
93 dl = mapping.get('DebugLoc')
94 if dl:
95 del mapping['DebugLoc']
96
97 assert(len(mapping) == 1)
98 (key, value) = mapping.items()[0]
99
100 if key == 'Caller' or key == 'Callee':
101 value = cgi.escape(demangle(value))
102
103 if dl and key != 'Caller':
104 return "<a href={}>{}</a>".format(
105 make_link(dl['File'], dl['Line']), value)
106 else:
107 return value
108
Adam Nemet6ab2d482017-03-02 17:00:59 +0000109 def getDiffPrefix(self):
110 if hasattr(self, 'Added'):
111 if self.Added:
112 return '+'
113 else:
114 return '-'
115 return ''
116
117 @property
118 def PassWithDiffPrefix(self):
119 return self.getDiffPrefix() + self.Pass
120
Adam Nemetb7278af2017-03-01 21:35:00 +0000121 @property
122 def message(self):
123 # Args is a list of mappings (dictionaries)
124 values = [self.getArgString(mapping) for mapping in self.Args]
125 return "".join(values)
126
127 @property
128 def RelativeHotness(self):
129 if self.max_hotness:
130 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
131 else:
132 return ''
133
134 @property
135 def key(self):
Adam Nemet6ab2d482017-03-02 17:00:59 +0000136 k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
Adam Nemet7370dad2017-03-02 17:00:56 +0000137 for arg in self.Args:
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000138 for (key, value) in iteritems(arg):
Adam Nemet7370dad2017-03-02 17:00:56 +0000139 if type(value) is dict:
140 value = tuple(value.items())
141 k += (key, value)
142 return k
143
144 def __hash__(self):
145 return hash(self.key)
146
147 def __eq__(self, other):
148 return self.key == other.key
149
150 def __repr__(self):
151 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000152
153
154class Analysis(Remark):
155 yaml_tag = '!Analysis'
156
157 @property
158 def color(self):
159 return "white"
160
161
162class AnalysisFPCommute(Analysis):
163 yaml_tag = '!AnalysisFPCommute'
164
165
166class AnalysisAliasing(Analysis):
167 yaml_tag = '!AnalysisAliasing'
168
169
170class Passed(Remark):
171 yaml_tag = '!Passed'
172
173 @property
174 def color(self):
175 return "green"
176
177
178class Missed(Remark):
179 yaml_tag = '!Missed'
180
181 @property
182 def color(self):
183 return "red"
184
185
186def get_remarks(input_file):
187 max_hotness = 0
188 all_remarks = dict()
189 file_remarks = defaultdict(functools.partial(defaultdict, list))
190
191 with open(input_file) as f:
192 docs = yaml.load_all(f, Loader=Loader)
193 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000194 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000195 # Avoid remarks withoug debug location or if they are duplicated
196 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
197 continue
198 all_remarks[remark.key] = remark
199
200 file_remarks[remark.File][remark.Line].append(remark)
201
Adam Nemet6ab2d482017-03-02 17:00:59 +0000202 # If we're reading a back a diff yaml file, max_hotness is already
203 # captured which may actually be less than the max hotness found
204 # in the file.
205 if hasattr(remark, 'max_hotness'):
206 max_hotness = remark.max_hotness
Adam Nemetb7278af2017-03-01 21:35:00 +0000207 max_hotness = max(max_hotness, remark.Hotness)
208
209 return max_hotness, all_remarks, file_remarks
210
211
212def gather_results(pmap, filenames):
213 remarks = pmap(get_remarks, filenames)
214 max_hotness = max(entry[0] for entry in remarks)
215
216 def merge_file_remarks(file_remarks_job, all_remarks, merged):
Brian Gesiak9b4e8972017-06-26 16:51:24 +0000217 for filename, d in iteritems(file_remarks_job):
218 for line, remarks in iteritems(d):
Adam Nemetb7278af2017-03-01 21:35:00 +0000219 for remark in remarks:
220 # Bring max_hotness into the remarks so that
221 # RelativeHotness does not depend on an external global.
222 remark.max_hotness = max_hotness
223 if remark.key not in all_remarks:
224 merged[filename][line].append(remark)
225
226 all_remarks = dict()
227 file_remarks = defaultdict(functools.partial(defaultdict, list))
228 for _, all_remarks_job, file_remarks_job in remarks:
229 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
230 all_remarks.update(all_remarks_job)
231
232 return all_remarks, file_remarks, max_hotness != 0