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