blob: 8b33c0abfe1b9778cc34cb41da2e22fe64e1ee56 [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):
36 return "{}#L{}".format(html_file_name(File), Line)
37
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
91 @property
92 def message(self):
93 # Args is a list of mappings (dictionaries)
94 values = [self.getArgString(mapping) for mapping in self.Args]
95 return "".join(values)
96
97 @property
98 def RelativeHotness(self):
99 if self.max_hotness:
100 return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
101 else:
102 return ''
103
104 @property
105 def key(self):
Adam Nemet7370dad2017-03-02 17:00:56 +0000106 k = (self.__class__, self.Pass, self.Name, self.File, self.Line, self.Column, self.Function)
107 for arg in self.Args:
108 for (key, value) in arg.iteritems():
109 if type(value) is dict:
110 value = tuple(value.items())
111 k += (key, value)
112 return k
113
114 def __hash__(self):
115 return hash(self.key)
116
117 def __eq__(self, other):
118 return self.key == other.key
119
120 def __repr__(self):
121 return str(self.key)
Adam Nemetb7278af2017-03-01 21:35:00 +0000122
123
124class Analysis(Remark):
125 yaml_tag = '!Analysis'
126
127 @property
128 def color(self):
129 return "white"
130
131
132class AnalysisFPCommute(Analysis):
133 yaml_tag = '!AnalysisFPCommute'
134
135
136class AnalysisAliasing(Analysis):
137 yaml_tag = '!AnalysisAliasing'
138
139
140class Passed(Remark):
141 yaml_tag = '!Passed'
142
143 @property
144 def color(self):
145 return "green"
146
147
148class Missed(Remark):
149 yaml_tag = '!Missed'
150
151 @property
152 def color(self):
153 return "red"
154
155
156def get_remarks(input_file):
157 max_hotness = 0
158 all_remarks = dict()
159 file_remarks = defaultdict(functools.partial(defaultdict, list))
160
161 with open(input_file) as f:
162 docs = yaml.load_all(f, Loader=Loader)
163 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000164 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000165 # Avoid remarks withoug debug location or if they are duplicated
166 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
167 continue
168 all_remarks[remark.key] = remark
169
170 file_remarks[remark.File][remark.Line].append(remark)
171
172 max_hotness = max(max_hotness, remark.Hotness)
173
174 return max_hotness, all_remarks, file_remarks
175
176
177def gather_results(pmap, filenames):
178 remarks = pmap(get_remarks, filenames)
179 max_hotness = max(entry[0] for entry in remarks)
180
181 def merge_file_remarks(file_remarks_job, all_remarks, merged):
182 for filename, d in file_remarks_job.iteritems():
183 for line, remarks in d.iteritems():
184 for remark in remarks:
185 # Bring max_hotness into the remarks so that
186 # RelativeHotness does not depend on an external global.
187 remark.max_hotness = max_hotness
188 if remark.key not in all_remarks:
189 merged[filename][line].append(remark)
190
191 all_remarks = dict()
192 file_remarks = defaultdict(functools.partial(defaultdict, list))
193 for _, all_remarks_job, file_remarks_job in remarks:
194 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
195 all_remarks.update(all_remarks_job)
196
197 return all_remarks, file_remarks, max_hotness != 0