1*6f9cba8fSJoseph Mingrone#!/usr/bin/env python 2*6f9cba8fSJoseph Mingrone 3*6f9cba8fSJoseph Mingrone""" 4*6f9cba8fSJoseph MingroneThis program parses the output from pcap_compile() to visualize the CFG after 5*6f9cba8fSJoseph Mingroneeach optimize phase. 6*6f9cba8fSJoseph Mingrone 7*6f9cba8fSJoseph MingroneUsage guide: 8*6f9cba8fSJoseph Mingrone1. Enable optimizer debugging code when configure libpcap, 9*6f9cba8fSJoseph Mingrone and build libpcap & the test programs 10*6f9cba8fSJoseph Mingrone ./configure --enable-optimizer-dbg 11*6f9cba8fSJoseph Mingrone make 12*6f9cba8fSJoseph Mingrone make testprogs 13*6f9cba8fSJoseph Mingrone2. Run filtertest to compile BPF expression and produce the CFG as a 14*6f9cba8fSJoseph Mingrone DOT graph, save to output a.txt 15*6f9cba8fSJoseph Mingrone testprogs/filtertest -g EN10MB host 192.168.1.1 > a.txt 16*6f9cba8fSJoseph Mingrone3. Send a.txt to this program's standard input 17*6f9cba8fSJoseph Mingrone cat a.txt | testprogs/visopts.py 18*6f9cba8fSJoseph Mingrone (Graphviz must be installed) 19*6f9cba8fSJoseph Mingrone4. Step 2&3 can be merged: 20*6f9cba8fSJoseph Mingrone testprogs/filtertest -g EN10MB host 192.168.1.1 | testprogs/visopts.py 21*6f9cba8fSJoseph Mingrone5. The standard output is something like this: 22*6f9cba8fSJoseph Mingrone generated files under directory: /tmp/visopts-W9ekBw 23*6f9cba8fSJoseph Mingrone the directory will be removed when this programs finished. 24*6f9cba8fSJoseph Mingrone open this link: http://localhost:39062/expr1.html 25*6f9cba8fSJoseph Mingrone6. Open the URL at the 3rd line in a browser. 26*6f9cba8fSJoseph Mingrone 27*6f9cba8fSJoseph MingroneNote: 28*6f9cba8fSJoseph Mingrone1. The CFG is translated to SVG images, expr1.html embeds them as external 29*6f9cba8fSJoseph Mingrone documents. If you open expr1.html as local file using file:// protocol, some 30*6f9cba8fSJoseph Mingrone browsers will deny such requests so the web page will not work properly. 31*6f9cba8fSJoseph Mingrone For Chrome, you can run it using the following command to avoid this: 32*6f9cba8fSJoseph Mingrone chromium --disable-web-security 33*6f9cba8fSJoseph Mingrone That's why this program starts a localhost HTTP server. 34*6f9cba8fSJoseph Mingrone2. expr1.html uses jQuery from https://ajax.googleapis.com, so it needs Internet 35*6f9cba8fSJoseph Mingrone access to work. 36*6f9cba8fSJoseph Mingrone""" 37*6f9cba8fSJoseph Mingrone 38*6f9cba8fSJoseph Mingroneimport sys, os 39*6f9cba8fSJoseph Mingroneimport string 40*6f9cba8fSJoseph Mingroneimport subprocess 41*6f9cba8fSJoseph Mingroneimport json 42*6f9cba8fSJoseph Mingrone 43*6f9cba8fSJoseph Mingronehtml_template = string.Template(""" 44*6f9cba8fSJoseph Mingrone<html> 45*6f9cba8fSJoseph Mingrone <head> 46*6f9cba8fSJoseph Mingrone <title>BPF compiler optimization phases for $expr </title> 47*6f9cba8fSJoseph Mingrone <style type="text/css"> 48*6f9cba8fSJoseph Mingrone .hc { 49*6f9cba8fSJoseph Mingrone /* half width container */ 50*6f9cba8fSJoseph Mingrone display: inline-block; 51*6f9cba8fSJoseph Mingrone float: left; 52*6f9cba8fSJoseph Mingrone width: 50%; 53*6f9cba8fSJoseph Mingrone } 54*6f9cba8fSJoseph Mingrone </style> 55*6f9cba8fSJoseph Mingrone 56*6f9cba8fSJoseph Mingrone <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/></script> 57*6f9cba8fSJoseph Mingrone <!--script type="text/javascript" src="./jquery.min.js"/></script--> 58*6f9cba8fSJoseph Mingrone <script type="text/javascript"> 59*6f9cba8fSJoseph Mingrone var expr = '$expr'; 60*6f9cba8fSJoseph Mingrone var exprid = 1; 61*6f9cba8fSJoseph Mingrone var gcount = $gcount; 62*6f9cba8fSJoseph Mingrone var logs = JSON.parse('$logs'); 63*6f9cba8fSJoseph Mingrone logs[gcount] = ""; 64*6f9cba8fSJoseph Mingrone 65*6f9cba8fSJoseph Mingrone var leftsvg = null; 66*6f9cba8fSJoseph Mingrone var rightsvg = null; 67*6f9cba8fSJoseph Mingrone 68*6f9cba8fSJoseph Mingrone function gurl(index) { 69*6f9cba8fSJoseph Mingrone index += 1; 70*6f9cba8fSJoseph Mingrone if (index < 10) 71*6f9cba8fSJoseph Mingrone s = "00" + index; 72*6f9cba8fSJoseph Mingrone else if (index < 100) 73*6f9cba8fSJoseph Mingrone s = "0" + index; 74*6f9cba8fSJoseph Mingrone else 75*6f9cba8fSJoseph Mingrone s = "" + index; 76*6f9cba8fSJoseph Mingrone return "./expr" + exprid + "_g" + s + ".svg" 77*6f9cba8fSJoseph Mingrone } 78*6f9cba8fSJoseph Mingrone 79*6f9cba8fSJoseph Mingrone function annotate_svgs() { 80*6f9cba8fSJoseph Mingrone if (!leftsvg || !rightsvg) return; 81*6f9cba8fSJoseph Mingrone 82*6f9cba8fSJoseph Mingrone $$.each([$$(leftsvg), $$(rightsvg)], function() { 83*6f9cba8fSJoseph Mingrone $$(this).find("[id|='block'][opacity]").each(function() { 84*6f9cba8fSJoseph Mingrone $$(this).removeAttr('opacity'); 85*6f9cba8fSJoseph Mingrone }); 86*6f9cba8fSJoseph Mingrone }); 87*6f9cba8fSJoseph Mingrone 88*6f9cba8fSJoseph Mingrone $$(leftsvg).find("[id|='block']").each(function() { 89*6f9cba8fSJoseph Mingrone var has = $$(rightsvg).find("#" + this.id).length != 0; 90*6f9cba8fSJoseph Mingrone if (!has) $$(this).attr("opacity", "0.4"); 91*6f9cba8fSJoseph Mingrone else { 92*6f9cba8fSJoseph Mingrone $$(this).click(function() { 93*6f9cba8fSJoseph Mingrone var target = $$(rightsvg).find("#" + this.id); 94*6f9cba8fSJoseph Mingrone var offset = $$("#rightsvgc").offset().top + target.position().top; 95*6f9cba8fSJoseph Mingrone window.scrollTo(0, offset); 96*6f9cba8fSJoseph Mingrone target.focus(); 97*6f9cba8fSJoseph Mingrone }); 98*6f9cba8fSJoseph Mingrone } 99*6f9cba8fSJoseph Mingrone }); 100*6f9cba8fSJoseph Mingrone $$(rightsvg).find("[id|='block']").each(function() { 101*6f9cba8fSJoseph Mingrone var has = $$(leftsvg).find("#" + this.id).length != 0; 102*6f9cba8fSJoseph Mingrone if (!has) $$(this).attr("opacity", "0.4"); 103*6f9cba8fSJoseph Mingrone else { 104*6f9cba8fSJoseph Mingrone $$(this).click(function() { 105*6f9cba8fSJoseph Mingrone var target = $$(leftsvg).find("#" + this.id); 106*6f9cba8fSJoseph Mingrone var offset = $$("#leftsvgc").offset().top + target.position().top; 107*6f9cba8fSJoseph Mingrone window.scrollTo(0, offset); 108*6f9cba8fSJoseph Mingrone target.focus(); 109*6f9cba8fSJoseph Mingrone }); 110*6f9cba8fSJoseph Mingrone } 111*6f9cba8fSJoseph Mingrone }); 112*6f9cba8fSJoseph Mingrone } 113*6f9cba8fSJoseph Mingrone 114*6f9cba8fSJoseph Mingrone function init_svgroot(svg) { 115*6f9cba8fSJoseph Mingrone svg.setAttribute("width", "100%"); 116*6f9cba8fSJoseph Mingrone svg.setAttribute("height", "100%"); 117*6f9cba8fSJoseph Mingrone } 118*6f9cba8fSJoseph Mingrone function wait_leftsvg() { 119*6f9cba8fSJoseph Mingrone if (leftsvg) return; 120*6f9cba8fSJoseph Mingrone var doc = document.getElementById("leftsvgc").getSVGDocument(); 121*6f9cba8fSJoseph Mingrone if (doc == null) { 122*6f9cba8fSJoseph Mingrone setTimeout(wait_leftsvg, 500); 123*6f9cba8fSJoseph Mingrone return; 124*6f9cba8fSJoseph Mingrone } 125*6f9cba8fSJoseph Mingrone leftsvg = doc.documentElement; 126*6f9cba8fSJoseph Mingrone //console.log(leftsvg); 127*6f9cba8fSJoseph Mingrone // initialize it 128*6f9cba8fSJoseph Mingrone init_svgroot(leftsvg); 129*6f9cba8fSJoseph Mingrone annotate_svgs(); 130*6f9cba8fSJoseph Mingrone } 131*6f9cba8fSJoseph Mingrone function wait_rightsvg() { 132*6f9cba8fSJoseph Mingrone if (rightsvg) return; 133*6f9cba8fSJoseph Mingrone var doc = document.getElementById("rightsvgc").getSVGDocument(); 134*6f9cba8fSJoseph Mingrone if (doc == null) { 135*6f9cba8fSJoseph Mingrone setTimeout(wait_rightsvg, 500); 136*6f9cba8fSJoseph Mingrone return; 137*6f9cba8fSJoseph Mingrone } 138*6f9cba8fSJoseph Mingrone rightsvg = doc.documentElement; 139*6f9cba8fSJoseph Mingrone //console.log(rightsvg); 140*6f9cba8fSJoseph Mingrone // initialize it 141*6f9cba8fSJoseph Mingrone init_svgroot(rightsvg); 142*6f9cba8fSJoseph Mingrone annotate_svgs(); 143*6f9cba8fSJoseph Mingrone } 144*6f9cba8fSJoseph Mingrone function load_left(index) { 145*6f9cba8fSJoseph Mingrone var url = gurl(index); 146*6f9cba8fSJoseph Mingrone var frag = "<embed id='leftsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>"; 147*6f9cba8fSJoseph Mingrone $$("#lsvg").html(frag); 148*6f9cba8fSJoseph Mingrone $$("#lcomment").html(logs[index]); 149*6f9cba8fSJoseph Mingrone $$("#lsvglink").attr("href", url); 150*6f9cba8fSJoseph Mingrone leftsvg = null; 151*6f9cba8fSJoseph Mingrone wait_leftsvg(); 152*6f9cba8fSJoseph Mingrone } 153*6f9cba8fSJoseph Mingrone function load_right(index) { 154*6f9cba8fSJoseph Mingrone var url = gurl(index); 155*6f9cba8fSJoseph Mingrone var frag = "<embed id='rightsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>"; 156*6f9cba8fSJoseph Mingrone $$("#rsvg").html(frag); 157*6f9cba8fSJoseph Mingrone $$("#rcomment").html(logs[index]); 158*6f9cba8fSJoseph Mingrone $$("#rsvglink").attr("href", url); 159*6f9cba8fSJoseph Mingrone rightsvg = null; 160*6f9cba8fSJoseph Mingrone wait_rightsvg(); 161*6f9cba8fSJoseph Mingrone } 162*6f9cba8fSJoseph Mingrone 163*6f9cba8fSJoseph Mingrone $$(document).ready(function() { 164*6f9cba8fSJoseph Mingrone for (var i = 0; i < gcount; i++) { 165*6f9cba8fSJoseph Mingrone var opt = "<option value='" + i + "'>loop" + i + " -- " + logs[i] + "</option>"; 166*6f9cba8fSJoseph Mingrone $$("#lselect").append(opt); 167*6f9cba8fSJoseph Mingrone $$("#rselect").append(opt); 168*6f9cba8fSJoseph Mingrone } 169*6f9cba8fSJoseph Mingrone var on_selected = function() { 170*6f9cba8fSJoseph Mingrone var index = parseInt($$(this).children("option:selected").val()); 171*6f9cba8fSJoseph Mingrone if (this.id == "lselect") 172*6f9cba8fSJoseph Mingrone load_left(index); 173*6f9cba8fSJoseph Mingrone else 174*6f9cba8fSJoseph Mingrone load_right(index); 175*6f9cba8fSJoseph Mingrone } 176*6f9cba8fSJoseph Mingrone $$("#lselect").change(on_selected); 177*6f9cba8fSJoseph Mingrone $$("#rselect").change(on_selected); 178*6f9cba8fSJoseph Mingrone 179*6f9cba8fSJoseph Mingrone $$("#backward").click(function() { 180*6f9cba8fSJoseph Mingrone var index = parseInt($$("#lselect option:selected").val()); 181*6f9cba8fSJoseph Mingrone if (index <= 0) return; 182*6f9cba8fSJoseph Mingrone $$("#lselect").val(index - 1).change(); 183*6f9cba8fSJoseph Mingrone $$("#rselect").val(index).change(); 184*6f9cba8fSJoseph Mingrone }); 185*6f9cba8fSJoseph Mingrone $$("#forward").click(function() { 186*6f9cba8fSJoseph Mingrone var index = parseInt($$("#rselect option:selected").val()); 187*6f9cba8fSJoseph Mingrone if (index >= gcount - 1) return; 188*6f9cba8fSJoseph Mingrone $$("#lselect").val(index).change(); 189*6f9cba8fSJoseph Mingrone $$("#rselect").val(index + 1).change(); 190*6f9cba8fSJoseph Mingrone }); 191*6f9cba8fSJoseph Mingrone 192*6f9cba8fSJoseph Mingrone if (gcount >= 1) $$("#lselect").val(0).change(); 193*6f9cba8fSJoseph Mingrone if (gcount >= 2) $$("#rselect").val(1).change(); 194*6f9cba8fSJoseph Mingrone }); 195*6f9cba8fSJoseph Mingrone </script> 196*6f9cba8fSJoseph Mingrone </head> 197*6f9cba8fSJoseph Mingrone <body style="width: 96%"> 198*6f9cba8fSJoseph Mingrone <div> 199*6f9cba8fSJoseph Mingrone <h1>$expr</h1> 200*6f9cba8fSJoseph Mingrone <div style="text-align: center;"> 201*6f9cba8fSJoseph Mingrone <button id="backward" type="button"><<</button> 202*6f9cba8fSJoseph Mingrone 203*6f9cba8fSJoseph Mingrone <button id="forward" type="button">>></button> 204*6f9cba8fSJoseph Mingrone </div> 205*6f9cba8fSJoseph Mingrone </div> 206*6f9cba8fSJoseph Mingrone <br/> 207*6f9cba8fSJoseph Mingrone <div style="clear: both;"> 208*6f9cba8fSJoseph Mingrone <div class="hc lc"> 209*6f9cba8fSJoseph Mingrone <select id="lselect"></select> 210*6f9cba8fSJoseph Mingrone <a id="lsvglink" target="_blank">open this svg in browser</a> 211*6f9cba8fSJoseph Mingrone <p id="lcomment"></p> 212*6f9cba8fSJoseph Mingrone </div> 213*6f9cba8fSJoseph Mingrone <div class="hc rc"> 214*6f9cba8fSJoseph Mingrone <select id="rselect"></select> 215*6f9cba8fSJoseph Mingrone <a id="rsvglink" target="_blank">open this svg in browser</a> 216*6f9cba8fSJoseph Mingrone <p id="rcomment"></p> 217*6f9cba8fSJoseph Mingrone </div> 218*6f9cba8fSJoseph Mingrone </div> 219*6f9cba8fSJoseph Mingrone <br/> 220*6f9cba8fSJoseph Mingrone <div style="clear: both;"> 221*6f9cba8fSJoseph Mingrone <div id="lsvg" class="hc lc"></div> 222*6f9cba8fSJoseph Mingrone <div id="rsvg" class="hc rc"></div> 223*6f9cba8fSJoseph Mingrone </div> 224*6f9cba8fSJoseph Mingrone </body> 225*6f9cba8fSJoseph Mingrone</html> 226*6f9cba8fSJoseph Mingrone""") 227*6f9cba8fSJoseph Mingrone 228*6f9cba8fSJoseph Mingronedef write_html(expr, gcount, logs): 229*6f9cba8fSJoseph Mingrone logs = map(lambda s: s.strip().replace("\n", "<br/>"), logs) 230*6f9cba8fSJoseph Mingrone 231*6f9cba8fSJoseph Mingrone global html_template 232*6f9cba8fSJoseph Mingrone html = html_template.safe_substitute(expr=expr.encode("string-escape"), gcount=gcount, logs=json.dumps(logs).encode("string-escape")) 233*6f9cba8fSJoseph Mingrone with file("expr1.html", "wt") as f: 234*6f9cba8fSJoseph Mingrone f.write(html) 235*6f9cba8fSJoseph Mingrone 236*6f9cba8fSJoseph Mingronedef render_on_html(infile): 237*6f9cba8fSJoseph Mingrone expr = None 238*6f9cba8fSJoseph Mingrone gid = 1 239*6f9cba8fSJoseph Mingrone log = "" 240*6f9cba8fSJoseph Mingrone dot = "" 241*6f9cba8fSJoseph Mingrone indot = 0 242*6f9cba8fSJoseph Mingrone logs = [] 243*6f9cba8fSJoseph Mingrone 244*6f9cba8fSJoseph Mingrone for line in infile: 245*6f9cba8fSJoseph Mingrone if line.startswith("machine codes for filter:"): 246*6f9cba8fSJoseph Mingrone expr = line[len("machine codes for filter:"):].strip() 247*6f9cba8fSJoseph Mingrone break 248*6f9cba8fSJoseph Mingrone elif line.startswith("digraph BPF {"): 249*6f9cba8fSJoseph Mingrone indot = 1 250*6f9cba8fSJoseph Mingrone dot = line 251*6f9cba8fSJoseph Mingrone elif indot: 252*6f9cba8fSJoseph Mingrone dot += line 253*6f9cba8fSJoseph Mingrone if line.startswith("}"): 254*6f9cba8fSJoseph Mingrone indot = 2 255*6f9cba8fSJoseph Mingrone else: 256*6f9cba8fSJoseph Mingrone log += line 257*6f9cba8fSJoseph Mingrone 258*6f9cba8fSJoseph Mingrone if indot == 2: 259*6f9cba8fSJoseph Mingrone try: 260*6f9cba8fSJoseph Mingrone p = subprocess.Popen(['dot', '-Tsvg'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 261*6f9cba8fSJoseph Mingrone except OSError as ose: 262*6f9cba8fSJoseph Mingrone print "Failed to run 'dot':", ose 263*6f9cba8fSJoseph Mingrone print "(Is Graphviz installed?)" 264*6f9cba8fSJoseph Mingrone exit(1) 265*6f9cba8fSJoseph Mingrone 266*6f9cba8fSJoseph Mingrone svg = p.communicate(dot)[0] 267*6f9cba8fSJoseph Mingrone with file("expr1_g%03d.svg" % (gid), "wt") as f: 268*6f9cba8fSJoseph Mingrone f.write(svg) 269*6f9cba8fSJoseph Mingrone 270*6f9cba8fSJoseph Mingrone logs.append(log) 271*6f9cba8fSJoseph Mingrone gid += 1 272*6f9cba8fSJoseph Mingrone log = "" 273*6f9cba8fSJoseph Mingrone dot = "" 274*6f9cba8fSJoseph Mingrone indot = 0 275*6f9cba8fSJoseph Mingrone 276*6f9cba8fSJoseph Mingrone if indot != 0: 277*6f9cba8fSJoseph Mingrone #unterminated dot graph for expression 278*6f9cba8fSJoseph Mingrone return False 279*6f9cba8fSJoseph Mingrone if expr is None: 280*6f9cba8fSJoseph Mingrone # BPF parser encounter error(s) 281*6f9cba8fSJoseph Mingrone return False 282*6f9cba8fSJoseph Mingrone write_html(expr, gid - 1, logs) 283*6f9cba8fSJoseph Mingrone return True 284*6f9cba8fSJoseph Mingrone 285*6f9cba8fSJoseph Mingronedef run_httpd(): 286*6f9cba8fSJoseph Mingrone import SimpleHTTPServer 287*6f9cba8fSJoseph Mingrone import SocketServer 288*6f9cba8fSJoseph Mingrone 289*6f9cba8fSJoseph Mingrone class MySocketServer(SocketServer.TCPServer): 290*6f9cba8fSJoseph Mingrone allow_reuse_address = True 291*6f9cba8fSJoseph Mingrone Handler = SimpleHTTPServer.SimpleHTTPRequestHandler 292*6f9cba8fSJoseph Mingrone httpd = MySocketServer(("localhost", 0), Handler) 293*6f9cba8fSJoseph Mingrone print "open this link: http://localhost:%d/expr1.html" % (httpd.server_address[1]) 294*6f9cba8fSJoseph Mingrone try: 295*6f9cba8fSJoseph Mingrone httpd.serve_forever() 296*6f9cba8fSJoseph Mingrone except KeyboardInterrupt as e: 297*6f9cba8fSJoseph Mingrone pass 298*6f9cba8fSJoseph Mingrone 299*6f9cba8fSJoseph Mingronedef main(): 300*6f9cba8fSJoseph Mingrone import tempfile 301*6f9cba8fSJoseph Mingrone import atexit 302*6f9cba8fSJoseph Mingrone import shutil 303*6f9cba8fSJoseph Mingrone os.chdir(tempfile.mkdtemp(prefix="visopts-")) 304*6f9cba8fSJoseph Mingrone atexit.register(shutil.rmtree, os.getcwd()) 305*6f9cba8fSJoseph Mingrone print "generated files under directory: %s" % os.getcwd() 306*6f9cba8fSJoseph Mingrone print " the directory will be removed when this program has finished." 307*6f9cba8fSJoseph Mingrone 308*6f9cba8fSJoseph Mingrone if not render_on_html(sys.stdin): 309*6f9cba8fSJoseph Mingrone return 1 310*6f9cba8fSJoseph Mingrone run_httpd() 311*6f9cba8fSJoseph Mingrone return 0 312*6f9cba8fSJoseph Mingrone 313*6f9cba8fSJoseph Mingroneif __name__ == "__main__": 314*6f9cba8fSJoseph Mingrone if '-h' in sys.argv or '--help' in sys.argv: 315*6f9cba8fSJoseph Mingrone print __doc__ 316*6f9cba8fSJoseph Mingrone exit(0) 317*6f9cba8fSJoseph Mingrone exit(main()) 318