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