blob: 2f930a48a056edf01015b8b34dcb5b42403990cd [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
27def demangle(name):
28 with p_lock:
29 p.stdin.write(name + '\n')
30 return p.stdout.readline().rstrip()
31
32def html_file_name(filename):
33 return filename.replace('/', '_') + ".html"
34
35def make_link(File, Line):
Filipe Cabecinhascc075642017-06-07 14:57:20 +000036 return "\"{}#L{}\"".format(html_file_name(File), Line)
Adam Nemetb7278af2017-03-01 21:35:00 +000037
38
39class Remark(yaml.YAMLObject):
40 # Work-around for http://pyyaml.org/ticket/154.
41 yaml_loader = Loader
42
Adam Nemeta8b692a2017-03-02 17:00:53 +000043 def initmissing(self):
44 if not hasattr(self, 'Hotness'):
45 self.Hotness = 0
46 if not hasattr(self, 'Args'):
47 self.Args = []
Adam Nemetb7278af2017-03-01 21:35:00 +000048
49 @property
50 def File(self):
51 return self.DebugLoc['File']
52
53 @property
54 def Line(self):
55 return int(self.DebugLoc['Line'])
56
57 @property
58 def Column(self):
59 return self.DebugLoc['Column']
60
61 @property
62 def DebugLocString(self):
63 return "{}:{}:{}".format(self.File, self.Line, self.Column)
64
65 @property
66 def DemangledFunctionName(self):
67 return demangle(self.Function)
68
69 @property
70 def Link(self):
71 return make_link(self.File, self.Line)
72
73 def getArgString(self, mapping):
74 mapping = mapping.copy()
75 dl = mapping.get('DebugLoc')
76 if dl:
77 del mapping['DebugLoc']
78
79 assert(len(mapping) == 1)
80 (key, value) = mapping.items()[0]
81
82 if key == 'Caller' or key == 'Callee':
83 value = cgi.escape(demangle(value))
84
85 if dl and key != 'Caller':
86 return "<a href={}>{}</a>".format(
87 make_link(dl['File'], dl['Line']), value)
88 else:
89 return value
90
Adam Nemet6ab2d482017-03-02 17:00:59 +000091 def getDiffPrefix(self):
92 if hasattr(self, 'Added'):
93 if self.Added:
94 return '+'
95 else:
96 return '-'
97 return ''
98
99 @property
100 def PassWithDiffPrefix(self):
101 return self.getDiffPrefix() + self.Pass
102
Adam Nemetb7278af2017-03-01 21:35:00 +0000103 @property
104 def message(self):
105 # Args is a list of mappings (dictionaries)
106 values = [self.getArgString(mapping) for mapping in self.Args]
107 return "".join(values)
108
109 @property
110 def RelativeHotness(self):
111 if self.max_hotness:
112 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
113 else:
114 return ''
115
116 @property
117 def key(self):
Adam Nemet6ab2d482017-03-02 17:00:59 +0000118 k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
Adam Nemet7370dad2017-03-02 17:00:56 +0000119 for arg in self.Args:
120 for (key, value) in arg.iteritems():
121 if type(value) is dict:
122 value = tuple(value.items())
123 k += (key, value)
124 return k
125
126 def __hash__(self):
127 return hash(self.key)
128
129 def __eq__(self, other):
130 return self.key == other.key
131
132 def __repr__(self):
133 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000134
135
136class Analysis(Remark):
137 yaml_tag = '!Analysis'
138
139 @property
140 def color(self):
141 return "white"
142
143
144class AnalysisFPCommute(Analysis):
145 yaml_tag = '!AnalysisFPCommute'
146
147
148class AnalysisAliasing(Analysis):
149 yaml_tag = '!AnalysisAliasing'
150
151
152class Passed(Remark):
153 yaml_tag = '!Passed'
154
155 @property
156 def color(self):
157 return "green"
158
159
160class Missed(Remark):
161 yaml_tag = '!Missed'
162
163 @property
164 def color(self):
165 return "red"
166
167
168def get_remarks(input_file):
169 max_hotness = 0
170 all_remarks = dict()
171 file_remarks = defaultdict(functools.partial(defaultdict, list))
172
173 with open(input_file) as f:
174 docs = yaml.load_all(f, Loader=Loader)
175 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000176 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000177 # Avoid remarks withoug debug location or if they are duplicated
178 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
179 continue
180 all_remarks[remark.key] = remark
181
182 file_remarks[remark.File][remark.Line].append(remark)
183
Adam Nemet6ab2d482017-03-02 17:00:59 +0000184 # If we're reading a back a diff yaml file, max_hotness is already
185 # captured which may actually be less than the max hotness found
186 # in the file.
187 if hasattr(remark, 'max_hotness'):
188 max_hotness = remark.max_hotness
Adam Nemetb7278af2017-03-01 21:35:00 +0000189 max_hotness = max(max_hotness, remark.Hotness)
190
191 return max_hotness, all_remarks, file_remarks
192
193
194def gather_results(pmap, filenames):
195 remarks = pmap(get_remarks, filenames)
196 max_hotness = max(entry[0] for entry in remarks)
197
198 def merge_file_remarks(file_remarks_job, all_remarks, merged):
199 for filename, d in file_remarks_job.iteritems():
200 for line, remarks in d.iteritems():
201 for remark in remarks:
202 # Bring max_hotness into the remarks so that
203 # RelativeHotness does not depend on an external global.
204 remark.max_hotness = max_hotness
205 if remark.key not in all_remarks:
206 merged[filename][line].append(remark)
207
208 all_remarks = dict()
209 file_remarks = defaultdict(functools.partial(defaultdict, list))
210 for _, all_remarks_job, file_remarks_job in remarks:
211 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
212 all_remarks.update(all_remarks_job)
213
214 return all_remarks, file_remarks, max_hotness != 0