blob: 4a5733dae7dd1f2700427db59037ba91d3b36fa5 [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):
106 return (self.__class__, self.Pass, self.Name, self.File, self.Line, self.Column, self.Function)
107
108
109class Analysis(Remark):
110 yaml_tag = '!Analysis'
111
112 @property
113 def color(self):
114 return "white"
115
116
117class AnalysisFPCommute(Analysis):
118 yaml_tag = '!AnalysisFPCommute'
119
120
121class AnalysisAliasing(Analysis):
122 yaml_tag = '!AnalysisAliasing'
123
124
125class Passed(Remark):
126 yaml_tag = '!Passed'
127
128 @property
129 def color(self):
130 return "green"
131
132
133class Missed(Remark):
134 yaml_tag = '!Missed'
135
136 @property
137 def color(self):
138 return "red"
139
140
141def get_remarks(input_file):
142 max_hotness = 0
143 all_remarks = dict()
144 file_remarks = defaultdict(functools.partial(defaultdict, list))
145
146 with open(input_file) as f:
147 docs = yaml.load_all(f, Loader=Loader)
148 for remark in docs:
Adam Nemeta8b692a2017-03-02 17:00:53 +0000149 remark.initmissing()
Adam Nemetb7278af2017-03-01 21:35:00 +0000150 # Avoid remarks withoug debug location or if they are duplicated
151 if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
152 continue
153 all_remarks[remark.key] = remark
154
155 file_remarks[remark.File][remark.Line].append(remark)
156
157 max_hotness = max(max_hotness, remark.Hotness)
158
159 return max_hotness, all_remarks, file_remarks
160
161
162def gather_results(pmap, filenames):
163 remarks = pmap(get_remarks, filenames)
164 max_hotness = max(entry[0] for entry in remarks)
165
166 def merge_file_remarks(file_remarks_job, all_remarks, merged):
167 for filename, d in file_remarks_job.iteritems():
168 for line, remarks in d.iteritems():
169 for remark in remarks:
170 # Bring max_hotness into the remarks so that
171 # RelativeHotness does not depend on an external global.
172 remark.max_hotness = max_hotness
173 if remark.key not in all_remarks:
174 merged[filename][line].append(remark)
175
176 all_remarks = dict()
177 file_remarks = defaultdict(functools.partial(defaultdict, list))
178 for _, all_remarks_job, file_remarks_job in remarks:
179 merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
180 all_remarks.update(all_remarks_job)
181
182 return all_remarks, file_remarks, max_hotness != 0