1 | #!/usr/dcs/software/supported/bin/perl -w |
2 | # LLVM Web Demo script |
3 | # |
4 | |
5 | use strict; |
6 | use CGI; |
7 | use POSIX; |
8 | use Mail::Send; |
9 | |
10 | $| = 1; |
11 | |
12 | my $ROOT = "/tmp/webcompile"; |
13 | #my $ROOT = "/home/vadve/lattner/webcompile"; |
14 | |
15 | open( STDERR, ">&STDOUT" ) or die "can't redirect stderr to stdout"; |
16 | |
17 | if ( !-d $ROOT ) { mkdir( $ROOT, 0777 ); } |
18 | |
19 | my $LOGFILE = "$ROOT/log.txt"; |
20 | my $FORM_URL = 'index.cgi'; |
21 | my $MAILADDR = 'sabre@nondot.org'; |
22 | my $CONTACT_ADDRESS = 'Questions or comments? Email the <a href="http://lists.llvm.org/mailman/listinfo/llvm-dev">LLVM-dev mailing list</a>.'; |
23 | my $LOGO_IMAGE_URL = 'cathead.png'; |
24 | my $TIMEOUTAMOUNT = 20; |
25 | $ENV{'LD_LIBRARY_PATH'} = '/home/vadve/shared/localtools/fc1/lib/'; |
26 | |
27 | my @PREPENDPATHDIRS = |
28 | ( |
29 | '/home/vadve/shared/llvm-gcc4.0-2.1/bin/', |
30 | '/home/vadve/shared/llvm-2.1/Release/bin'); |
31 | |
32 | my $defaultsrc = "#include <stdio.h>\n#include <stdlib.h>\n\n" . |
33 | "int power(int X) {\n if (X == 0) return 1;\n" . |
34 | " return X*power(X-1);\n}\n\n" . |
35 | "int main(int argc, char **argv) {\n" . |
36 | " printf(\"%d\\n\", power(atoi(argv[0])));\n}\n"; |
37 | |
38 | sub getname { |
39 | my ($extension) = @_; |
40 | for ( my $count = 0 ; ; $count++ ) { |
41 | my $name = |
42 | sprintf( "$ROOT/_%d_%d%s", $$, $count, $extension ); |
43 | if ( !-f $name ) { return $name; } |
44 | } |
45 | } |
46 | |
47 | my $c; |
48 | |
49 | sub barf { |
50 | print "<b>", @_, "</b>\n"; |
51 | print $c->end_html; |
52 | system("rm -f $ROOT/locked"); |
53 | exit 1; |
54 | } |
55 | |
56 | sub writeIntoFile { |
57 | my $extension = shift @_; |
58 | my $contents = join "", @_; |
59 | my $name = getname($extension); |
60 | local (*FILE); |
61 | open( FILE, ">$name" ) or barf("Can't write to $name: $!"); |
62 | print FILE $contents; |
63 | close FILE; |
64 | return $name; |
65 | } |
66 | |
67 | sub addlog { |
68 | my ( $source, $pid, $result ) = @_; |
69 | open( LOG, ">>$LOGFILE" ); |
70 | my $time = scalar localtime; |
71 | my $remotehost = $ENV{'REMOTE_ADDR'}; |
72 | print LOG "[$time] [$remotehost]: $pid\n"; |
73 | print LOG "<<<\n$source\n>>>\nResult is: <<<\n$result\n>>>\n"; |
74 | close LOG; |
75 | } |
76 | |
77 | sub dumpFile { |
78 | my ( $header, $file ) = @_; |
79 | my $result; |
80 | open( FILE, "$file" ) or barf("Can't read $file: $!"); |
81 | while (<FILE>) { |
82 | $result .= $_; |
83 | } |
84 | close FILE; |
85 | my $UnhilightedResult = $result; |
86 | my $HtmlResult = |
87 | "<h3>$header</h3>\n<pre>\n" . $c->escapeHTML($result) . "\n</pre>\n"; |
88 | if (wantarray) { |
89 | return ( $UnhilightedResult, $HtmlResult ); |
90 | } |
91 | else { |
92 | return $HtmlResult; |
93 | } |
94 | } |
95 | |
96 | sub syntaxHighlightLLVM { |
97 | my ($input) = @_; |
98 | $input =~ s@\b(void|i8|i1|i16|i32|i64|float|double|type|label|opaque)\b@<span class="llvm_type">$1</span>@g; |
99 | $input =~ s@\b(add|sub|mul|div|rem|and|or|xor|setne|seteq|setlt|setgt|setle|setge|phi|tail|call|cast|to|shl|shr|vaarg|vanext|ret|br|switch|invoke|unwind|malloc|alloca|free|load|store|getelementptr|begin|end|true|false|declare|global|constant|const|internal|uninitialized|external|implementation|linkonce|weak|appending|null|to|except|not|target|endian|pointersize|big|little|volatile)\b@<span class="llvm_keyword">$1</span>@g; |
100 | |
101 | # Add links to the FAQ. |
102 | $input =~ s@(_ZNSt8ios_base4Init[DC]1Ev)@<a href="../docs/FAQ.html#iosinit">$1</a>@g; |
103 | $input =~ s@\bundef\b@<a href="../docs/FAQ.html#undef">undef</a>@g; |
104 | return $input; |
105 | } |
106 | |
107 | sub mailto { |
108 | my ( $recipient, $body ) = @_; |
109 | my $msg = |
110 | new Mail::Send( Subject => "LLVM Demo Page Run", To => $recipient ); |
111 | my $fh = $msg->open(); |
112 | print $fh $body; |
113 | $fh->close(); |
114 | } |
115 | |
116 | $c = new CGI; |
117 | print $c->header; |
118 | |
119 | print <<EOF; |
120 | <html> |
121 | <head> |
122 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
123 | <title>Try out LLVM in your browser!</title> |
124 | <style> |
125 | \@import url("syntax.css"); |
126 | \@import url("http://llvm.org/llvm.css"); |
127 | </style> |
128 | </head> |
129 | <body leftmargin="10" marginwidth="10"> |
130 | |
131 | <div class="www_sectiontitle"> |
132 | Try out LLVM in your browser! |
133 | </div> |
134 | |
135 | <table border=0><tr><td> |
136 | <img align=right width=100 height=111 src="$LOGO_IMAGE_URL"> |
137 | </td><td> |
138 | EOF |
139 | |
140 | if ( -f "$ROOT/locked" ) { |
141 | my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$locktime) = |
142 | stat("$ROOT/locked"); |
143 | my $currtime = time(); |
144 | if ($locktime + 60 > $currtime) { |
145 | print "This page is already in use by someone else at this "; |
146 | print "time, try reloading in a second or two. Meow!</td></tr></table>'\n"; |
147 | exit 0; |
148 | } |
149 | } |
150 | |
151 | system("touch $ROOT/locked"); |
152 | |
153 | print <<END; |
154 | Bitter Melon the cat says, paste a C/C++ program in the text box or upload |
155 | one from your computer, and you can see LLVM compile it, meow!! |
156 | </td></tr></table><p> |
157 | END |
158 | |
159 | print $c->start_multipart_form( 'POST', $FORM_URL ); |
160 | |
161 | my $source = $c->param('source'); |
162 | |
163 | |
164 | # Start the user out with something valid if no code. |
165 | $source = $defaultsrc if (!defined($source)); |
166 | |
167 | print '<table border="0"><tr><td>'; |
168 | |
169 | print "Type your source code in below: (<a href='DemoInfo.html#hints'>hints and |
170 | advice</a>)<br>\n"; |
171 | |
172 | print $c->textarea( |
173 | -name => "source", |
174 | -rows => 16, |
175 | -columns => 60, |
176 | -default => $source |
177 | ), "<br>"; |
178 | |
179 | print "Or upload a file: "; |
180 | print $c->filefield( -name => 'uploaded_file', -default => '' ); |
181 | |
182 | print "<p />\n"; |
183 | |
184 | |
185 | print '<p></td><td valign=top>'; |
186 | |
187 | print "<center><h3>General Options</h3></center>"; |
188 | |
189 | print "Source language: ", |
190 | $c->radio_group( |
191 | -name => 'language', |
192 | -values => [ 'C', 'C++' ], |
193 | -default => 'C' |
194 | ), "<p>"; |
195 | |
196 | print $c->checkbox( |
197 | -name => 'linkopt', |
198 | -label => 'Run link-time optimizer', |
199 | -checked => 'checked' |
200 | ),' <a href="DemoInfo.html#lto">?</a><br>'; |
201 | |
202 | print $c->checkbox( |
203 | -name => 'showstats', |
204 | -label => 'Show detailed pass statistics' |
205 | ), ' <a href="DemoInfo.html#stats">?</a><br>'; |
206 | |
207 | print $c->checkbox( |
208 | -name => 'cxxdemangle', |
209 | -label => 'Demangle C++ names' |
210 | ),' <a href="DemoInfo.html#demangle">?</a><p>'; |
211 | |
212 | |
213 | print "<center><h3>Output Options</h3></center>"; |
214 | |
215 | print $c->checkbox( |
216 | -name => 'showbcanalysis', |
217 | -label => 'Show detailed bytecode analysis' |
218 | ),' <a href="DemoInfo.html#bcanalyzer">?</a><br>'; |
219 | |
220 | print $c->checkbox( |
221 | -name => 'showllvm2cpp', |
222 | -label => 'Show LLVM C++ API code' |
223 | ), ' <a href="DemoInfo.html#llvm2cpp">?</a>'; |
224 | |
225 | print "</td></tr></table>"; |
226 | |
227 | print "<center>", $c->submit(-value=> 'Compile Source Code'), |
228 | "</center>\n", $c->endform; |
229 | |
230 | print "\n<p>If you have questions about the LLVM code generated by the |
231 | front-end, please check the <a href='/docs/FAQ.html#cfe_code'>FAQ</a> and |
232 | the demo page <a href='DemoInfo.html#hints'>hints section</a>. |
233 | </p>\n"; |
234 | |
235 | $ENV{'PATH'} = ( join ( ':', @PREPENDPATHDIRS ) ) . ":" . $ENV{'PATH'}; |
236 | |
237 | sub sanitychecktools { |
238 | my $sanitycheckfail = ''; |
239 | |
240 | # insert tool-specific sanity checks here |
241 | $sanitycheckfail .= ' llvm-dis' |
242 | if `llvm-dis --help 2>&1` !~ /ll disassembler/; |
243 | |
244 | $sanitycheckfail .= ' llvm-gcc' |
245 | if ( `llvm-gcc --version 2>&1` !~ /Free Software Foundation/ ); |
246 | |
247 | $sanitycheckfail .= ' llvm-ld' |
248 | if `llvm-ld --help 2>&1` !~ /llvm linker/; |
249 | |
250 | $sanitycheckfail .= ' llvm-bcanalyzer' |
251 | if `llvm-bcanalyzer --help 2>&1` !~ /bcanalyzer/; |
252 | |
253 | barf( |
254 | "<br/>The demo page is currently unavailable. [tools: ($sanitycheckfail ) failed sanity check]" |
255 | ) |
256 | if $sanitycheckfail; |
257 | } |
258 | |
259 | sanitychecktools(); |
260 | |
261 | sub try_run { |
262 | my ( $program, $commandline, $outputFile ) = @_; |
263 | my $retcode = 0; |
264 | |
265 | eval { |
266 | local $SIG{ALRM} = sub { die "timeout"; }; |
267 | alarm $TIMEOUTAMOUNT; |
268 | $retcode = system($commandline); |
269 | alarm 0; |
270 | }; |
271 | if ( $@ and $@ =~ /timeout/ ) { |
272 | barf("Program $program took too long, compile time limited for the web script, sorry!\n"); |
273 | } |
274 | if ( -s $outputFile ) { |
275 | print scalar dumpFile( "Output from $program", $outputFile ); |
276 | } |
277 | #print "<p>Finished dumping command output.</p>\n"; |
278 | if ( WIFEXITED($retcode) && WEXITSTATUS($retcode) != 0 ) { |
279 | barf( |
280 | "$program exited with an error. Please correct source and resubmit.<p>\n" . |
281 | "Please note that this form only allows fully formed and correct source" . |
282 | " files. It will not compile fragments of code.<p>" |
283 | ); |
284 | } |
285 | if ( WIFSIGNALED($retcode) != 0 ) { |
286 | my $sig = WTERMSIG($retcode); |
287 | barf( |
288 | "Ouch, $program caught signal $sig. Sorry, better luck next time!\n" |
289 | ); |
290 | } |
291 | } |
292 | |
293 | my %suffixes = ( |
294 | 'Java' => '.java', |
295 | 'JO99' => '.jo9', |
296 | 'C' => '.c', |
297 | 'C++' => '.cc', |
298 | 'Stacker' => '.st', |
299 | 'preprocessed C' => '.i', |
300 | 'preprocessed C++' => '.ii' |
301 | ); |
302 | my %languages = ( |
303 | '.jo9' => 'JO99', |
304 | '.java' => 'Java', |
305 | '.c' => 'C', |
306 | '.i' => 'preprocessed C', |
307 | '.ii' => 'preprocessed C++', |
308 | '.cc' => 'C++', |
309 | '.cpp' => 'C++', |
310 | '.st' => 'Stacker' |
311 | ); |
312 | |
313 | my $uploaded_file_name = $c->param('uploaded_file'); |
314 | if ($uploaded_file_name) { |
315 | if ($source) { |
316 | barf( |
317 | "You must choose between uploading a file and typing code in. You can't do both at the same time." |
318 | ); |
319 | } |
320 | $uploaded_file_name =~ s/^.*(\.[A-Za-z]+)$/$1/; |
321 | my $language = $languages{$uploaded_file_name}; |
322 | $c->param( 'language', $language ); |
323 | |
324 | print "<p>Processing uploaded file. It looks like $language.</p>\n"; |
325 | my $fh = $c->upload('uploaded_file'); |
326 | if ( !$fh ) { |
327 | barf( "Error uploading file: " . $c->cgi_error ); |
328 | } |
329 | while (<$fh>) { |
330 | $source .= $_; |
331 | } |
332 | close $fh; |
333 | } |
334 | |
335 | if ($c->param('source')) { |
336 | print $c->hr; |
337 | my $extension = $suffixes{ $c->param('language') }; |
338 | barf "Unknown language; can't compile\n" unless $extension; |
339 | |
340 | # Add a newline to the source here to avoid a warning from gcc. |
341 | $source .= "\n"; |
342 | |
343 | # Avoid security hole due to #including bad stuff. |
344 | $source =~ |
345 | s@(\n)?#include.*[<"](.*\.\..*)[">].*\n@$1#error "invalid #include file $2 detected"\n@g; |
346 | |
347 | my $inputFile = writeIntoFile( $extension, $source ); |
348 | my $pid = $$; |
349 | |
350 | my $bytecodeFile = getname(".bc"); |
351 | my $outputFile = getname(".llvm-gcc.out"); |
352 | my $timerFile = getname(".llvm-gcc.time"); |
353 | |
354 | my $stats = ''; |
355 | if ( $extension eq ".st" ) { |
356 | $stats = "-stats -time-passes " |
357 | if ( $c->param('showstats') ); |
358 | try_run( "llvm Stacker front-end (stkrc)", |
359 | "stkrc $stats -o $bytecodeFile $inputFile > $outputFile 2>&1", |
360 | $outputFile ); |
361 | } else { |
362 | #$stats = "-Wa,--stats,--time-passes,--info-output-file=$timerFile" |
363 | $stats = "-ftime-report" |
364 | if ( $c->param('showstats') ); |
365 | try_run( "llvm C/C++ front-end (llvm-gcc)", |
366 | "llvm-gcc -emit-llvm -W -Wall -O2 $stats -o $bytecodeFile -c $inputFile > $outputFile 2>&1", |
367 | $outputFile ); |
368 | } |
369 | |
370 | if ( $c->param('showstats') && -s $timerFile ) { |
371 | my ( $UnhilightedResult, $HtmlResult ) = |
372 | dumpFile( "Statistics for front-end compilation", $timerFile ); |
373 | print "$HtmlResult\n"; |
374 | } |
375 | |
376 | if ( $c->param('linkopt') ) { |
377 | my $stats = ''; |
378 | my $outputFile = getname(".gccld.out"); |
379 | my $timerFile = getname(".gccld.time"); |
380 | $stats = "--stats --time-passes --info-output-file=$timerFile" |
381 | if ( $c->param('showstats') ); |
382 | my $tmpFile = getname(".bc"); |
383 | try_run( |
384 | "optimizing linker (llvm-ld)", |
385 | "llvm-ld $stats -o=$tmpFile $bytecodeFile > $outputFile 2>&1", |
386 | $outputFile |
387 | ); |
388 | system("mv $tmpFile.bc $bytecodeFile"); |
389 | system("rm $tmpFile"); |
390 | |
391 | if ( $c->param('showstats') && -s $timerFile ) { |
392 | my ( $UnhilightedResult, $HtmlResult ) = |
393 | dumpFile( "Statistics for optimizing linker", $timerFile ); |
394 | print "$HtmlResult\n"; |
395 | } |
396 | } |
397 | |
398 | print " Bytecode size is ", -s $bytecodeFile, " bytes.\n"; |
399 | |
400 | my $disassemblyFile = getname(".ll"); |
401 | try_run( "llvm-dis", |
402 | "llvm-dis -o=$disassemblyFile $bytecodeFile > $outputFile 2>&1", |
403 | $outputFile ); |
404 | |
405 | if ( $c->param('cxxdemangle') ) { |
406 | print " Demangling disassembler output.\n"; |
407 | my $tmpFile = getname(".ll"); |
408 | system("c++filt < $disassemblyFile > $tmpFile 2>&1"); |
409 | system("mv $tmpFile $disassemblyFile"); |
410 | } |
411 | |
412 | my ( $UnhilightedResult, $HtmlResult ); |
413 | if ( -s $disassemblyFile ) { |
414 | ( $UnhilightedResult, $HtmlResult ) = |
415 | dumpFile( "Output from LLVM disassembler", $disassemblyFile ); |
416 | print syntaxHighlightLLVM($HtmlResult); |
417 | } |
418 | else { |
419 | print "<p>Hmm, that's weird, llvm-dis didn't produce any output.</p>\n"; |
420 | } |
421 | |
422 | if ( $c->param('showbcanalysis') ) { |
423 | my $analFile = getname(".bca"); |
424 | try_run( "llvm-bcanalyzer", "llvm-bcanalyzer $bytecodeFile > $analFile 2>&1", |
425 | $analFile); |
426 | } |
427 | if ($c->param('showllvm2cpp') ) { |
428 | my $l2cppFile = getname(".l2cpp"); |
429 | try_run("llvm2cpp","llvm2cpp $bytecodeFile -o $l2cppFile 2>&1", |
430 | $l2cppFile); |
431 | } |
432 | |
433 | # Get the source presented by the user to CGI, convert newline sequences to simple \n. |
434 | my $actualsrc = $c->param('source'); |
435 | $actualsrc =~ s/\015\012/\n/go; |
436 | # Don't log this or mail it if it is the default code. |
437 | if ($actualsrc ne $defaultsrc) { |
438 | addlog( $source, $pid, $UnhilightedResult ); |
439 | |
440 | my ( $ip, $host, $lg, $lines ); |
441 | chomp( $lines = `wc -l < $inputFile` ); |
442 | $lg = $c->param('language'); |
443 | $ip = $c->remote_addr(); |
444 | chomp( $host = `host $ip` ) if $ip; |
445 | mailto( $MAILADDR, |
446 | "--- Query: ---\nFrom: ($ip) $host\nInput: $lines lines of $lg\n" |
447 | . "C++ demangle = " |
448 | . ( $c->param('cxxdemangle') ? 1 : 0 ) |
449 | . ", Link opt = " |
450 | . ( $c->param('linkopt') ? 1 : 0 ) . "\n\n" |
451 | . ", Show stats = " |
452 | . ( $c->param('showstats') ? 1 : 0 ) . "\n\n" |
453 | . "--- Source: ---\n$source\n" |
454 | . "--- Result: ---\n$UnhilightedResult\n" ); |
455 | } |
456 | unlink( $inputFile, $bytecodeFile, $outputFile, $disassemblyFile ); |
457 | } |
458 | |
459 | print $c->hr, "<address>$CONTACT_ADDRESS</address>", $c->end_html; |
460 | system("rm $ROOT/locked"); |
461 | exit 0; |
462 | |