7
7
8
8
The tools requires PyYAML and Pygments Python packages.'''
9
9
10
- import yaml
11
- # Try to use the C parser.
12
- try :
13
- from yaml import CLoader as Loader
14
- except ImportError :
15
- print ("For faster parsing, you may want to install libYAML for PyYAML" )
16
- from yaml import Loader
17
-
10
+ import optrecord
18
11
import functools
19
- from collections import defaultdict
20
- import itertools
21
12
from multiprocessing import Pool
22
13
from multiprocessing import Lock , cpu_count
23
14
import errno
24
15
import argparse
25
16
import os .path
26
17
import re
27
- import subprocess
28
18
import shutil
29
19
from pygments import highlight
30
20
from pygments .lexers .c_cpp import CppLexer
31
21
from pygments .formatters import HtmlFormatter
32
22
import cgi
33
23
34
- p = subprocess .Popen (['c++filt' , '-n' ], stdin = subprocess .PIPE , stdout = subprocess .PIPE )
35
- p_lock = Lock ()
36
-
37
-
38
- def demangle (name ):
39
- with p_lock :
40
- p .stdin .write (name + '\n ' )
41
- return p .stdout .readline ().rstrip ()
42
-
43
24
# This allows passing the global context to the child processes.
44
25
class Context :
45
- def __init__ (self , max_hotness = 0 , caller_loc = dict ()):
46
- self .max_hotness = max_hotness
47
-
26
+ def __init__ (self , caller_loc = dict ()):
48
27
# Map function names to their source location for function where inlining happened
49
28
self .caller_loc = caller_loc
50
29
51
- def should_display_hotness (self ):
52
- # If max_hotness is 0 at the end, we assume hotness information is
53
- # missing and no relative hotness information is displayed
54
- return self .max_hotness != 0
55
-
56
30
context = Context ()
57
31
58
- class Remark (yaml .YAMLObject ):
59
- # Work-around for http://pyyaml.org/ticket/154.
60
- yaml_loader = Loader
61
-
62
- def __getattr__ (self , name ):
63
- # If hotness is missing, assume 0
64
- if name == 'Hotness' :
65
- return 0
66
- raise AttributeError
67
-
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
- @classmethod
89
- def make_link (cls , File , Line ):
90
- return "{}#L{}" .format (SourceFileRenderer .html_file_name (File ), Line )
91
-
92
- @property
93
- def Link (self ):
94
- return Remark .make_link (self .File , self .Line )
95
-
96
- def getArgString (self , mapping ):
97
- mapping = mapping .copy ()
98
- dl = mapping .get ('DebugLoc' )
99
- if dl :
100
- del mapping ['DebugLoc' ]
101
-
102
- assert (len (mapping ) == 1 )
103
- (key , value ) = mapping .items ()[0 ]
104
-
105
- if key == 'Caller' or key == 'Callee' :
106
- value = cgi .escape (demangle (value ))
107
-
108
- if dl and key != 'Caller' :
109
- return "<a href={}>{}</a>" .format (
110
- Remark .make_link (dl ['File' ], dl ['Line' ]), value )
111
- else :
112
- return value
113
-
114
- @property
115
- def message (self ):
116
- # Args is a list of mappings (dictionaries)
117
- values = [self .getArgString (mapping ) for mapping in self .Args ]
118
- return "" .join (values )
119
-
120
- @property
121
- def RelativeHotness (self ):
122
- if context .should_display_hotness ():
123
- return "{}%" .format (int (round (self .Hotness * 100 / context .max_hotness )))
124
- else :
125
- return ''
126
-
127
- @property
128
- def key (self ):
129
- return (self .__class__ , self .Pass , self .Name , self .File , self .Line , self .Column , self .Function )
130
-
131
-
132
- class Analysis (Remark ):
133
- yaml_tag = '!Analysis'
134
-
135
- @property
136
- def color (self ):
137
- return "white"
138
-
139
-
140
- class AnalysisFPCommute (Analysis ):
141
- yaml_tag = '!AnalysisFPCommute'
142
-
143
-
144
- class AnalysisAliasing (Analysis ):
145
- yaml_tag = '!AnalysisAliasing'
146
-
147
-
148
- class Passed (Remark ):
149
- yaml_tag = '!Passed'
150
-
151
- @property
152
- def color (self ):
153
- return "green"
154
-
155
-
156
- class Missed (Remark ):
157
- yaml_tag = '!Missed'
158
-
159
- @property
160
- def color (self ):
161
- return "red"
162
-
163
-
164
32
class SourceFileRenderer :
165
33
def __init__ (self , source_dir , output_dir , filename ):
166
34
existing_filename = None
@@ -171,7 +39,7 @@ def __init__(self, source_dir, output_dir, filename):
171
39
if os .path .exists (fn ):
172
40
existing_filename = fn
173
41
174
- self .stream = open (os .path .join (output_dir , SourceFileRenderer .html_file_name (filename )), 'w' )
42
+ self .stream = open (os .path .join (output_dir , optrecord .html_file_name (filename )), 'w' )
175
43
if existing_filename :
176
44
self .source_stream = open (existing_filename )
177
45
else :
@@ -208,10 +76,9 @@ def render_source_lines(self, stream, line_remarks):
208
76
209
77
def render_inline_remarks (self , r , line ):
210
78
inlining_context = r .DemangledFunctionName
211
- print
212
79
dl = context .caller_loc .get (r .Function )
213
80
if dl :
214
- link = Remark .make_link (dl ['File' ], dl ['Line' ] - 2 )
81
+ link = optrecord .make_link (dl ['File' ], dl ['Line' ] - 2 )
215
82
inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>" .format (** locals ())
216
83
217
84
# Column is the number of characters *including* tabs, keep those and
@@ -254,10 +121,6 @@ def render(self, line_remarks):
254
121
</body>
255
122
</html>''' , file = self .stream )
256
123
257
- @classmethod
258
- def html_file_name (cls , filename ):
259
- return filename .replace ('/' , '_' ) + ".html"
260
-
261
124
262
125
class IndexRenderer :
263
126
def __init__ (self , output_dir ):
@@ -296,67 +159,25 @@ def render(self, all_remarks):
296
159
</html>''' , file = self .stream )
297
160
298
161
299
- def get_remarks (input_file ):
300
- max_hotness = 0
301
- all_remarks = dict ()
302
- file_remarks = defaultdict (functools .partial (defaultdict , list ))
303
-
304
- with open (input_file ) as f :
305
- docs = yaml .load_all (f , Loader = Loader )
306
-
307
- for remark in docs :
308
- # Avoid remarks withoug debug location or if they are duplicated
309
- if not hasattr (remark , 'DebugLoc' ) or remark .key in all_remarks :
310
- continue
311
- all_remarks [remark .key ] = remark
312
-
313
- file_remarks [remark .File ][remark .Line ].append (remark )
314
-
315
- max_hotness = max (max_hotness , remark .Hotness )
316
-
317
- return max_hotness , all_remarks , file_remarks
318
-
319
-
320
162
def _render_file (source_dir , output_dir , ctx , entry ):
321
163
global context
322
164
context = ctx
323
165
filename , remarks = entry
324
166
SourceFileRenderer (source_dir , output_dir , filename ).render (remarks )
325
167
326
168
327
- def gather_results (pmap , filenames ):
328
- remarks = pmap (get_remarks , filenames )
329
-
330
- def merge_file_remarks (file_remarks_job , all_remarks , merged ):
331
- for filename , d in file_remarks_job .iteritems ():
332
- for line , remarks in d .iteritems ():
333
- for remark in remarks :
334
- if remark .key not in all_remarks :
335
- merged [filename ][line ].append (remark )
336
-
337
- all_remarks = dict ()
338
- file_remarks = defaultdict (functools .partial (defaultdict , list ))
339
- for _ , all_remarks_job , file_remarks_job in remarks :
340
- merge_file_remarks (file_remarks_job , all_remarks , file_remarks )
341
- all_remarks .update (all_remarks_job )
342
-
343
- context .max_hotness = max (entry [0 ] for entry in remarks )
344
-
345
- return all_remarks , file_remarks
346
-
347
-
348
169
def map_remarks (all_remarks ):
349
170
# Set up a map between function names and their source location for
350
171
# function where inlining happened
351
172
for remark in all_remarks .itervalues ():
352
- if isinstance (remark , Passed ) and remark .Pass == "inline" and remark .Name == "Inlined" :
173
+ if isinstance (remark , optrecord . Passed ) and remark .Pass == "inline" and remark .Name == "Inlined" :
353
174
for arg in remark .Args :
354
175
caller = arg .get ('Caller' )
355
176
if caller :
356
177
context .caller_loc [caller ] = arg ['DebugLoc' ]
357
178
358
179
359
- def generate_report (pmap , all_remarks , file_remarks , source_dir , output_dir ):
180
+ def generate_report (pmap , all_remarks , file_remarks , source_dir , output_dir , should_display_hotness ):
360
181
try :
361
182
os .makedirs (output_dir )
362
183
except OSError as e :
@@ -368,7 +189,7 @@ def generate_report(pmap, all_remarks, file_remarks, source_dir, output_dir):
368
189
_render_file_bound = functools .partial (_render_file , source_dir , output_dir , context )
369
190
pmap (_render_file_bound , file_remarks .items ())
370
191
371
- if context . should_display_hotness () :
192
+ if should_display_hotness :
372
193
sorted_remarks = sorted (all_remarks .itervalues (), key = lambda r : (r .Hotness , r .__dict__ ), reverse = True )
373
194
else :
374
195
sorted_remarks = sorted (all_remarks .itervalues (), key = lambda r : (r .File , r .Line , r .Column , r .__dict__ ))
@@ -405,8 +226,8 @@ def generate_report(pmap, all_remarks, file_remarks, source_dir, output_dir):
405
226
pool = Pool (processes = args .jobs )
406
227
pmap = pool .map
407
228
408
- all_remarks , file_remarks = gather_results (pmap , args .yaml_files )
229
+ all_remarks , file_remarks , should_display_hotness = optrecord . gather_results (pmap , args .yaml_files )
409
230
410
231
map_remarks (all_remarks )
411
232
412
- generate_report (pmap , all_remarks , file_remarks , args .source_dir , args .output_dir )
233
+ generate_report (pmap , all_remarks , file_remarks , args .source_dir , args .output_dir , should_display_hotness )
0 commit comments