Home | History | Annotate | Download | only in scan-build
      1 #!/usr/bin/env perl
      2 #
      3 #                     The LLVM Compiler Infrastructure
      4 #
      5 # This file is distributed under the University of Illinois Open Source
      6 # License. See LICENSE.TXT for details.
      7 #
      8 ##===----------------------------------------------------------------------===##
      9 #
     10 # A script designed to wrap a build so that all calls to gcc are intercepted
     11 # and piped to the static analyzer.
     12 #
     13 ##===----------------------------------------------------------------------===##
     14 
     15 use strict;
     16 use warnings;
     17 use FindBin qw($RealBin);
     18 use Digest::MD5;
     19 use File::Basename;
     20 use Term::ANSIColor;
     21 use Term::ANSIColor qw(:constants);
     22 use Cwd qw/ getcwd abs_path /;
     23 use Sys::Hostname;
     24 
     25 my $Verbose = 0;       # Verbose output from this script.
     26 my $Prog = "scan-build";
     27 my $BuildName;
     28 my $BuildDate;
     29 
     30 my $TERM = $ENV{'TERM'};
     31 my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT
     32                 and defined $ENV{'SCAN_BUILD_COLOR'});
     33 
     34 my $UserName = HtmlEscape(getpwuid($<) || 'unknown');
     35 my $HostName = HtmlEscape(hostname() || 'unknown');
     36 my $CurrentDir = HtmlEscape(getcwd());
     37 my $CurrentDirSuffix = basename($CurrentDir);
     38 
     39 my $CmdArgs;
     40 
     41 my $HtmlTitle;
     42 
     43 my $Date = localtime();
     44 
     45 ##----------------------------------------------------------------------------##
     46 # Diagnostics
     47 ##----------------------------------------------------------------------------##
     48 
     49 sub Diag {
     50   if ($UseColor) {
     51     print BOLD, MAGENTA "$Prog: @_";
     52     print RESET;
     53   }
     54   else {
     55     print "$Prog: @_";
     56   }  
     57 }
     58 
     59 sub DiagCrashes {
     60   my $Dir = shift;
     61   Diag ("The analyzer encountered problems on some source files.\n");
     62   Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n");
     63   Diag ("Please consider submitting a bug report using these files:\n");
     64   Diag ("  http://clang-analyzer.llvm.org/filing_bugs.html\n")
     65 }
     66 
     67 sub DieDiag {
     68   if ($UseColor) {
     69     print BOLD, RED "$Prog: ";
     70     print RESET, RED @_;
     71     print RESET;
     72   }
     73   else {
     74     print "$Prog: ", @_;
     75   }
     76   exit(0);
     77 }
     78 
     79 ##----------------------------------------------------------------------------##
     80 # Some initial preprocessing of Clang options.
     81 ##----------------------------------------------------------------------------##
     82 
     83 # Find 'clang'
     84 my $ClangSB = Cwd::realpath("$RealBin/bin/clang");
     85 if (!defined $ClangSB || ! -x $ClangSB) {
     86   $ClangSB = Cwd::realpath("$RealBin/clang");
     87 }
     88 my $Clang;
     89 if (!defined $ClangSB || ! -x $ClangSB) {
     90   # Default to looking for 'clang' in the path.
     91   $Clang = `which clang`;
     92   chomp $Clang;
     93   if ($Clang eq "") {
     94     DieDiag("No 'clang' executable found in path.");
     95   }
     96 }
     97 else {
     98   $Clang = $ClangSB;
     99 }
    100 my $ClangCXX = $Clang . "++";
    101 
    102 ##----------------------------------------------------------------------------##
    103 # GetHTMLRunDir - Construct an HTML directory name for the current sub-run.
    104 ##----------------------------------------------------------------------------##
    105 
    106 sub GetHTMLRunDir {  
    107   die "Not enough arguments." if (@_ == 0);  
    108   my $Dir = shift @_;    
    109   my $TmpMode = 0;
    110   if (!defined $Dir) {
    111     if (`uname` =~ /Darwin/) {
    112       $Dir = $ENV{'TMPDIR'};
    113       if (!defined $Dir) { $Dir = "/tmp"; }
    114     }
    115     else {
    116       $Dir = "/tmp";
    117     }    
    118     $TmpMode = 1;
    119   }
    120   
    121   # Chop off any trailing '/' characters.
    122   while ($Dir =~ /\/$/) { chop $Dir; }
    123 
    124   # Get current date and time.
    125   my @CurrentTime = localtime();  
    126   my $year  = $CurrentTime[5] + 1900;
    127   my $day   = $CurrentTime[3];
    128   my $month = $CurrentTime[4] + 1;
    129   my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day);
    130   
    131   # Determine the run number.  
    132   my $RunNumber;
    133   
    134   if (-d $Dir) {    
    135     if (! -r $Dir) {
    136       DieDiag("directory '$Dir' exists but is not readable.\n");
    137     }    
    138     # Iterate over all files in the specified directory.    
    139     my $max = 0;    
    140     opendir(DIR, $Dir);
    141     my @FILES = grep { -d "$Dir/$_" } readdir(DIR);
    142     closedir(DIR);
    143 
    144     foreach my $f (@FILES) {
    145       # Strip the prefix '$Prog-' if we are dumping files to /tmp.
    146       if ($TmpMode) {
    147         next if (!($f =~ /^$Prog-(.+)/));
    148         $f = $1;
    149       }
    150 
    151       my @x = split/-/, $f;
    152       next if (scalar(@x) != 4);
    153       next if ($x[0] != $year);
    154       next if ($x[1] != $month);
    155       next if ($x[2] != $day);
    156       
    157       if ($x[3] > $max) {
    158         $max = $x[3];
    159       }      
    160     }
    161     
    162     $RunNumber = $max + 1;
    163   }
    164   else {
    165     
    166     if (-x $Dir) {
    167       DieDiag("'$Dir' exists but is not a directory.\n");
    168     }
    169 
    170     if ($TmpMode) {
    171       DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n");
    172     }
    173 
    174     # $Dir does not exist.  It will be automatically created by the 
    175     # clang driver.  Set the run number to 1.  
    176 
    177     $RunNumber = 1;
    178   }
    179   
    180   die "RunNumber must be defined!" if (!defined $RunNumber);
    181   
    182   # Append the run number.
    183   my $NewDir;
    184   if ($TmpMode) {
    185     $NewDir = "$Dir/$Prog-$DateString-$RunNumber";
    186   }
    187   else {
    188     $NewDir = "$Dir/$DateString-$RunNumber";
    189   }
    190   system 'mkdir','-p',$NewDir;
    191   return $NewDir;
    192 }
    193 
    194 sub SetHtmlEnv {
    195   
    196   die "Wrong number of arguments." if (scalar(@_) != 2);
    197   
    198   my $Args = shift;
    199   my $Dir = shift;
    200   
    201   die "No build command." if (scalar(@$Args) == 0);
    202   
    203   my $Cmd = $$Args[0];
    204 
    205   if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) {
    206     return;
    207   }
    208   
    209   if ($Verbose) {
    210     Diag("Emitting reports for this run to '$Dir'.\n");
    211   }
    212   
    213   $ENV{'CCC_ANALYZER_HTML'} = $Dir;
    214 }
    215 
    216 ##----------------------------------------------------------------------------##
    217 # ComputeDigest - Compute a digest of the specified file.
    218 ##----------------------------------------------------------------------------##
    219 
    220 sub ComputeDigest {
    221   my $FName = shift;
    222   DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName);  
    223   
    224   # Use Digest::MD5.  We don't have to be cryptographically secure.  We're
    225   # just looking for duplicate files that come from a non-malicious source.
    226   # We use Digest::MD5 because it is a standard Perl module that should
    227   # come bundled on most systems.  
    228   open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n");
    229   binmode FILE;
    230   my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest;
    231   close(FILE);
    232   
    233   # Return the digest.  
    234   return $Result;
    235 }
    236 
    237 ##----------------------------------------------------------------------------##
    238 #  UpdatePrefix - Compute the common prefix of files.
    239 ##----------------------------------------------------------------------------##
    240 
    241 my $Prefix;
    242 
    243 sub UpdatePrefix {
    244   my $x = shift;
    245   my $y = basename($x);
    246   $x =~ s/\Q$y\E$//;
    247 
    248   if (!defined $Prefix) {
    249     $Prefix = $x;
    250     return;
    251   }
    252   
    253   chop $Prefix while (!($x =~ /^\Q$Prefix/));
    254 }
    255 
    256 sub GetPrefix {
    257   return $Prefix;
    258 }
    259 
    260 ##----------------------------------------------------------------------------##
    261 #  UpdateInFilePath - Update the path in the report file.
    262 ##----------------------------------------------------------------------------##
    263 
    264 sub UpdateInFilePath {
    265   my $fname = shift;
    266   my $regex = shift;
    267   my $newtext = shift;
    268 
    269   open (RIN, $fname) or die "cannot open $fname";
    270   open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp";
    271 
    272   while (<RIN>) {
    273     s/$regex/$newtext/;
    274     print ROUT $_;
    275   }
    276 
    277   close (ROUT);
    278   close (RIN);
    279   system("mv", "$fname.tmp", $fname);
    280 }
    281 
    282 ##----------------------------------------------------------------------------##
    283 # AddStatLine - Decode and insert a statistics line into the database.
    284 ##----------------------------------------------------------------------------##
    285 
    286 sub AddStatLine {
    287   my $Line  = shift;
    288   my $Stats = shift;
    289 
    290   print $Line . "\n";
    291 
    292   my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable
    293       \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList:
    294       \ (yes|no)/x;
    295 
    296   if ($Line !~ $Regex) {
    297     return;
    298   }
    299 
    300   # Create a hash of the interesting fields
    301   my $Row = {
    302     Filename    => $1,
    303     Function    => $2,
    304     Total       => $3,
    305     Unreachable => $4,
    306     Aborted     => $5,
    307     Empty       => $6
    308   };
    309 
    310   # Add them to the stats array
    311   push @$Stats, $Row;
    312 }
    313 
    314 ##----------------------------------------------------------------------------##
    315 # ScanFile - Scan a report file for various identifying attributes.
    316 ##----------------------------------------------------------------------------##
    317 
    318 # Sometimes a source file is scanned more than once, and thus produces
    319 # multiple error reports.  We use a cache to solve this problem.
    320 
    321 my %AlreadyScanned;
    322 
    323 sub ScanFile {
    324   
    325   my $Index = shift;
    326   my $Dir = shift;
    327   my $FName = shift;
    328   my $Stats = shift;
    329   
    330   # Compute a digest for the report file.  Determine if we have already
    331   # scanned a file that looks just like it.
    332   
    333   my $digest = ComputeDigest("$Dir/$FName");
    334 
    335   if (defined $AlreadyScanned{$digest}) {
    336     # Redundant file.  Remove it.
    337     system ("rm", "-f", "$Dir/$FName");
    338     return;
    339   }
    340   
    341   $AlreadyScanned{$digest} = 1;
    342   
    343   # At this point the report file is not world readable.  Make it happen.
    344   system ("chmod", "644", "$Dir/$FName");
    345   
    346   # Scan the report file for tags.
    347   open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n");
    348 
    349   my $BugType        = "";
    350   my $BugFile        = "";
    351   my $BugCategory    = "";
    352   my $BugDescription = "";
    353   my $BugPathLength  = 1;
    354   my $BugLine        = 0;
    355 
    356   while (<IN>) {
    357     last if (/<!-- BUGMETAEND -->/);
    358 
    359     if (/<!-- BUGTYPE (.*) -->$/) {
    360       $BugType = $1;
    361     }
    362     elsif (/<!-- BUGFILE (.*) -->$/) {
    363       $BugFile = abs_path($1);
    364       UpdatePrefix($BugFile);
    365     }
    366     elsif (/<!-- BUGPATHLENGTH (.*) -->$/) {
    367       $BugPathLength = $1;
    368     }
    369     elsif (/<!-- BUGLINE (.*) -->$/) {
    370       $BugLine = $1;    
    371     }
    372     elsif (/<!-- BUGCATEGORY (.*) -->$/) {
    373       $BugCategory = $1;
    374     }
    375     elsif (/<!-- BUGDESC (.*) -->$/) {
    376       $BugDescription = $1;
    377     }
    378   }
    379 
    380   close(IN);
    381   
    382   if (!defined $BugCategory) {
    383     $BugCategory = "Other";
    384   }
    385 
    386   # Don't add internal statistics to the bug reports
    387   if ($BugCategory =~ /statistics/i) {
    388     AddStatLine($BugDescription, $Stats);
    389     return;
    390   }
    391   
    392   push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine,
    393                  $BugPathLength ];
    394 }
    395 
    396 ##----------------------------------------------------------------------------##
    397 # CopyFiles - Copy resource files to target directory.
    398 ##----------------------------------------------------------------------------##
    399 
    400 sub CopyFiles {
    401 
    402   my $Dir = shift;
    403 
    404   my $JS = Cwd::realpath("$RealBin/sorttable.js");
    405   
    406   DieDiag("Cannot find 'sorttable.js'.\n")
    407     if (! -r $JS);  
    408 
    409   system ("cp", $JS, "$Dir");
    410 
    411   DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n")
    412     if (! -r "$Dir/sorttable.js");
    413     
    414   my $CSS = Cwd::realpath("$RealBin/scanview.css");
    415   
    416   DieDiag("Cannot find 'scanview.css'.\n")
    417     if (! -r $CSS);
    418 
    419   system ("cp", $CSS, "$Dir");
    420 
    421   DieDiag("Could not copy 'scanview.css' to '$Dir'.\n")
    422     if (! -r $CSS);
    423 }
    424 
    425 ##----------------------------------------------------------------------------##
    426 # CalcStats - Calculates visitation statistics and returns the string.
    427 ##----------------------------------------------------------------------------##
    428 
    429 sub CalcStats {
    430   my $Stats = shift;
    431 
    432   my $TotalBlocks = 0;
    433   my $UnreachedBlocks = 0;
    434   my $TotalFunctions = scalar(@$Stats);
    435   my $BlockAborted = 0;
    436   my $WorkListAborted = 0;
    437   my $Aborted = 0;
    438 
    439   # Calculate the unique files
    440   my $FilesHash = {};
    441 
    442   foreach my $Row (@$Stats) {
    443     $FilesHash->{$Row->{Filename}} = 1;
    444     $TotalBlocks += $Row->{Total};
    445     $UnreachedBlocks += $Row->{Unreachable};
    446     $BlockAborted++ if $Row->{Aborted} eq 'yes';
    447     $WorkListAborted++ if $Row->{Empty} eq 'no';
    448     $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no';
    449   }
    450 
    451   my $TotalFiles = scalar(keys(%$FilesHash));
    452 
    453   # Calculations
    454   my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100);
    455   my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions
    456       * 100);
    457   my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted /
    458       $TotalFunctions * 100);
    459   my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks
    460       * 100);
    461 
    462   my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions"
    463     . " in $TotalFiles files\n"
    464     . "$Aborted functions aborted early ($PercentAborted%)\n"
    465     . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n"
    466     . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n"
    467     . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n";
    468 
    469   return $StatsString;
    470 }
    471 
    472 ##----------------------------------------------------------------------------##
    473 # Postprocess - Postprocess the results of an analysis scan.
    474 ##----------------------------------------------------------------------------##
    475 
    476 sub Postprocess {
    477   
    478   my $Dir           = shift;
    479   my $BaseDir       = shift;
    480   my $AnalyzerStats = shift;
    481   
    482   die "No directory specified." if (!defined $Dir);
    483   
    484   if (! -d $Dir) {
    485     Diag("No bugs found.\n");
    486     return 0;
    487   }
    488   
    489   opendir(DIR, $Dir);
    490   my @files = grep { /^report-.*\.html$/ } readdir(DIR);
    491   closedir(DIR);
    492 
    493   if (scalar(@files) == 0 and ! -e "$Dir/failures") {
    494     Diag("Removing directory '$Dir' because it contains no reports.\n");
    495     system ("rm", "-fR", $Dir);
    496     return 0;
    497   }
    498   
    499   # Scan each report file and build an index.  
    500   my @Index;
    501   my @Stats;
    502   foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); }
    503   
    504   # Scan the failures directory and use the information in the .info files
    505   # to update the common prefix directory.
    506   my @failures;
    507   my @attributes_ignored;
    508   if (-d "$Dir/failures") {
    509     opendir(DIR, "$Dir/failures");
    510     @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR);
    511     closedir(DIR);
    512     opendir(DIR, "$Dir/failures");        
    513     @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR);
    514     closedir(DIR);
    515     foreach my $file (@failures) {
    516       open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n");
    517       my $Path = <IN>;
    518       if (defined $Path) { UpdatePrefix($Path); }
    519       close IN;
    520     }    
    521   }
    522   
    523   # Generate an index.html file.  
    524   my $FName = "$Dir/index.html";  
    525   open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n");
    526   
    527   # Print out the header.
    528   
    529 print OUT <<ENDTEXT;
    530 <html>
    531 <head>
    532 <title>${HtmlTitle}</title>
    533 <link type="text/css" rel="stylesheet" href="scanview.css"/>
    534 <script src="sorttable.js"></script>
    535 <script language='javascript' type="text/javascript">
    536 function SetDisplay(RowClass, DisplayVal)
    537 {
    538   var Rows = document.getElementsByTagName("tr");
    539   for ( var i = 0 ; i < Rows.length; ++i ) {
    540     if (Rows[i].className == RowClass) {
    541       Rows[i].style.display = DisplayVal;
    542     }
    543   }
    544 }
    545 
    546 function CopyCheckedStateToCheckButtons(SummaryCheckButton) {
    547   var Inputs = document.getElementsByTagName("input");
    548   for ( var i = 0 ; i < Inputs.length; ++i ) {
    549     if (Inputs[i].type == "checkbox") {
    550       if(Inputs[i] != SummaryCheckButton) {
    551         Inputs[i].checked = SummaryCheckButton.checked;
    552         Inputs[i].onclick();
    553 	  }
    554     }
    555   }
    556 }
    557 
    558 function returnObjById( id ) {
    559     if (document.getElementById) 
    560         var returnVar = document.getElementById(id);
    561     else if (document.all)
    562         var returnVar = document.all[id];
    563     else if (document.layers) 
    564         var returnVar = document.layers[id];
    565     return returnVar; 
    566 }
    567 
    568 var NumUnchecked = 0;
    569 
    570 function ToggleDisplay(CheckButton, ClassName) {
    571   if (CheckButton.checked) {
    572     SetDisplay(ClassName, "");
    573     if (--NumUnchecked == 0) {
    574       returnObjById("AllBugsCheck").checked = true;
    575     }
    576   }
    577   else {
    578     SetDisplay(ClassName, "none");
    579     NumUnchecked++;
    580     returnObjById("AllBugsCheck").checked = false;
    581   }
    582 }
    583 </script>
    584 <!-- SUMMARYENDHEAD -->
    585 </head>
    586 <body>
    587 <h1>${HtmlTitle}</h1>
    588 
    589 <table>
    590 <tr><th>User:</th><td>${UserName}\@${HostName}</td></tr>
    591 <tr><th>Working Directory:</th><td>${CurrentDir}</td></tr>
    592 <tr><th>Command Line:</th><td>${CmdArgs}</td></tr>
    593 <tr><th>Date:</th><td>${Date}</td></tr>
    594 ENDTEXT
    595 
    596 print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n"
    597   if (defined($BuildName) && defined($BuildDate));
    598 
    599 print OUT <<ENDTEXT;
    600 </table>
    601 ENDTEXT
    602 
    603   if (scalar(@files)) {
    604     # Print out the summary table.
    605     my %Totals;
    606 
    607     for my $row ( @Index ) {
    608       my $bug_type = ($row->[2]);
    609       my $bug_category = ($row->[1]);
    610       my $key = "$bug_category:$bug_type";
    611 
    612       if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; }
    613       else { $Totals{$key}->[0]++; }
    614     }
    615 
    616     print OUT "<h2>Bug Summary</h2>";
    617 
    618     if (defined $BuildName) {
    619       print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
    620     }
    621   
    622   my $TotalBugs = scalar(@Index);
    623 print OUT <<ENDTEXT;
    624 <table>
    625 <thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead>
    626 <tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr>
    627 ENDTEXT
    628   
    629     my $last_category;
    630 
    631     for my $key (
    632       sort {
    633         my $x = $Totals{$a};
    634         my $y = $Totals{$b};
    635         my $res = $x->[1] cmp $y->[1];
    636         $res = $x->[2] cmp $y->[2] if ($res == 0);
    637         $res
    638       } keys %Totals ) 
    639     {
    640       my $val = $Totals{$key};
    641       my $category = $val->[1];
    642       if (!defined $last_category or $last_category ne $category) {
    643         $last_category = $category;
    644         print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n";
    645       }      
    646       my $x = lc $key;
    647       $x =~ s/[ ,'":\/()]+/_/g;
    648       print OUT "<tr><td class=\"SUMM_DESC\">";
    649       print OUT $val->[2];
    650       print OUT "</td><td class=\"Q\">";
    651       print OUT $val->[0];
    652       print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n";
    653     }
    654 
    655   # Print out the table of errors.
    656 
    657 print OUT <<ENDTEXT;
    658 </table>
    659 <h2>Reports</h2>
    660 
    661 <table class="sortable" style="table-layout:automatic">
    662 <thead><tr>
    663   <td>Bug Group</td>
    664   <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind">&nbsp;&#x25BE;</span></td>
    665   <td>File</td>
    666   <td class="Q">Line</td>
    667   <td class="Q">Path Length</td>
    668   <td class="sorttable_nosort"></td>
    669   <!-- REPORTBUGCOL -->
    670 </tr></thead>
    671 <tbody>
    672 ENDTEXT
    673 
    674     my $prefix = GetPrefix();
    675     my $regex;
    676     my $InFileRegex;
    677     my $InFilePrefix = "File:</td><td>";
    678   
    679     if (defined $prefix) { 
    680       $regex = qr/^\Q$prefix\E/is;    
    681       $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
    682     }    
    683 
    684     for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) {
    685       my $x = "$row->[1]:$row->[2]";
    686       $x = lc $x;
    687       $x =~ s/[ ,'":\/()]+/_/g;
    688     
    689       my $ReportFile = $row->[0];
    690           
    691       print OUT "<tr class=\"bt_$x\">";
    692       print OUT "<td class=\"DESC\">";
    693       print OUT $row->[1];
    694       print OUT "</td>";
    695       print OUT "<td class=\"DESC\">";
    696       print OUT $row->[2];
    697       print OUT "</td>";
    698       
    699       # Update the file prefix.      
    700       my $fname = $row->[3];
    701 
    702       if (defined $regex) {
    703         $fname =~ s/$regex//;
    704         UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
    705       }
    706       
    707       print OUT "<td>";      
    708       my @fname = split /\//,$fname;
    709       if ($#fname > 0) {
    710         while ($#fname >= 0) {
    711           my $x = shift @fname;
    712           print OUT $x;
    713           if ($#fname >= 0) {
    714             print OUT "<span class=\"W\"> </span>/";
    715           }
    716         }
    717       }
    718       else {
    719         print OUT $fname;
    720       }      
    721       print OUT "</td>";
    722       
    723       # Print out the quantities.
    724       for my $j ( 4 .. 5 ) {
    725         print OUT "<td class=\"Q\">$row->[$j]</td>";        
    726       }
    727       
    728       # Print the rest of the columns.
    729       for (my $j = 6; $j <= $#{$row}; ++$j) {
    730         print OUT "<td>$row->[$j]</td>"
    731       }
    732 
    733       # Emit the "View" link.
    734       print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>";
    735         
    736       # Emit REPORTBUG markers.
    737       print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n";
    738         
    739       # End the row.
    740       print OUT "</tr>\n";
    741     }
    742   
    743     print OUT "</tbody>\n</table>\n\n";
    744   }
    745 
    746   if (scalar (@failures) || scalar(@attributes_ignored)) {
    747     print OUT "<h2>Analyzer Failures</h2>\n";
    748     
    749     if (scalar @attributes_ignored) {
    750       print OUT "The analyzer's parser ignored the following attributes:<p>\n";
    751       print OUT "<table>\n";
    752       print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
    753       foreach my $file (sort @attributes_ignored) {
    754         die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/));
    755         my $attribute = $1;
    756         # Open the attribute file to get the first file that failed.
    757         next if (!open (ATTR, "$Dir/failures/$file"));
    758         my $ppfile = <ATTR>;
    759         chomp $ppfile;
    760         close ATTR;
    761         next if (! -e "$Dir/failures/$ppfile");
    762         # Open the info file and get the name of the source file.
    763         open (INFO, "$Dir/failures/$ppfile.info.txt") or
    764           die "Cannot open $Dir/failures/$ppfile.info.txt\n";
    765         my $srcfile = <INFO>;
    766         chomp $srcfile;
    767         close (INFO);
    768         # Print the information in the table.
    769         my $prefix = GetPrefix();
    770         if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
    771         print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
    772         my $ppfile_clang = $ppfile;
    773         $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
    774         print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
    775       }
    776       print OUT "</table>\n";
    777     }
    778     
    779     if (scalar @failures) {
    780       print OUT "<p>The analyzer had problems processing the following files:</p>\n";
    781       print OUT "<table>\n";
    782       print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
    783       foreach my $file (sort @failures) {
    784         $file =~ /(.+).info.txt$/;
    785         # Get the preprocessed file.
    786         my $ppfile = $1;
    787         # Open the info file and get the name of the source file.
    788         open (INFO, "$Dir/failures/$file") or
    789           die "Cannot open $Dir/failures/$file\n";
    790         my $srcfile = <INFO>;
    791         chomp $srcfile;
    792         my $problem = <INFO>;
    793         chomp $problem;
    794         close (INFO);
    795         # Print the information in the table.
    796         my $prefix = GetPrefix();
    797         if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
    798         print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
    799         my $ppfile_clang = $ppfile;
    800         $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
    801         print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
    802       }
    803       print OUT "</table>\n";
    804     }    
    805     print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n";
    806   }
    807   
    808   print OUT "</body></html>\n";  
    809   close(OUT);
    810   CopyFiles($Dir);
    811 
    812   # Make sure $Dir and $BaseDir are world readable/executable.
    813   system("chmod", "755", $Dir);
    814   if (defined $BaseDir) { system("chmod", "755", $BaseDir); }
    815 
    816   # Print statistics
    817   print CalcStats(\@Stats) if $AnalyzerStats;
    818 
    819   my $Num = scalar(@Index);
    820   Diag("$Num bugs found.\n");
    821   if ($Num > 0 && -r "$Dir/index.html") {
    822     Diag("Run 'scan-view $Dir' to examine bug reports.\n");
    823   }
    824   
    825   DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored);
    826   
    827   return $Num;
    828 }
    829 
    830 ##----------------------------------------------------------------------------##
    831 # RunBuildCommand - Run the build command.
    832 ##----------------------------------------------------------------------------##
    833 
    834 sub AddIfNotPresent {
    835   my $Args = shift;
    836   my $Arg = shift;  
    837   my $found = 0;
    838   
    839   foreach my $k (@$Args) {
    840     if ($k eq $Arg) {
    841       $found = 1;
    842       last;
    843     }
    844   }
    845   
    846   if ($found == 0) {
    847     push @$Args, $Arg;
    848   }
    849 }
    850 
    851 sub RunBuildCommand {
    852   
    853   my $Args = shift;
    854   my $IgnoreErrors = shift;
    855   my $Cmd = $Args->[0];
    856   my $CCAnalyzer = shift;
    857   my $CXXAnalyzer = shift;
    858   
    859   # Get only the part of the command after the last '/'.
    860   if ($Cmd =~ /\/([^\/]+)$/) {
    861     $Cmd = $1;
    862   }
    863   
    864   if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or 
    865       $Cmd =~ /(.*\/?cc[^\/]*$)/ or
    866       $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or
    867       $Cmd =~ /(.*\/?clang$)/ or 
    868       $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) {
    869 
    870     if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) {
    871       $ENV{"CCC_CC"} = $1;      
    872     }
    873         
    874     shift @$Args;
    875     unshift @$Args, $CCAnalyzer;
    876   }
    877   elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or 
    878         $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or
    879         $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or
    880         $Cmd =~ /(.*\/?clang\+\+$)/ or
    881         $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) {
    882     if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) {
    883       $ENV{"CCC_CXX"} = $1;      
    884     }        
    885     shift @$Args;
    886     unshift @$Args, $CXXAnalyzer;
    887   }
    888   elsif ($IgnoreErrors) {
    889     if ($Cmd eq "make" or $Cmd eq "gmake") {
    890       AddIfNotPresent($Args, "CC=$CCAnalyzer");
    891       AddIfNotPresent($Args, "CXX=$CXXAnalyzer");
    892       AddIfNotPresent($Args,"-k");
    893       AddIfNotPresent($Args,"-i");
    894     }
    895     elsif ($Cmd eq "xcodebuild") {
    896       AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
    897     }
    898   } 
    899   
    900   if ($Cmd eq "xcodebuild") {
    901     # Check if using iPhone SDK 3.0 (simulator).  If so the compiler being
    902     # used should be gcc-4.2.
    903     if (!defined $ENV{"CCC_CC"}) {
    904       for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
    905         if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
    906           if (@$Args[$i+1] =~ /^iphonesimulator3/) {
    907             $ENV{"CCC_CC"} = "gcc-4.2";
    908             $ENV{"CCC_CXX"} = "g++-4.2";            
    909           }
    910         }
    911       }
    912     }
    913 
    914     # Disable PCH files until clang supports them.
    915     AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
    916     
    917     # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
    918     # linking C++ object files.  Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
    919     # (via c++-analyzer) when linking such files.
    920     $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
    921   }
    922   
    923   return (system(@$Args) >> 8);
    924 }
    925 
    926 ##----------------------------------------------------------------------------##
    927 # DisplayHelp - Utility function to display all help options.
    928 ##----------------------------------------------------------------------------##
    929 
    930 sub DisplayHelp {
    931   
    932 print <<ENDTEXT;
    933 USAGE: $Prog [options] <build command> [build options]
    934 
    935 ENDTEXT
    936 
    937   if (defined $BuildName) {
    938     print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n";
    939   }
    940 
    941 print <<ENDTEXT;
    942 OPTIONS:
    943 
    944  -analyze-headers - Also analyze functions in #included files.
    945  
    946  -o             - Target directory for HTML report files.  Subdirectories
    947                   will be created as needed to represent separate "runs" of
    948                   the analyzer.  If this option is not specified, a directory
    949                   is created in /tmp (TMPDIR on Mac OS X) to store the reports.
    950                   
    951  -h             - Display this message.
    952  --help
    953 
    954  -k             - Add a "keep on going" option to the specified build command.
    955  --keep-going     This option currently supports make and xcodebuild.
    956                   This is a convenience option; one can specify this
    957                   behavior directly using build options.
    958 
    959  --html-title [title]       - Specify the title used on generated HTML pages.
    960  --html-title=[title]         If not specified, a default title will be used.
    961 
    962  -plist         - By default the output of scan-build is a set of HTML files.
    963                   This option outputs the results as a set of .plist files.
    964 
    965  --status-bugs  - By default, the exit status of $Prog is the same as the
    966                   executed build command.  Specifying this option causes the
    967                   exit status of $Prog to be 1 if it found potential bugs
    968                   and 0 otherwise.
    969 
    970  --use-cc [compiler path]   - By default, $Prog uses 'gcc' to compile and link
    971  --use-cc=[compiler path]     your C and Objective-C code. Use this option
    972                               to specify an alternate compiler.
    973 
    974  --use-c++ [compiler path]  - By default, $Prog uses 'g++' to compile and link
    975  --use-c++=[compiler path]    your C++ and Objective-C++ code. Use this option
    976                               to specify an alternate compiler.
    977 
    978  -v             - Verbose output from $Prog and the analyzer.
    979                   A second and third '-v' increases verbosity.
    980 
    981  -V             - View analysis results in a web browser when the build
    982  --view           completes.
    983 
    984 ADVANCED OPTIONS:
    985 
    986  -constraints [model] - Specify the contraint engine used by the analyzer.
    987                         By default the 'range' model is used.  Specifying 
    988                         'basic' uses a simpler, less powerful constraint model
    989                         used by checker-0.160 and earlier.
    990 
    991  -store [model] - Specify the store model used by the analyzer. By default,
    992                   the 'region' store model is used. 'region' specifies a field-
    993                   sensitive store model. Users can also specify 'basic', which
    994                   is far less precise but can more quickly analyze code.
    995                   'basic' was the default store model for checker-0.221 and
    996                   earlier.
    997 
    998  -no-failure-reports - Do not create a 'failures' subdirectory that includes
    999                        analyzer crash reports and preprocessed source files.
   1000 
   1001  -stats - Generates visitation statistics for the project being analyzed.
   1002 
   1003  -maxloop N - specifiy the number of times a block can be visited before giving
   1004               up. Default is 4. Increase for more comprehensive coverage at a
   1005               cost of speed.
   1006 
   1007 CONTROLLING CHECKERS:
   1008 
   1009  A default group of checkers are always run unless explicitly disabled.
   1010  Checkers may be enabled/disabled using the following options:
   1011 
   1012  -enable-checker [checker name]
   1013  -disable-checker [checker name]
   1014 ENDTEXT
   1015 
   1016 # Query clang for list of checkers that are enabled.
   1017 my %EnabledCheckers;
   1018 foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
   1019   pipe(FROM_CHILD, TO_PARENT);
   1020   my $pid = fork();
   1021   if ($pid == 0) {
   1022     close FROM_CHILD;
   1023     open(STDOUT,">&", \*TO_PARENT);
   1024     open(STDERR,">&", \*TO_PARENT);
   1025     exec $Clang, ('--analyze', '-x', $lang, '-', '-###');
   1026   }
   1027   close(TO_PARENT);
   1028   while(<FROM_CHILD>) {
   1029     foreach my $val (split /\s+/) {
   1030       $val =~ s/\"//g;
   1031       if ($val =~ /-analyzer-checker\=([^\s]+)/) {
   1032         $EnabledCheckers{$1} = 1;
   1033       }
   1034     }
   1035   }
   1036   waitpid($pid,0);
   1037   close(FROM_CHILD);
   1038 }
   1039 
   1040 # Query clang for complete list of checkers.
   1041 pipe(FROM_CHILD, TO_PARENT);
   1042 my $pid = fork();
   1043 if ($pid == 0) {
   1044   close FROM_CHILD;
   1045   open(STDOUT,">&", \*TO_PARENT);
   1046   open(STDERR,">&", \*TO_PARENT);
   1047   exec $Clang, ('-cc1', '-analyzer-checker-help');
   1048 }
   1049 close(TO_PARENT);
   1050 my $foundCheckers = 0;
   1051 while(<FROM_CHILD>) {
   1052   if (/CHECKERS:/) {
   1053     $foundCheckers = 1;
   1054     last;
   1055   }
   1056 }
   1057 if (!$foundCheckers) {
   1058   print "  *** Could not query Clang for the list of available checkers.";
   1059 }
   1060 else {
   1061   print("\nAVAILABLE CHECKERS:\n\n");
   1062   my $skip = 0;
   1063   while(<FROM_CHILD>) {
   1064     if (/experimental/) {
   1065       $skip = 1;
   1066       next;
   1067     }
   1068     if ($skip) {
   1069       next if (!/^\s\s[^\s]/);
   1070       $skip = 0;
   1071     }
   1072     s/^\s\s//;
   1073     if (/^([^\s]+)/) {
   1074       # Is the checker enabled?
   1075       my $checker = $1;
   1076       my $enabled = 0;
   1077       my $aggregate = "";
   1078       foreach my $domain (split /\./, $checker) {
   1079         $aggregate .= $domain;
   1080         if ($EnabledCheckers{$aggregate}) {
   1081           $enabled =1;
   1082           last;
   1083         }        
   1084       }
   1085       
   1086       if ($enabled) {
   1087         print " + ";
   1088       }
   1089       else {
   1090         print "   ";
   1091       }
   1092     }
   1093     else {
   1094       print "   ";
   1095     }
   1096     print $_;
   1097   } 
   1098 }
   1099 waitpid($pid,0);
   1100 close(FROM_CHILD);
   1101 
   1102 print <<ENDTEXT
   1103 
   1104  NOTE: "+" indicates that an analysis is enabled by default.
   1105 
   1106 BUILD OPTIONS
   1107 
   1108  You can specify any build option acceptable to the build command.
   1109 
   1110 EXAMPLE
   1111 
   1112  $Prog -o /tmp/myhtmldir make -j4
   1113      
   1114  The above example causes analysis reports to be deposited into
   1115  a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option.
   1116  A different subdirectory is created each time $Prog analyzes a project.
   1117  The analyzer should support most parallel builds, but not distributed builds.
   1118 
   1119 ENDTEXT
   1120 }
   1121 
   1122 ##----------------------------------------------------------------------------##
   1123 # HtmlEscape - HTML entity encode characters that are special in HTML
   1124 ##----------------------------------------------------------------------------##
   1125 
   1126 sub HtmlEscape {
   1127   # copy argument to new variable so we don't clobber the original
   1128   my $arg = shift || '';
   1129   my $tmp = $arg;
   1130   $tmp =~ s/&/&amp;/g;
   1131   $tmp =~ s/</&lt;/g;
   1132   $tmp =~ s/>/&gt;/g;
   1133   return $tmp;
   1134 }
   1135 
   1136 ##----------------------------------------------------------------------------##
   1137 # ShellEscape - backslash escape characters that are special to the shell
   1138 ##----------------------------------------------------------------------------##
   1139 
   1140 sub ShellEscape {
   1141   # copy argument to new variable so we don't clobber the original
   1142   my $arg = shift || '';
   1143   if ($arg =~ /["\s]/) { return "'" . $arg . "'"; }
   1144   return $arg;
   1145 }
   1146 
   1147 ##----------------------------------------------------------------------------##
   1148 # Process command-line arguments.
   1149 ##----------------------------------------------------------------------------##
   1150 
   1151 my $AnalyzeHeaders = 0;
   1152 my $HtmlDir;           # Parent directory to store HTML files.
   1153 my $IgnoreErrors = 0;  # Ignore build errors.
   1154 my $ViewResults  = 0;  # View results when the build terminates.
   1155 my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found
   1156 my @AnalysesToRun;
   1157 my $StoreModel;
   1158 my $ConstraintsModel;
   1159 my $OutputFormat = "html";
   1160 my $AnalyzerStats = 0;
   1161 my $MaxLoop = 0;
   1162 
   1163 if (!@ARGV) {
   1164   DisplayHelp();
   1165   exit 1;
   1166 }
   1167 
   1168 
   1169 my $displayHelp = 0;
   1170 
   1171 while (@ARGV) {
   1172   
   1173   # Scan for options we recognize.
   1174   
   1175   my $arg = $ARGV[0];
   1176 
   1177   if ($arg eq "-h" or $arg eq "--help") {
   1178     $displayHelp = 1;
   1179     shift @ARGV;
   1180     next;
   1181   }
   1182   
   1183   if ($arg eq '-analyze-headers') {
   1184     shift @ARGV;    
   1185     $AnalyzeHeaders = 1;
   1186     next;
   1187   }
   1188   
   1189   if ($arg eq "-o") {
   1190     shift @ARGV;
   1191         
   1192     if (!@ARGV) {
   1193       DieDiag("'-o' option requires a target directory name.\n");
   1194     }
   1195     
   1196     # Construct an absolute path.  Uses the current working directory
   1197     # as a base if the original path was not absolute.
   1198     $HtmlDir = abs_path(shift @ARGV);
   1199     
   1200     next;
   1201   }
   1202 
   1203   if ($arg =~ /^--html-title(=(.+))?$/) {
   1204     shift @ARGV;
   1205 
   1206     if (!defined $2 || $2 eq '') {
   1207       if (!@ARGV) {
   1208         DieDiag("'--html-title' option requires a string.\n");
   1209       }
   1210 
   1211       $HtmlTitle = shift @ARGV;
   1212     } else {
   1213       $HtmlTitle = $2;
   1214     }
   1215 
   1216     next;
   1217   }
   1218   
   1219   if ($arg eq "-k" or $arg eq "--keep-going") {
   1220     shift @ARGV;
   1221     $IgnoreErrors = 1;
   1222     next;
   1223   }
   1224 
   1225   if ($arg =~ /^--use-cc(=(.+))?$/) {
   1226     shift @ARGV;
   1227     my $cc;
   1228     
   1229     if (!defined $2 || $2 eq "") {
   1230       if (!@ARGV) {
   1231         DieDiag("'--use-cc' option requires a compiler executable name.\n");
   1232       }
   1233       $cc = shift @ARGV;
   1234     }
   1235     else {
   1236       $cc = $2;
   1237     }
   1238     
   1239     $ENV{"CCC_CC"} = $cc;
   1240     next;
   1241   }
   1242   
   1243   if ($arg =~ /^--use-c\+\+(=(.+))?$/) {
   1244     shift @ARGV;
   1245     my $cxx;    
   1246     
   1247     if (!defined $2 || $2 eq "") {
   1248       if (!@ARGV) {
   1249         DieDiag("'--use-c++' option requires a compiler executable name.\n");
   1250       }
   1251       $cxx = shift @ARGV;
   1252     }
   1253     else {
   1254       $cxx = $2;
   1255     }
   1256     
   1257     $ENV{"CCC_CXX"} = $cxx;
   1258     next;
   1259   }
   1260   
   1261   if ($arg eq "-v") {
   1262     shift @ARGV;
   1263     $Verbose++;
   1264     next;
   1265   }
   1266   
   1267   if ($arg eq "-V" or $arg eq "--view") {
   1268     shift @ARGV;
   1269     $ViewResults = 1;    
   1270     next;
   1271   }
   1272   
   1273   if ($arg eq "--status-bugs") {
   1274     shift @ARGV;
   1275     $ExitStatusFoundBugs = 1;
   1276     next;
   1277   }
   1278 
   1279   if ($arg eq "-store") {
   1280     shift @ARGV;
   1281     $StoreModel = shift @ARGV;
   1282     next;
   1283   }
   1284   
   1285   if ($arg eq "-constraints") {
   1286     shift @ARGV;
   1287     $ConstraintsModel = shift @ARGV;
   1288     next;
   1289   }
   1290   
   1291   if ($arg eq "-plist") {
   1292     shift @ARGV;
   1293     $OutputFormat = "plist";
   1294     next;
   1295   }
   1296   if ($arg eq "-plist-html") {
   1297     shift @ARGV;
   1298     $OutputFormat = "plist-html";
   1299     next;
   1300   }
   1301   
   1302   if ($arg eq "-no-failure-reports") {
   1303     $ENV{"CCC_REPORT_FAILURES"} = 0;
   1304     next;
   1305   }
   1306   if ($arg eq "-stats") {
   1307     shift @ARGV;
   1308     $AnalyzerStats = 1;
   1309     next;
   1310   }
   1311   if ($arg eq "-maxloop") {
   1312     shift @ARGV;
   1313     $MaxLoop = shift @ARGV;
   1314     next;
   1315   }
   1316   if ($arg eq "-enable-checker") {
   1317     shift @ARGV;
   1318     push @AnalysesToRun, "-analyzer-checker", shift @ARGV;
   1319     next;
   1320   }
   1321   if ($arg eq "-disable-checker") {
   1322     shift @ARGV;
   1323     push @AnalysesToRun, "-analyzer-disable-checker", shift @ARGV;
   1324     next;
   1325   }
   1326 
   1327   DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
   1328   
   1329   last;
   1330 }
   1331 
   1332 if (!@ARGV and $displayHelp == 0) {
   1333   Diag("No build command specified.\n\n");
   1334   $displayHelp = 1;
   1335 }
   1336 
   1337 if ($displayHelp) {
   1338   DisplayHelp();
   1339   exit 1;
   1340 }
   1341 
   1342 # Determine where results go.
   1343 $CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV)));
   1344 $HtmlTitle = "${CurrentDirSuffix} - scan-build results"
   1345   unless (defined($HtmlTitle));
   1346 
   1347 # Determine the output directory for the HTML reports.
   1348 my $BaseDir = $HtmlDir;
   1349 $HtmlDir = GetHTMLRunDir($HtmlDir);
   1350 
   1351 # Determine the location of ccc-analyzer.
   1352 my $AbsRealBin = Cwd::realpath($RealBin);
   1353 my $Cmd = "$AbsRealBin/libexec/ccc-analyzer";
   1354 my $CmdCXX = "$AbsRealBin/libexec/c++-analyzer";
   1355 
   1356 if (!defined $Cmd || ! -x $Cmd) {
   1357   $Cmd = "$AbsRealBin/ccc-analyzer";
   1358   DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd);
   1359 }
   1360 if (!defined $CmdCXX || ! -x $CmdCXX) {
   1361   $CmdCXX = "$AbsRealBin/c++-analyzer";
   1362   DieDiag("Executable 'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -x $CmdCXX);
   1363 }
   1364 
   1365 if (!defined $ClangSB || ! -x $ClangSB) {
   1366   Diag("'clang' executable not found in '$RealBin/bin'.\n");
   1367   Diag("Using 'clang' from path: $Clang\n");
   1368 }
   1369 
   1370 # Set the appropriate environment variables.
   1371 SetHtmlEnv(\@ARGV, $HtmlDir);
   1372 $ENV{'CC'} = $Cmd;
   1373 $ENV{'CXX'} = $CmdCXX;
   1374 $ENV{'CLANG'} = $Clang;
   1375 $ENV{'CLANG_CXX'} = $ClangCXX;
   1376 if ($Verbose >= 2) {
   1377   $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
   1378 }
   1379 if ($Verbose >= 3) {
   1380   $ENV{'CCC_ANALYZER_LOG'} = 1;
   1381 }
   1382 if ($AnalyzeHeaders) {
   1383   push @AnalysesToRun,"-analyzer-opt-analyze-headers";  
   1384 }
   1385 if ($AnalyzerStats) {
   1386   push @AnalysesToRun, '-analyzer-checker', 'debug.Stats';
   1387 }
   1388 if ($MaxLoop > 0) {
   1389   push @AnalysesToRun, '-analyzer-max-loop ' . $MaxLoop;
   1390 }
   1391 
   1392 $ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun;
   1393 
   1394 if (defined $StoreModel) {
   1395   $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel;
   1396 }
   1397 if (defined $ConstraintsModel) {
   1398   $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel;
   1399 }
   1400 if (defined $OutputFormat) {
   1401   $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat;
   1402 }
   1403 
   1404 # Run the build.
   1405 my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX);
   1406 
   1407 if (defined $OutputFormat) {
   1408   if ($OutputFormat =~ /plist/) {
   1409     Diag "Analysis run complete.\n";
   1410     Diag "Analysis results (plist files) deposited in '$HtmlDir'\n";
   1411   }
   1412   elsif ($OutputFormat =~ /html/) {
   1413     # Postprocess the HTML directory.
   1414     my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats);
   1415 
   1416     if ($ViewResults and -r "$HtmlDir/index.html") {
   1417       Diag "Analysis run complete.\n";
   1418       Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n";
   1419       my $ScanView = Cwd::realpath("$RealBin/scan-view");
   1420       if (! -x $ScanView) { $ScanView = "scan-view"; }
   1421       exec $ScanView, "$HtmlDir";
   1422     }
   1423 
   1424     if ($ExitStatusFoundBugs) {
   1425       exit 1 if ($NumBugs > 0);
   1426       exit 0;
   1427     }
   1428   }
   1429 }
   1430 
   1431 exit $ExitStatus;
   1432 
   1433