1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/devtools/rootAnalysis/run_complete Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,367 @@ 1.4 +#!/usr/bin/perl 1.5 + 1.6 +# Sixgill: Static assertion checker for C/C++ programs. 1.7 +# Copyright (C) 2009-2010 Stanford University 1.8 +# Author: Brian Hackett 1.9 +# 1.10 +# This program is free software: you can redistribute it and/or modify 1.11 +# it under the terms of the GNU General Public License as published by 1.12 +# the Free Software Foundation, either version 3 of the License, or 1.13 +# (at your option) any later version. 1.14 +# 1.15 +# This program is distributed in the hope that it will be useful, 1.16 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 1.17 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.18 +# GNU General Public License for more details. 1.19 +# 1.20 +# You should have received a copy of the GNU General Public License 1.21 +# along with this program. If not, see <http://www.gnu.org/licenses/>. 1.22 + 1.23 +# do a complete run of the system from raw source to reports. this requires 1.24 +# various run_monitor processes to be running in the background (maybe on other 1.25 +# machines) and watching a shared poll_file for jobs. if the output directory 1.26 +# for this script already exists then an incremental analysis will be performed 1.27 +# and the reports will only reflect the changes since the earlier run. 1.28 + 1.29 +use strict; 1.30 +use warnings; 1.31 +use IO::Handle; 1.32 +use File::Basename qw(dirname); 1.33 +use Getopt::Long; 1.34 +use Cwd; 1.35 + 1.36 +################################# 1.37 +# environment specific settings # 1.38 +################################# 1.39 + 1.40 +my $WORKDIR; 1.41 +my $SIXGILL_BIN; 1.42 + 1.43 +# poll file shared with the run_monitor script. 1.44 +my $poll_file; 1.45 + 1.46 +# root directory of the project. 1.47 +my $build_dir; 1.48 + 1.49 +# directory containing gcc wrapper scripts. 1.50 +my $wrap_dir; 1.51 + 1.52 +# optional file with annotations from the web interface. 1.53 +my $ann_file = ""; 1.54 + 1.55 +# optional output directory to do a diff against. 1.56 +my $old_dir = ""; 1.57 + 1.58 +# run in the foreground 1.59 +my $foreground; 1.60 + 1.61 +my $builder = "make -j4"; 1.62 + 1.63 +my $suppress_logs; 1.64 +GetOptions("build-root|b=s" => \$build_dir, 1.65 + "poll-file=s" => \$poll_file, 1.66 + "no-logs!" => \$suppress_logs, 1.67 + "work-dir=s" => \$WORKDIR, 1.68 + "sixgill-binaries|binaries|b=s" => \$SIXGILL_BIN, 1.69 + "wrap-dir=s" => \$wrap_dir, 1.70 + "annotations-file|annotations|a=s" => \$ann_file, 1.71 + "old-dir|old=s" => \$old_dir, 1.72 + "foreground!" => \$foreground, 1.73 + "buildcommand=s" => \$builder, 1.74 + ) 1.75 + or die; 1.76 + 1.77 +if (not -d $build_dir) { 1.78 + mkdir($build_dir); 1.79 +} 1.80 +if ($old_dir ne "" && not -d $old_dir) { 1.81 + die "Old directory '$old_dir' does not exist\n"; 1.82 +} 1.83 + 1.84 +$WORKDIR ||= "sixgill-work"; 1.85 +mkdir($WORKDIR, 0755) if ! -d $WORKDIR; 1.86 +$poll_file ||= "$WORKDIR/poll.file"; 1.87 +$build_dir ||= "$WORKDIR/js-inbound-xgill"; 1.88 + 1.89 +if (!defined $SIXGILL_BIN) { 1.90 + chomp(my $path = `which xmanager`); 1.91 + if ($path) { 1.92 + use File::Basename qw(dirname); 1.93 + $SIXGILL_BIN = dirname($path); 1.94 + } else { 1.95 + die "Cannot find sixgill binaries. Use the -b option."; 1.96 + } 1.97 +} 1.98 + 1.99 +$wrap_dir ||= "$WORKDIR/xgill-inbound/wrap_gcc"; 1.100 +$wrap_dir = "$SIXGILL_BIN/../scripts/wrap_gcc" if not (-e "$wrap_dir/basecc"); 1.101 +die "Bad wrapper directory: $wrap_dir" if not (-e "$wrap_dir/basecc"); 1.102 + 1.103 +# code to clean the project from $build_dir. 1.104 +sub clean_project { 1.105 + system("make clean"); 1.106 +} 1.107 + 1.108 +# code to build the project from $build_dir. 1.109 +sub build_project { 1.110 + return system($builder) >> 8; 1.111 +} 1.112 + 1.113 +our %kill_on_exit; 1.114 +END { 1.115 + for my $pid (keys %kill_on_exit) { 1.116 + kill($pid); 1.117 + } 1.118 +} 1.119 + 1.120 +# commands to start the various xgill binaries. timeouts can be specified 1.121 +# for the backend analyses here, and a memory limit can be specified for 1.122 +# xmanager if desired (and USE_COUNT_ALLOCATOR is defined in util/alloc.h). 1.123 +my $xmanager = "$SIXGILL_BIN/xmanager"; 1.124 +my $xsource = "$SIXGILL_BIN/xsource"; 1.125 +my $xmemlocal = "$SIXGILL_BIN/xmemlocal -timeout=20"; 1.126 +my $xinfer = "$SIXGILL_BIN/xinfer -timeout=60"; 1.127 +my $xcheck = "$SIXGILL_BIN/xcheck -timeout=30"; 1.128 + 1.129 +# prefix directory to strip off source files. 1.130 +my $prefix_dir = $build_dir; 1.131 + 1.132 +########################## 1.133 +# general purpose script # 1.134 +########################## 1.135 + 1.136 +# Prevent ccache from being used. I don't think this does any good. The problem 1.137 +# I'm struggling with is that if autoconf.mk still has 'ccache gcc' in it, the 1.138 +# builds fail in a mysterious way. 1.139 +$ENV{CCACHE_COMPILERCHECK} = 'date +%s.%N'; 1.140 +delete $ENV{CCACHE_PREFIX}; 1.141 + 1.142 +my $usage = "USAGE: run_complete result-dir\n"; 1.143 +my $result_dir = shift or die $usage; 1.144 + 1.145 +if (not $foreground) { 1.146 + my $pid = fork(); 1.147 + if ($pid != 0) { 1.148 + print "Forked, exiting...\n"; 1.149 + exit(0); 1.150 + } 1.151 +} 1.152 + 1.153 +# if the result directory does not already exist, mark for a clean build. 1.154 +my $do_clean = 0; 1.155 +if (not (-d $result_dir)) { 1.156 + $do_clean = 1; 1.157 + mkdir $result_dir; 1.158 +} 1.159 + 1.160 +if (!$suppress_logs) { 1.161 + my $log_file = "$result_dir/complete.log"; 1.162 + open(OUT, ">>", $log_file) or die "append to $log_file: $!"; 1.163 + OUT->autoflush(1); # don't buffer writes to the main log. 1.164 + 1.165 + # redirect stdout and stderr to the log. 1.166 + STDOUT->fdopen(\*OUT, "w"); 1.167 + STDERR->fdopen(\*OUT, "w"); 1.168 +} 1.169 + 1.170 +# pids to wait on before exiting. these are collating worker output. 1.171 +my @waitpids; 1.172 + 1.173 +chdir $result_dir; 1.174 + 1.175 +# to do a partial run, comment out the commands here you don't want to do. 1.176 + 1.177 +my $status = run_build(); 1.178 + 1.179 +# end of run commands. 1.180 + 1.181 +for my $pid (@waitpids) { 1.182 + waitpid($pid, 0); 1.183 + $status ||= $? >> 8; 1.184 +} 1.185 + 1.186 +print "Exiting run_complete with status $status\n"; 1.187 +exit $status; 1.188 + 1.189 +# get the IP address which a freshly created manager is listening on. 1.190 +sub get_manager_address 1.191 +{ 1.192 + my $log_file = shift or die; 1.193 + 1.194 + # give the manager one second to start, any longer and something's broken. 1.195 + sleep(1); 1.196 + 1.197 + my $log_data = `cat $log_file`; 1.198 + my ($port) = $log_data =~ /Listening on ([\.\:0-9]*)/ 1.199 + or die "no manager found"; 1.200 + print OUT "Connecting to manager on port $port\n" unless $suppress_logs; 1.201 + print "Connecting to manager on port $port.\n"; 1.202 + return $1; 1.203 +} 1.204 + 1.205 +sub run_build 1.206 +{ 1.207 + print "build started: "; 1.208 + print scalar(localtime()); 1.209 + print "\n"; 1.210 + 1.211 + # fork off a process to run the build. 1.212 + defined(my $pid = fork) or die; 1.213 + 1.214 + # log file for the manager. 1.215 + my $manager_log_file = "$result_dir/build_manager.log"; 1.216 + 1.217 + if (!$pid) { 1.218 + # this is the child process, fork another process to run a manager. 1.219 + defined(my $pid = fork) or die; 1.220 + exec("$xmanager -terminate-on-assert > $manager_log_file 2>&1") if (!$pid); 1.221 + $kill_on_exit{$pid} = 1; 1.222 + 1.223 + if (!$suppress_logs) { 1.224 + # open new streams to redirect stdout and stderr. 1.225 + open(LOGOUT, "> $result_dir/build.log"); 1.226 + open(LOGERR, "> $result_dir/build_err.log"); 1.227 + STDOUT->fdopen(\*LOGOUT, "w"); 1.228 + STDERR->fdopen(\*LOGERR, "w"); 1.229 + } 1.230 + 1.231 + my $address = get_manager_address($manager_log_file); 1.232 + 1.233 + # write the configuration file for the wrapper script. 1.234 + my $config_file = "$WORKDIR/xgill.config"; 1.235 + open(CONFIG, ">", $config_file) or die "create $config_file: $!"; 1.236 + print CONFIG "$prefix_dir\n"; 1.237 + print CONFIG Cwd::abs_path("$result_dir/build_xgill.log")."\n"; 1.238 + print CONFIG "$address\n"; 1.239 + my @extra = ("-fplugin-arg-xgill-mangle=1"); 1.240 + push(@extra, "-fplugin-arg-xgill-annfile=$ann_file") 1.241 + if ($ann_file ne "" && -e $ann_file); 1.242 + print CONFIG join(" ", @extra) . "\n"; 1.243 + close(CONFIG); 1.244 + 1.245 + # Tell the wrapper where to find the config 1.246 + $ENV{"XGILL_CONFIG"} = Cwd::abs_path($config_file); 1.247 + 1.248 + # update the PATH so that the build will see the wrappers. 1.249 + $ENV{"PATH"} = "$wrap_dir:" . $ENV{"PATH"}; 1.250 + 1.251 + # do the build, cleaning if necessary. 1.252 + chdir $build_dir; 1.253 + clean_project() if ($do_clean); 1.254 + my $exit_status = build_project(); 1.255 + 1.256 + # signal the manager that it's over. 1.257 + system("$xsource -remote=$address -end-manager"); 1.258 + 1.259 + # wait for the manager to clean up and terminate. 1.260 + print "Waiting for manager to finish (build status $exit_status)...\n"; 1.261 + waitpid($pid, 0); 1.262 + my $manager_status = $?; 1.263 + delete $kill_on_exit{$pid}; 1.264 + 1.265 + # build is finished, the complete run can resume. 1.266 + # return value only useful if --foreground 1.267 + print "Exiting with status " . ($manager_status || $exit_status) . "\n"; 1.268 + exit($manager_status || $exit_status); 1.269 + } 1.270 + 1.271 + # this is the complete process, wait for the build to finish. 1.272 + waitpid($pid, 0); 1.273 + my $status = $? >> 8; 1.274 + print "build finished (status $status): "; 1.275 + print scalar(localtime()); 1.276 + print "\n"; 1.277 + 1.278 + return $status; 1.279 +} 1.280 + 1.281 +sub run_pass 1.282 +{ 1.283 + my ($name, $command) = @_; 1.284 + my $log_file = "$result_dir/manager.$name.log"; 1.285 + 1.286 + # extra commands to pass to the manager. 1.287 + my $manager_extra = ""; 1.288 + $manager_extra .= "-modset-wait=10" if ($name eq "xmemlocal"); 1.289 + 1.290 + # fork off a manager process for the analysis. 1.291 + defined(my $pid = fork) or die; 1.292 + exec("$xmanager $manager_extra > $log_file 2>&1") if (!$pid); 1.293 + 1.294 + my $address = get_manager_address($log_file); 1.295 + 1.296 + # write the poll file for this pass. 1.297 + if (! -d dirname($poll_file)) { 1.298 + system("mkdir", "-p", dirname($poll_file)); 1.299 + } 1.300 + open(POLL, "> $poll_file"); 1.301 + print POLL "$command\n"; 1.302 + print POLL "$result_dir/$name\n"; 1.303 + print POLL "$address\n"; 1.304 + close(POLL); 1.305 + 1.306 + print "$name started: "; 1.307 + print scalar(localtime()); 1.308 + print "\n"; 1.309 + 1.310 + waitpid($pid, 0); 1.311 + unlink($poll_file); 1.312 + 1.313 + print "$name finished: "; 1.314 + print scalar(localtime()); 1.315 + print "\n"; 1.316 + 1.317 + # collate the worker's output into a single file. make this asynchronous 1.318 + # so we can wait a bit and make sure we get all worker output. 1.319 + defined($pid = fork) or die; 1.320 + 1.321 + if (!$pid) { 1.322 + sleep(20); 1.323 + exec("cat $name.*.log > $name.log"); 1.324 + } 1.325 + 1.326 + push(@waitpids, $pid); 1.327 +} 1.328 + 1.329 +# the names of all directories containing reports to archive. 1.330 +my $indexes; 1.331 + 1.332 +sub run_index 1.333 +{ 1.334 + my ($name, $kind) = @_; 1.335 + 1.336 + return if (not (-e "report_$kind.xdb")); 1.337 + 1.338 + print "$name started: "; 1.339 + print scalar(localtime()); 1.340 + print "\n"; 1.341 + 1.342 + # make an index for the report diff if applicable. 1.343 + if ($old_dir ne "") { 1.344 + system("make_index $kind $old_dir > $name.diff.log"); 1.345 + system("mv $kind diff_$kind"); 1.346 + $indexes .= " diff_$kind"; 1.347 + } 1.348 + 1.349 + # make an index for the full set of reports. 1.350 + system("make_index $kind > $name.log"); 1.351 + $indexes .= " $kind"; 1.352 + 1.353 + print "$name finished: "; 1.354 + print scalar(localtime()); 1.355 + print "\n"; 1.356 +} 1.357 + 1.358 +sub archive_indexes 1.359 +{ 1.360 + print "archive started: "; 1.361 + print scalar(localtime()); 1.362 + print "\n"; 1.363 + 1.364 + system("tar -czf reports.tgz $indexes"); 1.365 + system("rm -rf $indexes"); 1.366 + 1.367 + print "archive finished: "; 1.368 + print scalar(localtime()); 1.369 + print "\n"; 1.370 +}