xref: /freebsd/contrib/libpcap/testprogs/visopts.py (revision 6f9cba8f8b5efd16249633e52483ea351876b67b)
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">&lt;&lt;</button>
202*6f9cba8fSJoseph Mingrone          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
203*6f9cba8fSJoseph Mingrone        <button id="forward" type="button">&gt;&gt;</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