3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Thomas Nagy, 2007-2010 (ita)
10 Debugging helper for parallel compilation, outputs
11 a file named pdebug.svg in the source directory::
14 opt.load('parallel_debug')
19 import re, sys, threading, time, traceback
21 from Queue import Queue
23 from queue import Queue
24 from waflib import Runner, Options, Task, Logs, Errors
26 SVG_TEMPLATE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
27 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
28 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0"
29 x="${project.x}" y="${project.y}" width="${project.width}" height="${project.height}" id="svg602" xml:space="preserve">
31 <style type='text/css' media='screen'>
32 g.over rect { stroke:#FF0000; fill-opacity:0.4 }
35 <script type='text/javascript'><![CDATA[
36 var svg = document.getElementsByTagName('svg')[0];
38 svg.addEventListener('mouseover', function(e) {
39 var g = e.target.parentNode;
40 var x = document.getElementById('r_' + g.id);
42 g.setAttribute('class', g.getAttribute('class') + ' over');
43 x.setAttribute('class', x.getAttribute('class') + ' over');
44 showInfo(e, g.id, e.target.attributes.tooltip.value);
48 svg.addEventListener('mouseout', function(e) {
49 var g = e.target.parentNode;
50 var x = document.getElementById('r_' + g.id);
52 g.setAttribute('class', g.getAttribute('class').replace(' over', ''));
53 x.setAttribute('class', x.getAttribute('class').replace(' over', ''));
58 function showInfo(evt, txt, details) {
60 tooltip = document.getElementById('tooltip');
62 var t = document.getElementById('tooltiptext');
63 t.firstChild.data = txt + " " + details;
65 var x = evt.clientX + 9;
66 if (x > 250) { x -= t.getComputedTextLength() + 16; }
67 var y = evt.clientY + 20;
68 tooltip.setAttribute("transform", "translate(" + x + "," + y + ")");
69 tooltip.setAttributeNS(null, "visibility", "visible");
71 var r = document.getElementById('tooltiprect');
72 r.setAttribute('width', t.getComputedTextLength() + 6);
76 function hideInfo(evt) {
77 var tooltip = document.getElementById('tooltip');
78 tooltip.setAttributeNS(null,"visibility","hidden");
82 <!-- inkscape requires a big rectangle or it will not export the pictures properly -->
84 x='${project.x}' y='${project.y}' width='${project.width}' height='${project.height}'
85 style="font-size:10;fill:#ffffff;fill-opacity:0.01;fill-rule:evenodd;stroke:#ffffff;"></rect>
88 <text x="${project.title_x}" y="${project.title_y}"
89 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">${project.title}</text>
93 ${for cls in project.groups}
94 <g id='${cls.classname}'>
95 ${for rect in cls.rects}
96 <rect x='${rect.x}' y='${rect.y}' width='${rect.width}' height='${rect.height}' tooltip='${rect.name}' style="font-size:10;fill:${rect.color};fill-rule:evenodd;stroke:#000000;stroke-width:0.4;" />
101 ${for info in project.infos}
102 <g id='r_${info.classname}'>
103 <rect x='${info.x}' y='${info.y}' width='${info.width}' height='${info.height}' style="font-size:10;fill:${info.color};fill-rule:evenodd;stroke:#000000;stroke-width:0.4;" />
104 <text x="${info.text_x}" y="${info.text_y}"
105 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"
110 ${if project.tooltip}
111 <g transform="translate(0,0)" visibility="hidden" id="tooltip">
112 <rect id="tooltiprect" y="-15" x="-3" width="1" height="20" style="stroke:black;fill:#edefc2;stroke-width:1"/>
113 <text id="tooltiptext" style="font-family:Arial; font-size:12;fill:black;"> </text>
120 COMPILE_TEMPLATE = '''def f(project):
122 def xml_escape(value):
123 return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">")
128 reg_act = re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<code>[^}]*?)\})", re.M)
129 def compile_template(line):
139 extr.append(g('code'))
143 line2 = reg_act.sub(repl, line)
144 params = line2.split('<<|@|>>')
153 buf.append(indent * '\t' + txt)
155 for x in range(len(extr)):
157 app("lst.append(%r)" % params[x])
160 if f.startswith(('if', 'for')):
163 elif f.startswith('py:'):
165 elif f.startswith(('endif', 'endfor')):
167 elif f.startswith(('else', 'elif')):
171 elif f.startswith('xml:'):
172 app('lst.append(xml_escape(%s))' % f[4:])
174 #app('lst.append((%s) or "cannot find %s")' % (f, f))
175 app('lst.append(str(%s))' % f)
179 app("lst.append(%r)" % params[-1])
181 fun = COMPILE_TEMPLATE % "\n\t".join(buf)
182 # uncomment the following to debug the template
183 #for i, x in enumerate(fun.splitlines()):
185 return Task.funex(fun)
193 'YELLOW' : '#fefe44',
201 info = [] # list of (text,color)
203 def map_to_color(name):
207 cls = Task.classes[name]
209 return color2code['RED']
212 if cls.color in color2code:
213 return color2code[cls.color]
214 return color2code['RED']
217 m = self.generator.bld.producer
219 # TODO another place for this?
220 del self.generator.bld.task_sigs[self.uid()]
224 self.generator.bld.producer.set_running(1, self)
229 self.err_msg = traceback.format_exc()
230 self.hasrun = Task.EXCEPTION
233 m.error_handler(self)
238 self.hasrun = Task.CRASHED
242 except Errors.WafError:
245 self.err_msg = traceback.format_exc()
246 self.hasrun = Task.EXCEPTION
248 self.hasrun = Task.SUCCESS
249 if self.hasrun != Task.SUCCESS:
250 m.error_handler(self)
252 self.generator.bld.producer.set_running(-1, self)
254 Task.Task.process_back = Task.Task.process
255 Task.Task.process = process
257 old_start = Runner.Parallel.start
260 Options.options.dband
261 except AttributeError:
262 self.bld.fatal('use def options(opt): opt.load("parallel_debug")!')
264 self.taskinfo = Queue()
268 Runner.Parallel.start = do_start
270 lock_running = threading.Lock()
271 def set_running(self, by, tsk):
274 cache = self.lock_cache
275 except AttributeError:
276 cache = self.lock_cache = {}
280 vals = cache.values()
281 for i in range(self.numjobs):
289 self.taskinfo.put( (i, id(tsk), time.time(), tsk.__class__.__name__, self.processed, self.count, by, ",".join(map(str, tsk.outputs))) )
290 Runner.Parallel.set_running = set_running
292 def name2class(name):
293 return name.replace(' ', '_').replace('.', '_')
295 def make_picture(producer):
296 # first, cast the parameters
297 if not hasattr(producer.bld, 'path'):
303 tup = producer.taskinfo.get(False)
304 tmp.append(list(tup))
309 ini = float(tmp[0][2])
322 info.append((name, map_to_color(name)))
323 info.sort(key=lambda x: x[0])
329 acc.append("%d %d %f %r %d %d %d %s" % (x[0], x[1], x[2] - ini, x[3], x[4], x[5], thread_count, x[7]))
331 data_node = producer.bld.path.make_node('pdebug.dat')
332 data_node.write('\n'.join(acc))
334 tmp = [lst[:2] + [float(lst[2]) - ini] + lst[3:] for lst in tmp]
339 st[l[0]] = len(st.keys())
340 tmp = [ [st[lst[0]]] + lst[1:] for lst in tmp ]
341 THREAD_AMOUNT = len(st.keys())
346 st[l[1]] = len(st.keys())
347 tmp = [ [lst[0]] + [st[lst[1]]] + lst[2:] for lst in tmp ]
350 BAND = Options.options.dband
354 for x in range(len(tmp)):
364 for y in range(x + 1, len(tmp)):
368 #print id, thread_id, begin, end
369 #acc.append( ( 10*thread_id, 10*(thread_id+1), 10*begin, 10*end ) )
370 acc.append( (BAND * begin, BAND*thread_id, BAND*end - BAND*begin, BAND, line[3], line[7]) )
373 if Options.options.dmaxtime < 0.1:
380 gwidth = BAND * Options.options.dmaxtime
382 ratio = float(Options.options.dwidth) / gwidth
383 gwidth = Options.options.dwidth
384 gheight = BAND * (THREAD_AMOUNT + len(info) + 1.5)
387 # simple data model for our template
388 class tobject(object):
394 model.width = gwidth + 4
395 model.height = gheight + 4
397 model.tooltip = not Options.options.dnotooltip
399 model.title = Options.options.dtitle
400 model.title_x = gwidth / 2
401 model.title_y = gheight + - 5
404 for (x, y, w, h, clsname, name) in acc:
406 groups[clsname].append((x, y, w, h, name))
408 groups[clsname] = [(x, y, w, h, name)]
410 # groups of rectangles (else js highlighting is slow)
414 model.groups.append(g)
415 g.classname = name2class(cls)
417 for (x, y, w, h, name) in groups[cls]:
425 r.color = map_to_color(cls)
431 for (text, color) in info:
433 model.infos.append(inf)
434 inf.classname = name2class(text)
436 inf.y = 5 + (cnt + 0.5) * BAND
442 inf.text_x = 2 + 2 * BAND
443 inf.text_y = 5 + (cnt + 0.5) * BAND + 10
448 template1 = compile_template(SVG_TEMPLATE)
449 txt = template1(model)
451 node = producer.bld.path.make_node('pdebug.svg')
453 Logs.warn('Created the diagram %r', node)
456 opt.add_option('--dtitle', action='store', default='Parallel build representation for %r' % ' '.join(sys.argv),
457 help='title for the svg diagram', dest='dtitle')
458 opt.add_option('--dwidth', action='store', type='int', help='diagram width', default=800, dest='dwidth')
459 opt.add_option('--dtime', action='store', type='float', help='recording interval in seconds', default=0.009, dest='dtime')
460 opt.add_option('--dband', action='store', type='int', help='band width', default=22, dest='dband')
461 opt.add_option('--dmaxtime', action='store', type='float', help='maximum time, for drawing fair comparisons', default=0, dest='dmaxtime')
462 opt.add_option('--dnotooltip', action='store_true', help='disable tooltips', default=False, dest='dnotooltip')