Include waf as an extracted source directory, rather than as a one-in-a-file script.
[samba.git] / buildtools / wafadmin / 3rdparty / ParallelDebug.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2007-2010 (ita)
4
5 """
6 debugging helpers for parallel compilation, outputs
7 a svg file in the build directory
8 """
9
10 import os, time, sys, threading
11 try: from Queue import Queue
12 except: from queue import Queue
13 import Runner, Options, Utils, Task, Logs
14 from Constants import *
15
16 #import random
17 #random.seed(100)
18
19 def set_options(opt):
20         opt.add_option('--dtitle', action='store', default='Parallel build representation for %r' % ' '.join(sys.argv),
21                 help='title for the svg diagram', dest='dtitle')
22         opt.add_option('--dwidth', action='store', type='int', help='diagram width', default=1000, dest='dwidth')
23         opt.add_option('--dtime', action='store', type='float', help='recording interval in seconds', default=0.009, dest='dtime')
24         opt.add_option('--dband', action='store', type='int', help='band width', default=22, dest='dband')
25         opt.add_option('--dmaxtime', action='store', type='float', help='maximum time, for drawing fair comparisons', default=0, dest='dmaxtime')
26
27 # red   #ff4d4d
28 # green #4da74d
29 # lila  #a751ff
30
31 color2code = {
32         'GREEN'  : '#4da74d',
33         'YELLOW' : '#fefe44',
34         'PINK'   : '#a751ff',
35         'RED'    : '#cc1d1d',
36         'BLUE'   : '#6687bb',
37         'CYAN'   : '#34e2e2',
38
39 }
40
41 mp = {}
42 info = [] # list of (text,color)
43
44 def map_to_color(name):
45         if name in mp:
46                 return mp[name]
47         try:
48                 cls = Task.TaskBase.classes[name]
49         except KeyError:
50                 return color2code['RED']
51         if cls.color in mp:
52                 return mp[cls.color]
53         if cls.color in color2code:
54                 return color2code[cls.color]
55         return color2code['RED']
56
57 def loop(self):
58         while 1:
59                 tsk=Runner.TaskConsumer.ready.get()
60                 tsk.master.set_running(1, id(threading.currentThread()), tsk)
61                 Runner.process_task(tsk)
62                 tsk.master.set_running(-1, id(threading.currentThread()), tsk)
63 Runner.TaskConsumer.loop = loop
64
65
66 old_start = Runner.Parallel.start
67 def do_start(self):
68         print Options.options
69         try:
70                 Options.options.dband
71         except AttributeError:
72                 raise ValueError('use def options(opt): opt.load("parallel_debug")!')
73
74         self.taskinfo = Queue()
75         old_start(self)
76         process_colors(self)
77 Runner.Parallel.start = do_start
78
79 def set_running(self, by, i, tsk):
80         self.taskinfo.put( (i, id(tsk), time.time(), tsk.__class__.__name__, self.processed, self.count, by)  )
81 Runner.Parallel.set_running = set_running
82
83 def name2class(name):
84         return name.replace(' ', '_').replace('.', '_')
85
86 def process_colors(producer):
87         # first, cast the parameters
88         tmp = []
89         try:
90                 while True:
91                         tup = producer.taskinfo.get(False)
92                         tmp.append(list(tup))
93         except:
94                 pass
95
96         try:
97                 ini = float(tmp[0][2])
98         except:
99                 return
100
101         if not info:
102                 seen = []
103                 for x in tmp:
104                         name = x[3]
105                         if not name in seen:
106                                 seen.append(name)
107                         else:
108                                 continue
109
110                         info.append((name, map_to_color(name)))
111                 info.sort(key=lambda x: x[0])
112
113         thread_count = 0
114         acc = []
115         for x in tmp:
116                 thread_count += x[6]
117                 acc.append("%d %d %f %r %d %d %d" % (x[0], x[1], x[2] - ini, x[3], x[4], x[5], thread_count))
118         f = open('pdebug.dat', 'w')
119         #Utils.write('\n'.join(acc))
120         f.write('\n'.join(acc))
121
122         tmp = [lst[:2] + [float(lst[2]) - ini] + lst[3:] for lst in tmp]
123
124         st = {}
125         for l in tmp:
126                 if not l[0] in st:
127                         st[l[0]] = len(st.keys())
128         tmp = [  [st[lst[0]]] + lst[1:] for lst in tmp ]
129         THREAD_AMOUNT = len(st.keys())
130
131         st = {}
132         for l in tmp:
133                 if not l[1] in st:
134                         st[l[1]] = len(st.keys())
135         tmp = [  [lst[0]] + [st[lst[1]]] + lst[2:] for lst in tmp ]
136
137
138         BAND = Options.options.dband
139
140         seen = {}
141         acc = []
142         for x in range(len(tmp)):
143                 line = tmp[x]
144                 id = line[1]
145
146                 if id in seen:
147                         continue
148                 seen[id] = True
149
150                 begin = line[2]
151                 thread_id = line[0]
152                 for y in range(x + 1, len(tmp)):
153                         line = tmp[y]
154                         if line[1] == id:
155                                 end = line[2]
156                                 #print id, thread_id, begin, end
157                                 #acc.append(  ( 10*thread_id, 10*(thread_id+1), 10*begin, 10*end ) )
158                                 acc.append( (BAND * begin, BAND*thread_id, BAND*end - BAND*begin, BAND, line[3]) )
159                                 break
160
161         if Options.options.dmaxtime < 0.1:
162                 gwidth = 1
163                 for x in tmp:
164                         m = BAND * x[2]
165                         if m > gwidth:
166                                 gwidth = m
167         else:
168                 gwidth = BAND * Options.options.dmaxtime
169
170         ratio = float(Options.options.dwidth) / gwidth
171         gwidth = Options.options.dwidth
172
173         gheight = BAND * (THREAD_AMOUNT + len(info) + 1.5)
174
175         out = []
176
177         out.append("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
178 <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"
179 \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">
180 <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.0\"
181    x=\"%r\" y=\"%r\" width=\"%r\" height=\"%r\"
182    id=\"svg602\" xml:space=\"preserve\">
183
184 <style type='text/css' media='screen'>
185     g.over rect  { stroke:#FF0000; fill-opacity:0.4 }
186 </style>
187
188 <script type='text/javascript'><![CDATA[
189     var svg  = document.getElementsByTagName('svg')[0];
190     var svgNS = svg.getAttribute('xmlns');
191     svg.addEventListener('mouseover',function(e){
192       var g = e.target.parentNode;
193       var x = document.getElementById('r_'+g.id);
194       if (x) {
195          g.setAttribute('class', g.getAttribute('class')+' over');
196          x.setAttribute('class', x.getAttribute('class')+' over');
197          showInfo(e, g.id);
198       }
199     },false);
200     svg.addEventListener('mouseout',function(e){
201       var g = e.target.parentNode;
202       var x = document.getElementById('r_'+g.id);
203       if (x) {
204          g.setAttribute('class',g.getAttribute('class').replace(' over',''));
205          x.setAttribute('class',x.getAttribute('class').replace(' over',''));
206          hideInfo(e);
207       }
208     },false);
209
210 function showInfo(evt, txt) {
211     tooltip = document.getElementById('tooltip');
212
213     var t = document.getElementById('tooltiptext');
214     t.firstChild.data = txt;
215
216     var x = evt.clientX+10;
217     if (x > 200) { x -= t.getComputedTextLength() + 16; }
218     var y = evt.clientY+30;
219     tooltip.setAttribute("transform", "translate(" + x + "," + y + ")");
220     tooltip.setAttributeNS(null,"visibility","visible");
221
222     var r = document.getElementById('tooltiprect');
223     r.setAttribute('width', t.getComputedTextLength()+6)
224 }
225
226
227 function hideInfo(evt) {
228     tooltip = document.getElementById('tooltip');
229     tooltip.setAttributeNS(null,"visibility","hidden");
230 }
231
232 ]]></script>
233
234 <!-- inkscape requires a big rectangle or it will not export the pictures properly -->
235 <rect
236    x='%r' y='%r'
237    width='%r' height='%r' z-index='10'
238    style=\"font-size:10;fill:#ffffff;fill-opacity:0.01;fill-rule:evenodd;stroke:#ffffff;\"
239    />\n
240
241 """ % (0, 0, gwidth + 4, gheight + 4,   0, 0, gwidth + 4, gheight + 4))
242
243         # main title
244         if Options.options.dtitle:
245                 out.append("""<text x="%d" y="%d" style="font-size:15px; text-anchor:middle; font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans">%s</text>
246 """ % (gwidth/2, gheight - 5, Options.options.dtitle))
247
248         # the rectangles
249         groups = {}
250         for (x, y, w, h, clsname) in acc:
251                 try:
252                         groups[clsname].append((x, y, w, h))
253                 except:
254                         groups[clsname] = [(x, y, w, h)]
255
256         for cls in groups:
257
258                 out.append("<g id='%s'>\n" % name2class(cls))
259
260                 for (x, y, w, h) in groups[cls]:
261                         out.append("""   <rect
262    x='%r' y='%r'
263    width='%r' height='%r' z-index='11'
264    style=\"font-size:10;fill:%s;fill-rule:evenodd;stroke:#000000;stroke-width:0.2px;\"
265    />\n""" % (2 + x*ratio, 2 + y, w*ratio, h, map_to_color(cls)))
266
267                 out.append("</g>\n")
268
269         # output the caption
270         cnt = THREAD_AMOUNT
271
272         for (text, color) in info:
273                 # caption box
274                 b = BAND/2
275                 out.append("""<g id='r_%s'><rect
276                 x='%r' y='%r'
277                 width='%r' height='%r'
278                 style=\"font-size:10;fill:%s;fill-rule:evenodd;stroke:#000000;stroke-width:0.2px;\"
279   />\n""" %                       (name2class(text), 2 + BAND,     5 + (cnt + 0.5) * BAND, b, b, color))
280
281                 # caption text
282                 out.append("""<text
283    style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
284    x="%r" y="%d">%s</text></g>\n""" % (2 + 2 * BAND, 5 + (cnt + 0.5) * BAND + 10, text))
285                 cnt += 1
286
287         out.append("""
288 <g transform="translate(0,0)" visibility="hidden" id="tooltip">
289   <rect id="tooltiprect" y="-15" x="-3" width="1" height="20" style="stroke:black;fill:#edefc2;stroke-width:1"/>
290   <text id="tooltiptext" style="font-family:Arial; font-size:12;fill:black;"> </text>
291 </g>""")
292
293         out.append("\n</svg>")
294
295         #node = producer.bld.path.make_node('pdebug.svg')
296         f = open('pdebug.svg', 'w')
297         f.write("".join(out))
298
299