1*67e74705SXin Li#!/usr/bin/env perl 2*67e74705SXin Li# 3*67e74705SXin Li# The LLVM Compiler Infrastructure 4*67e74705SXin Li# 5*67e74705SXin Li# This file is distributed under the University of Illinois Open Source 6*67e74705SXin Li# License. See LICENSE.TXT for details. 7*67e74705SXin Li# 8*67e74705SXin Li##===----------------------------------------------------------------------===## 9*67e74705SXin Li# 10*67e74705SXin Li# A script designed to wrap a build so that all calls to gcc are intercepted 11*67e74705SXin Li# and piped to the static analyzer. 12*67e74705SXin Li# 13*67e74705SXin Li##===----------------------------------------------------------------------===## 14*67e74705SXin Li 15*67e74705SXin Liuse strict; 16*67e74705SXin Liuse warnings; 17*67e74705SXin Liuse FindBin qw($RealBin); 18*67e74705SXin Liuse Digest::MD5; 19*67e74705SXin Liuse File::Basename; 20*67e74705SXin Liuse File::Find; 21*67e74705SXin Liuse File::Copy qw(copy); 22*67e74705SXin Liuse File::Path qw( rmtree mkpath ); 23*67e74705SXin Liuse Term::ANSIColor; 24*67e74705SXin Liuse Term::ANSIColor qw(:constants); 25*67e74705SXin Liuse Cwd qw/ getcwd abs_path /; 26*67e74705SXin Liuse Sys::Hostname; 27*67e74705SXin Liuse Hash::Util qw(lock_keys); 28*67e74705SXin Li 29*67e74705SXin Limy $Prog = "scan-build"; 30*67e74705SXin Limy $BuildName; 31*67e74705SXin Limy $BuildDate; 32*67e74705SXin Li 33*67e74705SXin Limy $TERM = $ENV{'TERM'}; 34*67e74705SXin Limy $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT 35*67e74705SXin Li and defined $ENV{'SCAN_BUILD_COLOR'}); 36*67e74705SXin Li 37*67e74705SXin Li# Portability: getpwuid is not implemented for Win32 (see Perl language 38*67e74705SXin Li# reference, perlport), use getlogin instead. 39*67e74705SXin Limy $UserName = HtmlEscape(getlogin() || getpwuid($<) || 'unknown'); 40*67e74705SXin Limy $HostName = HtmlEscape(hostname() || 'unknown'); 41*67e74705SXin Limy $CurrentDir = HtmlEscape(getcwd()); 42*67e74705SXin Li 43*67e74705SXin Limy $CmdArgs; 44*67e74705SXin Li 45*67e74705SXin Limy $Date = localtime(); 46*67e74705SXin Li 47*67e74705SXin Li# Command-line/config arguments. 48*67e74705SXin Limy %Options = ( 49*67e74705SXin Li Verbose => 0, # Verbose output from this script. 50*67e74705SXin Li AnalyzeHeaders => 0, 51*67e74705SXin Li OutputDir => undef, # Parent directory to store HTML files. 52*67e74705SXin Li HtmlTitle => basename($CurrentDir)." - scan-build results", 53*67e74705SXin Li IgnoreErrors => 0, # Ignore build errors. 54*67e74705SXin Li ViewResults => 0, # View results when the build terminates. 55*67e74705SXin Li ExitStatusFoundBugs => 0, # Exit status reflects whether bugs were found 56*67e74705SXin Li KeepEmpty => 0, # Don't remove output directory even with 0 results. 57*67e74705SXin Li EnableCheckers => {}, 58*67e74705SXin Li DisableCheckers => {}, 59*67e74705SXin Li UseCC => undef, # C compiler to use for compilation. 60*67e74705SXin Li UseCXX => undef, # C++ compiler to use for compilation. 61*67e74705SXin Li AnalyzerTarget => undef, 62*67e74705SXin Li StoreModel => undef, 63*67e74705SXin Li ConstraintsModel => undef, 64*67e74705SXin Li InternalStats => undef, 65*67e74705SXin Li OutputFormat => "html", 66*67e74705SXin Li ConfigOptions => [], # Options to pass through to the analyzer's -analyzer-config flag. 67*67e74705SXin Li ReportFailures => undef, 68*67e74705SXin Li AnalyzerStats => 0, 69*67e74705SXin Li MaxLoop => 0, 70*67e74705SXin Li PluginsToLoad => [], 71*67e74705SXin Li AnalyzerDiscoveryMethod => undef, 72*67e74705SXin Li OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. 73*67e74705SXin Li ForceAnalyzeDebugCode => 0 74*67e74705SXin Li); 75*67e74705SXin Lilock_keys(%Options); 76*67e74705SXin Li 77*67e74705SXin Li##----------------------------------------------------------------------------## 78*67e74705SXin Li# Diagnostics 79*67e74705SXin Li##----------------------------------------------------------------------------## 80*67e74705SXin Li 81*67e74705SXin Lisub Diag { 82*67e74705SXin Li if ($UseColor) { 83*67e74705SXin Li print BOLD, MAGENTA "$Prog: @_"; 84*67e74705SXin Li print RESET; 85*67e74705SXin Li } 86*67e74705SXin Li else { 87*67e74705SXin Li print "$Prog: @_"; 88*67e74705SXin Li } 89*67e74705SXin Li} 90*67e74705SXin Li 91*67e74705SXin Lisub ErrorDiag { 92*67e74705SXin Li if ($UseColor) { 93*67e74705SXin Li print STDERR BOLD, RED "$Prog: "; 94*67e74705SXin Li print STDERR RESET, RED @_; 95*67e74705SXin Li print STDERR RESET; 96*67e74705SXin Li } else { 97*67e74705SXin Li print STDERR "$Prog: @_"; 98*67e74705SXin Li } 99*67e74705SXin Li} 100*67e74705SXin Li 101*67e74705SXin Lisub DiagCrashes { 102*67e74705SXin Li my $Dir = shift; 103*67e74705SXin Li Diag ("The analyzer encountered problems on some source files.\n"); 104*67e74705SXin Li Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); 105*67e74705SXin Li Diag ("Please consider submitting a bug report using these files:\n"); 106*67e74705SXin Li Diag (" http://clang-analyzer.llvm.org/filing_bugs.html\n") 107*67e74705SXin Li} 108*67e74705SXin Li 109*67e74705SXin Lisub DieDiag { 110*67e74705SXin Li if ($UseColor) { 111*67e74705SXin Li print STDERR BOLD, RED "$Prog: "; 112*67e74705SXin Li print STDERR RESET, RED @_; 113*67e74705SXin Li print STDERR RESET; 114*67e74705SXin Li } 115*67e74705SXin Li else { 116*67e74705SXin Li print STDERR "$Prog: ", @_; 117*67e74705SXin Li } 118*67e74705SXin Li exit 1; 119*67e74705SXin Li} 120*67e74705SXin Li 121*67e74705SXin Li##----------------------------------------------------------------------------## 122*67e74705SXin Li# Print default checker names 123*67e74705SXin Li##----------------------------------------------------------------------------## 124*67e74705SXin Li 125*67e74705SXin Liif (grep /^--help-checkers$/, @ARGV) { 126*67e74705SXin Li my @options = qx($0 -h); 127*67e74705SXin Li foreach (@options) { 128*67e74705SXin Li next unless /^ \+/; 129*67e74705SXin Li s/^\s*//; 130*67e74705SXin Li my ($sign, $name, @text) = split ' ', $_; 131*67e74705SXin Li print $name, $/ if $sign eq '+'; 132*67e74705SXin Li } 133*67e74705SXin Li exit 0; 134*67e74705SXin Li} 135*67e74705SXin Li 136*67e74705SXin Li##----------------------------------------------------------------------------## 137*67e74705SXin Li# Declaration of Clang options. Populated later. 138*67e74705SXin Li##----------------------------------------------------------------------------## 139*67e74705SXin Li 140*67e74705SXin Limy $Clang; 141*67e74705SXin Limy $ClangSB; 142*67e74705SXin Limy $ClangCXX; 143*67e74705SXin Limy $ClangVersion; 144*67e74705SXin Li 145*67e74705SXin Li##----------------------------------------------------------------------------## 146*67e74705SXin Li# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. 147*67e74705SXin Li##----------------------------------------------------------------------------## 148*67e74705SXin Li 149*67e74705SXin Lisub GetHTMLRunDir { 150*67e74705SXin Li die "Not enough arguments." if (@_ == 0); 151*67e74705SXin Li my $Dir = shift @_; 152*67e74705SXin Li my $TmpMode = 0; 153*67e74705SXin Li if (!defined $Dir) { 154*67e74705SXin Li $Dir = $ENV{'TMPDIR'} || $ENV{'TEMP'} || $ENV{'TMP'} || "/tmp"; 155*67e74705SXin Li $TmpMode = 1; 156*67e74705SXin Li } 157*67e74705SXin Li 158*67e74705SXin Li # Chop off any trailing '/' characters. 159*67e74705SXin Li while ($Dir =~ /\/$/) { chop $Dir; } 160*67e74705SXin Li 161*67e74705SXin Li # Get current date and time. 162*67e74705SXin Li my @CurrentTime = localtime(); 163*67e74705SXin Li my $year = $CurrentTime[5] + 1900; 164*67e74705SXin Li my $day = $CurrentTime[3]; 165*67e74705SXin Li my $month = $CurrentTime[4] + 1; 166*67e74705SXin Li my $hour = $CurrentTime[2]; 167*67e74705SXin Li my $min = $CurrentTime[1]; 168*67e74705SXin Li my $sec = $CurrentTime[0]; 169*67e74705SXin Li 170*67e74705SXin Li my $TimeString = sprintf("%02d%02d%02d", $hour, $min, $sec); 171*67e74705SXin Li my $DateString = sprintf("%d-%02d-%02d-%s-$$", 172*67e74705SXin Li $year, $month, $day, $TimeString); 173*67e74705SXin Li 174*67e74705SXin Li # Determine the run number. 175*67e74705SXin Li my $RunNumber; 176*67e74705SXin Li 177*67e74705SXin Li if (-d $Dir) { 178*67e74705SXin Li if (! -r $Dir) { 179*67e74705SXin Li DieDiag("directory '$Dir' exists but is not readable.\n"); 180*67e74705SXin Li } 181*67e74705SXin Li # Iterate over all files in the specified directory. 182*67e74705SXin Li my $max = 0; 183*67e74705SXin Li opendir(DIR, $Dir); 184*67e74705SXin Li my @FILES = grep { -d "$Dir/$_" } readdir(DIR); 185*67e74705SXin Li closedir(DIR); 186*67e74705SXin Li 187*67e74705SXin Li foreach my $f (@FILES) { 188*67e74705SXin Li # Strip the prefix '$Prog-' if we are dumping files to /tmp. 189*67e74705SXin Li if ($TmpMode) { 190*67e74705SXin Li next if (!($f =~ /^$Prog-(.+)/)); 191*67e74705SXin Li $f = $1; 192*67e74705SXin Li } 193*67e74705SXin Li 194*67e74705SXin Li my @x = split/-/, $f; 195*67e74705SXin Li next if (scalar(@x) != 4); 196*67e74705SXin Li next if ($x[0] != $year); 197*67e74705SXin Li next if ($x[1] != $month); 198*67e74705SXin Li next if ($x[2] != $day); 199*67e74705SXin Li next if ($x[3] != $TimeString); 200*67e74705SXin Li next if ($x[4] != $$); 201*67e74705SXin Li 202*67e74705SXin Li if ($x[5] > $max) { 203*67e74705SXin Li $max = $x[5]; 204*67e74705SXin Li } 205*67e74705SXin Li } 206*67e74705SXin Li 207*67e74705SXin Li $RunNumber = $max + 1; 208*67e74705SXin Li } 209*67e74705SXin Li else { 210*67e74705SXin Li 211*67e74705SXin Li if (-x $Dir) { 212*67e74705SXin Li DieDiag("'$Dir' exists but is not a directory.\n"); 213*67e74705SXin Li } 214*67e74705SXin Li 215*67e74705SXin Li if ($TmpMode) { 216*67e74705SXin Li DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); 217*67e74705SXin Li } 218*67e74705SXin Li 219*67e74705SXin Li # $Dir does not exist. It will be automatically created by the 220*67e74705SXin Li # clang driver. Set the run number to 1. 221*67e74705SXin Li 222*67e74705SXin Li $RunNumber = 1; 223*67e74705SXin Li } 224*67e74705SXin Li 225*67e74705SXin Li die "RunNumber must be defined!" if (!defined $RunNumber); 226*67e74705SXin Li 227*67e74705SXin Li # Append the run number. 228*67e74705SXin Li my $NewDir; 229*67e74705SXin Li if ($TmpMode) { 230*67e74705SXin Li $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; 231*67e74705SXin Li } 232*67e74705SXin Li else { 233*67e74705SXin Li $NewDir = "$Dir/$DateString-$RunNumber"; 234*67e74705SXin Li } 235*67e74705SXin Li 236*67e74705SXin Li # Make sure that the directory does not exist in order to avoid hijack. 237*67e74705SXin Li if (-e $NewDir) { 238*67e74705SXin Li DieDiag("The directory '$NewDir' already exists.\n"); 239*67e74705SXin Li } 240*67e74705SXin Li 241*67e74705SXin Li mkpath($NewDir); 242*67e74705SXin Li return $NewDir; 243*67e74705SXin Li} 244*67e74705SXin Li 245*67e74705SXin Lisub SetHtmlEnv { 246*67e74705SXin Li 247*67e74705SXin Li die "Wrong number of arguments." if (scalar(@_) != 2); 248*67e74705SXin Li 249*67e74705SXin Li my $Args = shift; 250*67e74705SXin Li my $Dir = shift; 251*67e74705SXin Li 252*67e74705SXin Li die "No build command." if (scalar(@$Args) == 0); 253*67e74705SXin Li 254*67e74705SXin Li my $Cmd = $$Args[0]; 255*67e74705SXin Li 256*67e74705SXin Li if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) { 257*67e74705SXin Li return; 258*67e74705SXin Li } 259*67e74705SXin Li 260*67e74705SXin Li if ($Options{Verbose}) { 261*67e74705SXin Li Diag("Emitting reports for this run to '$Dir'.\n"); 262*67e74705SXin Li } 263*67e74705SXin Li 264*67e74705SXin Li $ENV{'CCC_ANALYZER_HTML'} = $Dir; 265*67e74705SXin Li} 266*67e74705SXin Li 267*67e74705SXin Li##----------------------------------------------------------------------------## 268*67e74705SXin Li# ComputeDigest - Compute a digest of the specified file. 269*67e74705SXin Li##----------------------------------------------------------------------------## 270*67e74705SXin Li 271*67e74705SXin Lisub ComputeDigest { 272*67e74705SXin Li my $FName = shift; 273*67e74705SXin Li DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); 274*67e74705SXin Li 275*67e74705SXin Li # Use Digest::MD5. We don't have to be cryptographically secure. We're 276*67e74705SXin Li # just looking for duplicate files that come from a non-malicious source. 277*67e74705SXin Li # We use Digest::MD5 because it is a standard Perl module that should 278*67e74705SXin Li # come bundled on most systems. 279*67e74705SXin Li open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); 280*67e74705SXin Li binmode FILE; 281*67e74705SXin Li my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; 282*67e74705SXin Li close(FILE); 283*67e74705SXin Li 284*67e74705SXin Li # Return the digest. 285*67e74705SXin Li return $Result; 286*67e74705SXin Li} 287*67e74705SXin Li 288*67e74705SXin Li##----------------------------------------------------------------------------## 289*67e74705SXin Li# UpdatePrefix - Compute the common prefix of files. 290*67e74705SXin Li##----------------------------------------------------------------------------## 291*67e74705SXin Li 292*67e74705SXin Limy $Prefix; 293*67e74705SXin Li 294*67e74705SXin Lisub UpdatePrefix { 295*67e74705SXin Li my $x = shift; 296*67e74705SXin Li my $y = basename($x); 297*67e74705SXin Li $x =~ s/\Q$y\E$//; 298*67e74705SXin Li 299*67e74705SXin Li if (!defined $Prefix) { 300*67e74705SXin Li $Prefix = $x; 301*67e74705SXin Li return; 302*67e74705SXin Li } 303*67e74705SXin Li 304*67e74705SXin Li chop $Prefix while (!($x =~ /^\Q$Prefix/)); 305*67e74705SXin Li} 306*67e74705SXin Li 307*67e74705SXin Lisub GetPrefix { 308*67e74705SXin Li return $Prefix; 309*67e74705SXin Li} 310*67e74705SXin Li 311*67e74705SXin Li##----------------------------------------------------------------------------## 312*67e74705SXin Li# UpdateInFilePath - Update the path in the report file. 313*67e74705SXin Li##----------------------------------------------------------------------------## 314*67e74705SXin Li 315*67e74705SXin Lisub UpdateInFilePath { 316*67e74705SXin Li my $fname = shift; 317*67e74705SXin Li my $regex = shift; 318*67e74705SXin Li my $newtext = shift; 319*67e74705SXin Li 320*67e74705SXin Li open (RIN, $fname) or die "cannot open $fname"; 321*67e74705SXin Li open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; 322*67e74705SXin Li 323*67e74705SXin Li while (<RIN>) { 324*67e74705SXin Li s/$regex/$newtext/; 325*67e74705SXin Li print ROUT $_; 326*67e74705SXin Li } 327*67e74705SXin Li 328*67e74705SXin Li close (ROUT); 329*67e74705SXin Li close (RIN); 330*67e74705SXin Li rename("$fname.tmp", $fname) 331*67e74705SXin Li} 332*67e74705SXin Li 333*67e74705SXin Li##----------------------------------------------------------------------------## 334*67e74705SXin Li# AddStatLine - Decode and insert a statistics line into the database. 335*67e74705SXin Li##----------------------------------------------------------------------------## 336*67e74705SXin Li 337*67e74705SXin Lisub AddStatLine { 338*67e74705SXin Li my $Line = shift; 339*67e74705SXin Li my $Stats = shift; 340*67e74705SXin Li my $File = shift; 341*67e74705SXin Li 342*67e74705SXin Li print $Line . "\n"; 343*67e74705SXin Li 344*67e74705SXin Li my $Regex = qr/(.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable 345*67e74705SXin Li \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList: 346*67e74705SXin Li \ (yes|no)/x; 347*67e74705SXin Li 348*67e74705SXin Li if ($Line !~ $Regex) { 349*67e74705SXin Li return; 350*67e74705SXin Li } 351*67e74705SXin Li 352*67e74705SXin Li # Create a hash of the interesting fields 353*67e74705SXin Li my $Row = { 354*67e74705SXin Li Filename => $File, 355*67e74705SXin Li Function => $1, 356*67e74705SXin Li Total => $2, 357*67e74705SXin Li Unreachable => $3, 358*67e74705SXin Li Aborted => $4, 359*67e74705SXin Li Empty => $5 360*67e74705SXin Li }; 361*67e74705SXin Li 362*67e74705SXin Li # Add them to the stats array 363*67e74705SXin Li push @$Stats, $Row; 364*67e74705SXin Li} 365*67e74705SXin Li 366*67e74705SXin Li##----------------------------------------------------------------------------## 367*67e74705SXin Li# ScanFile - Scan a report file for various identifying attributes. 368*67e74705SXin Li##----------------------------------------------------------------------------## 369*67e74705SXin Li 370*67e74705SXin Li# Sometimes a source file is scanned more than once, and thus produces 371*67e74705SXin Li# multiple error reports. We use a cache to solve this problem. 372*67e74705SXin Li 373*67e74705SXin Limy %AlreadyScanned; 374*67e74705SXin Li 375*67e74705SXin Lisub ScanFile { 376*67e74705SXin Li 377*67e74705SXin Li my $Index = shift; 378*67e74705SXin Li my $Dir = shift; 379*67e74705SXin Li my $FName = shift; 380*67e74705SXin Li my $Stats = shift; 381*67e74705SXin Li 382*67e74705SXin Li # Compute a digest for the report file. Determine if we have already 383*67e74705SXin Li # scanned a file that looks just like it. 384*67e74705SXin Li 385*67e74705SXin Li my $digest = ComputeDigest("$Dir/$FName"); 386*67e74705SXin Li 387*67e74705SXin Li if (defined $AlreadyScanned{$digest}) { 388*67e74705SXin Li # Redundant file. Remove it. 389*67e74705SXin Li unlink("$Dir/$FName"); 390*67e74705SXin Li return; 391*67e74705SXin Li } 392*67e74705SXin Li 393*67e74705SXin Li $AlreadyScanned{$digest} = 1; 394*67e74705SXin Li 395*67e74705SXin Li # At this point the report file is not world readable. Make it happen. 396*67e74705SXin Li chmod(0644, "$Dir/$FName"); 397*67e74705SXin Li 398*67e74705SXin Li # Scan the report file for tags. 399*67e74705SXin Li open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); 400*67e74705SXin Li 401*67e74705SXin Li my $BugType = ""; 402*67e74705SXin Li my $BugFile = ""; 403*67e74705SXin Li my $BugFunction = ""; 404*67e74705SXin Li my $BugCategory = ""; 405*67e74705SXin Li my $BugDescription = ""; 406*67e74705SXin Li my $BugPathLength = 1; 407*67e74705SXin Li my $BugLine = 0; 408*67e74705SXin Li 409*67e74705SXin Li while (<IN>) { 410*67e74705SXin Li last if (/<!-- BUGMETAEND -->/); 411*67e74705SXin Li 412*67e74705SXin Li if (/<!-- BUGTYPE (.*) -->$/) { 413*67e74705SXin Li $BugType = $1; 414*67e74705SXin Li } 415*67e74705SXin Li elsif (/<!-- BUGFILE (.*) -->$/) { 416*67e74705SXin Li $BugFile = abs_path($1); 417*67e74705SXin Li if (!defined $BugFile) { 418*67e74705SXin Li # The file no longer exists: use the original path. 419*67e74705SXin Li $BugFile = $1; 420*67e74705SXin Li } 421*67e74705SXin Li UpdatePrefix($BugFile); 422*67e74705SXin Li } 423*67e74705SXin Li elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { 424*67e74705SXin Li $BugPathLength = $1; 425*67e74705SXin Li } 426*67e74705SXin Li elsif (/<!-- BUGLINE (.*) -->$/) { 427*67e74705SXin Li $BugLine = $1; 428*67e74705SXin Li } 429*67e74705SXin Li elsif (/<!-- BUGCATEGORY (.*) -->$/) { 430*67e74705SXin Li $BugCategory = $1; 431*67e74705SXin Li } 432*67e74705SXin Li elsif (/<!-- BUGDESC (.*) -->$/) { 433*67e74705SXin Li $BugDescription = $1; 434*67e74705SXin Li } 435*67e74705SXin Li elsif (/<!-- FUNCTIONNAME (.*) -->$/) { 436*67e74705SXin Li $BugFunction = $1; 437*67e74705SXin Li } 438*67e74705SXin Li 439*67e74705SXin Li } 440*67e74705SXin Li 441*67e74705SXin Li 442*67e74705SXin Li close(IN); 443*67e74705SXin Li 444*67e74705SXin Li if (!defined $BugCategory) { 445*67e74705SXin Li $BugCategory = "Other"; 446*67e74705SXin Li } 447*67e74705SXin Li 448*67e74705SXin Li # Don't add internal statistics to the bug reports 449*67e74705SXin Li if ($BugCategory =~ /statistics/i) { 450*67e74705SXin Li AddStatLine($BugDescription, $Stats, $BugFile); 451*67e74705SXin Li return; 452*67e74705SXin Li } 453*67e74705SXin Li 454*67e74705SXin Li push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugFunction, $BugLine, 455*67e74705SXin Li $BugPathLength ]; 456*67e74705SXin Li} 457*67e74705SXin Li 458*67e74705SXin Li##----------------------------------------------------------------------------## 459*67e74705SXin Li# CopyFiles - Copy resource files to target directory. 460*67e74705SXin Li##----------------------------------------------------------------------------## 461*67e74705SXin Li 462*67e74705SXin Lisub CopyFiles { 463*67e74705SXin Li 464*67e74705SXin Li my $Dir = shift; 465*67e74705SXin Li 466*67e74705SXin Li my $JS = Cwd::realpath("$RealBin/../share/scan-build/sorttable.js"); 467*67e74705SXin Li 468*67e74705SXin Li DieDiag("Cannot find 'sorttable.js'.\n") 469*67e74705SXin Li if (! -r $JS); 470*67e74705SXin Li 471*67e74705SXin Li copy($JS, "$Dir"); 472*67e74705SXin Li 473*67e74705SXin Li DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") 474*67e74705SXin Li if (! -r "$Dir/sorttable.js"); 475*67e74705SXin Li 476*67e74705SXin Li my $CSS = Cwd::realpath("$RealBin/../share/scan-build/scanview.css"); 477*67e74705SXin Li 478*67e74705SXin Li DieDiag("Cannot find 'scanview.css'.\n") 479*67e74705SXin Li if (! -r $CSS); 480*67e74705SXin Li 481*67e74705SXin Li copy($CSS, "$Dir"); 482*67e74705SXin Li 483*67e74705SXin Li DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") 484*67e74705SXin Li if (! -r $CSS); 485*67e74705SXin Li} 486*67e74705SXin Li 487*67e74705SXin Li##----------------------------------------------------------------------------## 488*67e74705SXin Li# CalcStats - Calculates visitation statistics and returns the string. 489*67e74705SXin Li##----------------------------------------------------------------------------## 490*67e74705SXin Li 491*67e74705SXin Lisub CalcStats { 492*67e74705SXin Li my $Stats = shift; 493*67e74705SXin Li 494*67e74705SXin Li my $TotalBlocks = 0; 495*67e74705SXin Li my $UnreachedBlocks = 0; 496*67e74705SXin Li my $TotalFunctions = scalar(@$Stats); 497*67e74705SXin Li my $BlockAborted = 0; 498*67e74705SXin Li my $WorkListAborted = 0; 499*67e74705SXin Li my $Aborted = 0; 500*67e74705SXin Li 501*67e74705SXin Li # Calculate the unique files 502*67e74705SXin Li my $FilesHash = {}; 503*67e74705SXin Li 504*67e74705SXin Li foreach my $Row (@$Stats) { 505*67e74705SXin Li $FilesHash->{$Row->{Filename}} = 1; 506*67e74705SXin Li $TotalBlocks += $Row->{Total}; 507*67e74705SXin Li $UnreachedBlocks += $Row->{Unreachable}; 508*67e74705SXin Li $BlockAborted++ if $Row->{Aborted} eq 'yes'; 509*67e74705SXin Li $WorkListAborted++ if $Row->{Empty} eq 'no'; 510*67e74705SXin Li $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no'; 511*67e74705SXin Li } 512*67e74705SXin Li 513*67e74705SXin Li my $TotalFiles = scalar(keys(%$FilesHash)); 514*67e74705SXin Li 515*67e74705SXin Li # Calculations 516*67e74705SXin Li my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100); 517*67e74705SXin Li my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions 518*67e74705SXin Li * 100); 519*67e74705SXin Li my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted / 520*67e74705SXin Li $TotalFunctions * 100); 521*67e74705SXin Li my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks 522*67e74705SXin Li * 100); 523*67e74705SXin Li 524*67e74705SXin Li my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions" 525*67e74705SXin Li . " in $TotalFiles files\n" 526*67e74705SXin Li . "$Aborted functions aborted early ($PercentAborted%)\n" 527*67e74705SXin Li . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n" 528*67e74705SXin Li . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n" 529*67e74705SXin Li . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n"; 530*67e74705SXin Li 531*67e74705SXin Li return $StatsString; 532*67e74705SXin Li} 533*67e74705SXin Li 534*67e74705SXin Li##----------------------------------------------------------------------------## 535*67e74705SXin Li# Postprocess - Postprocess the results of an analysis scan. 536*67e74705SXin Li##----------------------------------------------------------------------------## 537*67e74705SXin Li 538*67e74705SXin Limy @filesFound; 539*67e74705SXin Limy $baseDir; 540*67e74705SXin Lisub FileWanted { 541*67e74705SXin Li my $baseDirRegEx = quotemeta $baseDir; 542*67e74705SXin Li my $file = $File::Find::name; 543*67e74705SXin Li 544*67e74705SXin Li # The name of the file is generated by clang binary (HTMLDiagnostics.cpp) 545*67e74705SXin Li if ($file =~ /report-.*\.html$/) { 546*67e74705SXin Li my $relative_file = $file; 547*67e74705SXin Li $relative_file =~ s/$baseDirRegEx//g; 548*67e74705SXin Li push @filesFound, $relative_file; 549*67e74705SXin Li } 550*67e74705SXin Li} 551*67e74705SXin Li 552*67e74705SXin Lisub Postprocess { 553*67e74705SXin Li 554*67e74705SXin Li my $Dir = shift; 555*67e74705SXin Li my $BaseDir = shift; 556*67e74705SXin Li my $AnalyzerStats = shift; 557*67e74705SXin Li my $KeepEmpty = shift; 558*67e74705SXin Li 559*67e74705SXin Li die "No directory specified." if (!defined $Dir); 560*67e74705SXin Li 561*67e74705SXin Li if (! -d $Dir) { 562*67e74705SXin Li Diag("No bugs found.\n"); 563*67e74705SXin Li return 0; 564*67e74705SXin Li } 565*67e74705SXin Li 566*67e74705SXin Li $baseDir = $Dir . "/"; 567*67e74705SXin Li find({ wanted => \&FileWanted, follow => 0}, $Dir); 568*67e74705SXin Li 569*67e74705SXin Li if (scalar(@filesFound) == 0 and ! -e "$Dir/failures") { 570*67e74705SXin Li if (! $KeepEmpty) { 571*67e74705SXin Li Diag("Removing directory '$Dir' because it contains no reports.\n"); 572*67e74705SXin Li rmtree($Dir) or die "Cannot rmtree '$Dir' : $!"; 573*67e74705SXin Li } 574*67e74705SXin Li Diag("No bugs found.\n"); 575*67e74705SXin Li return 0; 576*67e74705SXin Li } 577*67e74705SXin Li 578*67e74705SXin Li # Scan each report file and build an index. 579*67e74705SXin Li my @Index; 580*67e74705SXin Li my @Stats; 581*67e74705SXin Li foreach my $file (@filesFound) { ScanFile(\@Index, $Dir, $file, \@Stats); } 582*67e74705SXin Li 583*67e74705SXin Li # Scan the failures directory and use the information in the .info files 584*67e74705SXin Li # to update the common prefix directory. 585*67e74705SXin Li my @failures; 586*67e74705SXin Li my @attributes_ignored; 587*67e74705SXin Li if (-d "$Dir/failures") { 588*67e74705SXin Li opendir(DIR, "$Dir/failures"); 589*67e74705SXin Li @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); 590*67e74705SXin Li closedir(DIR); 591*67e74705SXin Li opendir(DIR, "$Dir/failures"); 592*67e74705SXin Li @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); 593*67e74705SXin Li closedir(DIR); 594*67e74705SXin Li foreach my $file (@failures) { 595*67e74705SXin Li open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); 596*67e74705SXin Li my $Path = <IN>; 597*67e74705SXin Li if (defined $Path) { UpdatePrefix($Path); } 598*67e74705SXin Li close IN; 599*67e74705SXin Li } 600*67e74705SXin Li } 601*67e74705SXin Li 602*67e74705SXin Li # Generate an index.html file. 603*67e74705SXin Li my $FName = "$Dir/index.html"; 604*67e74705SXin Li open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); 605*67e74705SXin Li 606*67e74705SXin Li # Print out the header. 607*67e74705SXin Li 608*67e74705SXin Liprint OUT <<ENDTEXT; 609*67e74705SXin Li<html> 610*67e74705SXin Li<head> 611*67e74705SXin Li<title>${Options{HtmlTitle}}</title> 612*67e74705SXin Li<link type="text/css" rel="stylesheet" href="scanview.css"/> 613*67e74705SXin Li<script src="sorttable.js"></script> 614*67e74705SXin Li<script language='javascript' type="text/javascript"> 615*67e74705SXin Lifunction SetDisplay(RowClass, DisplayVal) 616*67e74705SXin Li{ 617*67e74705SXin Li var Rows = document.getElementsByTagName("tr"); 618*67e74705SXin Li for ( var i = 0 ; i < Rows.length; ++i ) { 619*67e74705SXin Li if (Rows[i].className == RowClass) { 620*67e74705SXin Li Rows[i].style.display = DisplayVal; 621*67e74705SXin Li } 622*67e74705SXin Li } 623*67e74705SXin Li} 624*67e74705SXin Li 625*67e74705SXin Lifunction CopyCheckedStateToCheckButtons(SummaryCheckButton) { 626*67e74705SXin Li var Inputs = document.getElementsByTagName("input"); 627*67e74705SXin Li for ( var i = 0 ; i < Inputs.length; ++i ) { 628*67e74705SXin Li if (Inputs[i].type == "checkbox") { 629*67e74705SXin Li if(Inputs[i] != SummaryCheckButton) { 630*67e74705SXin Li Inputs[i].checked = SummaryCheckButton.checked; 631*67e74705SXin Li Inputs[i].onclick(); 632*67e74705SXin Li } 633*67e74705SXin Li } 634*67e74705SXin Li } 635*67e74705SXin Li} 636*67e74705SXin Li 637*67e74705SXin Lifunction returnObjById( id ) { 638*67e74705SXin Li if (document.getElementById) 639*67e74705SXin Li var returnVar = document.getElementById(id); 640*67e74705SXin Li else if (document.all) 641*67e74705SXin Li var returnVar = document.all[id]; 642*67e74705SXin Li else if (document.layers) 643*67e74705SXin Li var returnVar = document.layers[id]; 644*67e74705SXin Li return returnVar; 645*67e74705SXin Li} 646*67e74705SXin Li 647*67e74705SXin Livar NumUnchecked = 0; 648*67e74705SXin Li 649*67e74705SXin Lifunction ToggleDisplay(CheckButton, ClassName) { 650*67e74705SXin Li if (CheckButton.checked) { 651*67e74705SXin Li SetDisplay(ClassName, ""); 652*67e74705SXin Li if (--NumUnchecked == 0) { 653*67e74705SXin Li returnObjById("AllBugsCheck").checked = true; 654*67e74705SXin Li } 655*67e74705SXin Li } 656*67e74705SXin Li else { 657*67e74705SXin Li SetDisplay(ClassName, "none"); 658*67e74705SXin Li NumUnchecked++; 659*67e74705SXin Li returnObjById("AllBugsCheck").checked = false; 660*67e74705SXin Li } 661*67e74705SXin Li} 662*67e74705SXin Li</script> 663*67e74705SXin Li<!-- SUMMARYENDHEAD --> 664*67e74705SXin Li</head> 665*67e74705SXin Li<body> 666*67e74705SXin Li<h1>${Options{HtmlTitle}}</h1> 667*67e74705SXin Li 668*67e74705SXin Li<table> 669*67e74705SXin Li<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> 670*67e74705SXin Li<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> 671*67e74705SXin Li<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> 672*67e74705SXin Li<tr><th>Clang Version:</th><td>${ClangVersion}</td></tr> 673*67e74705SXin Li<tr><th>Date:</th><td>${Date}</td></tr> 674*67e74705SXin LiENDTEXT 675*67e74705SXin Li 676*67e74705SXin Liprint OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" 677*67e74705SXin Li if (defined($BuildName) && defined($BuildDate)); 678*67e74705SXin Li 679*67e74705SXin Liprint OUT <<ENDTEXT; 680*67e74705SXin Li</table> 681*67e74705SXin LiENDTEXT 682*67e74705SXin Li 683*67e74705SXin Li if (scalar(@filesFound)) { 684*67e74705SXin Li # Print out the summary table. 685*67e74705SXin Li my %Totals; 686*67e74705SXin Li 687*67e74705SXin Li for my $row ( @Index ) { 688*67e74705SXin Li my $bug_type = ($row->[2]); 689*67e74705SXin Li my $bug_category = ($row->[1]); 690*67e74705SXin Li my $key = "$bug_category:$bug_type"; 691*67e74705SXin Li 692*67e74705SXin Li if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } 693*67e74705SXin Li else { $Totals{$key}->[0]++; } 694*67e74705SXin Li } 695*67e74705SXin Li 696*67e74705SXin Li print OUT "<h2>Bug Summary</h2>"; 697*67e74705SXin Li 698*67e74705SXin Li if (defined $BuildName) { 699*67e74705SXin Li print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" 700*67e74705SXin Li } 701*67e74705SXin Li 702*67e74705SXin Li my $TotalBugs = scalar(@Index); 703*67e74705SXin Liprint OUT <<ENDTEXT; 704*67e74705SXin Li<table> 705*67e74705SXin Li<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> 706*67e74705SXin Li<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> 707*67e74705SXin LiENDTEXT 708*67e74705SXin Li 709*67e74705SXin Li my $last_category; 710*67e74705SXin Li 711*67e74705SXin Li for my $key ( 712*67e74705SXin Li sort { 713*67e74705SXin Li my $x = $Totals{$a}; 714*67e74705SXin Li my $y = $Totals{$b}; 715*67e74705SXin Li my $res = $x->[1] cmp $y->[1]; 716*67e74705SXin Li $res = $x->[2] cmp $y->[2] if ($res == 0); 717*67e74705SXin Li $res 718*67e74705SXin Li } keys %Totals ) 719*67e74705SXin Li { 720*67e74705SXin Li my $val = $Totals{$key}; 721*67e74705SXin Li my $category = $val->[1]; 722*67e74705SXin Li if (!defined $last_category or $last_category ne $category) { 723*67e74705SXin Li $last_category = $category; 724*67e74705SXin Li print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; 725*67e74705SXin Li } 726*67e74705SXin Li my $x = lc $key; 727*67e74705SXin Li $x =~ s/[ ,'":\/()]+/_/g; 728*67e74705SXin Li print OUT "<tr><td class=\"SUMM_DESC\">"; 729*67e74705SXin Li print OUT $val->[2]; 730*67e74705SXin Li print OUT "</td><td class=\"Q\">"; 731*67e74705SXin Li print OUT $val->[0]; 732*67e74705SXin Li print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; 733*67e74705SXin Li } 734*67e74705SXin Li 735*67e74705SXin Li # Print out the table of errors. 736*67e74705SXin Li 737*67e74705SXin Liprint OUT <<ENDTEXT; 738*67e74705SXin Li</table> 739*67e74705SXin Li<h2>Reports</h2> 740*67e74705SXin Li 741*67e74705SXin Li<table class="sortable" style="table-layout:automatic"> 742*67e74705SXin Li<thead><tr> 743*67e74705SXin Li <td>Bug Group</td> 744*67e74705SXin Li <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> 745*67e74705SXin Li <td>File</td> 746*67e74705SXin Li <td>Function/Method</td> 747*67e74705SXin Li <td class="Q">Line</td> 748*67e74705SXin Li <td class="Q">Path Length</td> 749*67e74705SXin Li <td class="sorttable_nosort"></td> 750*67e74705SXin Li <!-- REPORTBUGCOL --> 751*67e74705SXin Li</tr></thead> 752*67e74705SXin Li<tbody> 753*67e74705SXin LiENDTEXT 754*67e74705SXin Li 755*67e74705SXin Li my $prefix = GetPrefix(); 756*67e74705SXin Li my $regex; 757*67e74705SXin Li my $InFileRegex; 758*67e74705SXin Li my $InFilePrefix = "File:</td><td>"; 759*67e74705SXin Li 760*67e74705SXin Li if (defined $prefix) { 761*67e74705SXin Li $regex = qr/^\Q$prefix\E/is; 762*67e74705SXin Li $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; 763*67e74705SXin Li } 764*67e74705SXin Li 765*67e74705SXin Li for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { 766*67e74705SXin Li my $x = "$row->[1]:$row->[2]"; 767*67e74705SXin Li $x = lc $x; 768*67e74705SXin Li $x =~ s/[ ,'":\/()]+/_/g; 769*67e74705SXin Li 770*67e74705SXin Li my $ReportFile = $row->[0]; 771*67e74705SXin Li 772*67e74705SXin Li print OUT "<tr class=\"bt_$x\">"; 773*67e74705SXin Li print OUT "<td class=\"DESC\">"; 774*67e74705SXin Li print OUT $row->[1]; 775*67e74705SXin Li print OUT "</td>"; 776*67e74705SXin Li print OUT "<td class=\"DESC\">"; 777*67e74705SXin Li print OUT $row->[2]; 778*67e74705SXin Li print OUT "</td>"; 779*67e74705SXin Li 780*67e74705SXin Li # Update the file prefix. 781*67e74705SXin Li my $fname = $row->[3]; 782*67e74705SXin Li 783*67e74705SXin Li if (defined $regex) { 784*67e74705SXin Li $fname =~ s/$regex//; 785*67e74705SXin Li UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) 786*67e74705SXin Li } 787*67e74705SXin Li 788*67e74705SXin Li print OUT "<td>"; 789*67e74705SXin Li my @fname = split /\//,$fname; 790*67e74705SXin Li if ($#fname > 0) { 791*67e74705SXin Li while ($#fname >= 0) { 792*67e74705SXin Li my $x = shift @fname; 793*67e74705SXin Li print OUT $x; 794*67e74705SXin Li if ($#fname >= 0) { 795*67e74705SXin Li print OUT "/"; 796*67e74705SXin Li } 797*67e74705SXin Li } 798*67e74705SXin Li } 799*67e74705SXin Li else { 800*67e74705SXin Li print OUT $fname; 801*67e74705SXin Li } 802*67e74705SXin Li print OUT "</td>"; 803*67e74705SXin Li 804*67e74705SXin Li print OUT "<td class=\"DESC\">"; 805*67e74705SXin Li print OUT $row->[4]; 806*67e74705SXin Li print OUT "</td>"; 807*67e74705SXin Li 808*67e74705SXin Li # Print out the quantities. 809*67e74705SXin Li for my $j ( 5 .. 6 ) { 810*67e74705SXin Li print OUT "<td class=\"Q\">$row->[$j]</td>"; 811*67e74705SXin Li } 812*67e74705SXin Li 813*67e74705SXin Li # Print the rest of the columns. 814*67e74705SXin Li for (my $j = 7; $j <= $#{$row}; ++$j) { 815*67e74705SXin Li print OUT "<td>$row->[$j]</td>" 816*67e74705SXin Li } 817*67e74705SXin Li 818*67e74705SXin Li # Emit the "View" link. 819*67e74705SXin Li print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; 820*67e74705SXin Li 821*67e74705SXin Li # Emit REPORTBUG markers. 822*67e74705SXin Li print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; 823*67e74705SXin Li 824*67e74705SXin Li # End the row. 825*67e74705SXin Li print OUT "</tr>\n"; 826*67e74705SXin Li } 827*67e74705SXin Li 828*67e74705SXin Li print OUT "</tbody>\n</table>\n\n"; 829*67e74705SXin Li } 830*67e74705SXin Li 831*67e74705SXin Li if (scalar (@failures) || scalar(@attributes_ignored)) { 832*67e74705SXin Li print OUT "<h2>Analyzer Failures</h2>\n"; 833*67e74705SXin Li 834*67e74705SXin Li if (scalar @attributes_ignored) { 835*67e74705SXin Li print OUT "The analyzer's parser ignored the following attributes:<p>\n"; 836*67e74705SXin Li print OUT "<table>\n"; 837*67e74705SXin Li print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; 838*67e74705SXin Li foreach my $file (sort @attributes_ignored) { 839*67e74705SXin Li die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); 840*67e74705SXin Li my $attribute = $1; 841*67e74705SXin Li # Open the attribute file to get the first file that failed. 842*67e74705SXin Li next if (!open (ATTR, "$Dir/failures/$file")); 843*67e74705SXin Li my $ppfile = <ATTR>; 844*67e74705SXin Li chomp $ppfile; 845*67e74705SXin Li close ATTR; 846*67e74705SXin Li next if (! -e "$Dir/failures/$ppfile"); 847*67e74705SXin Li # Open the info file and get the name of the source file. 848*67e74705SXin Li open (INFO, "$Dir/failures/$ppfile.info.txt") or 849*67e74705SXin Li die "Cannot open $Dir/failures/$ppfile.info.txt\n"; 850*67e74705SXin Li my $srcfile = <INFO>; 851*67e74705SXin Li chomp $srcfile; 852*67e74705SXin Li close (INFO); 853*67e74705SXin Li # Print the information in the table. 854*67e74705SXin Li my $prefix = GetPrefix(); 855*67e74705SXin Li if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } 856*67e74705SXin Li 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"; 857*67e74705SXin Li my $ppfile_clang = $ppfile; 858*67e74705SXin Li $ppfile_clang =~ s/[.](.+)$/.clang.$1/; 859*67e74705SXin Li print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; 860*67e74705SXin Li } 861*67e74705SXin Li print OUT "</table>\n"; 862*67e74705SXin Li } 863*67e74705SXin Li 864*67e74705SXin Li if (scalar @failures) { 865*67e74705SXin Li print OUT "<p>The analyzer had problems processing the following files:</p>\n"; 866*67e74705SXin Li print OUT "<table>\n"; 867*67e74705SXin Li print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; 868*67e74705SXin Li foreach my $file (sort @failures) { 869*67e74705SXin Li $file =~ /(.+).info.txt$/; 870*67e74705SXin Li # Get the preprocessed file. 871*67e74705SXin Li my $ppfile = $1; 872*67e74705SXin Li # Open the info file and get the name of the source file. 873*67e74705SXin Li open (INFO, "$Dir/failures/$file") or 874*67e74705SXin Li die "Cannot open $Dir/failures/$file\n"; 875*67e74705SXin Li my $srcfile = <INFO>; 876*67e74705SXin Li chomp $srcfile; 877*67e74705SXin Li my $problem = <INFO>; 878*67e74705SXin Li chomp $problem; 879*67e74705SXin Li close (INFO); 880*67e74705SXin Li # Print the information in the table. 881*67e74705SXin Li my $prefix = GetPrefix(); 882*67e74705SXin Li if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } 883*67e74705SXin Li 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"; 884*67e74705SXin Li my $ppfile_clang = $ppfile; 885*67e74705SXin Li $ppfile_clang =~ s/[.](.+)$/.clang.$1/; 886*67e74705SXin Li print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; 887*67e74705SXin Li } 888*67e74705SXin Li print OUT "</table>\n"; 889*67e74705SXin Li } 890*67e74705SXin Li 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"; 891*67e74705SXin Li } 892*67e74705SXin Li 893*67e74705SXin Li print OUT "</body></html>\n"; 894*67e74705SXin Li close(OUT); 895*67e74705SXin Li CopyFiles($Dir); 896*67e74705SXin Li 897*67e74705SXin Li # Make sure $Dir and $BaseDir are world readable/executable. 898*67e74705SXin Li chmod(0755, $Dir); 899*67e74705SXin Li if (defined $BaseDir) { chmod(0755, $BaseDir); } 900*67e74705SXin Li 901*67e74705SXin Li # Print statistics 902*67e74705SXin Li print CalcStats(\@Stats) if $AnalyzerStats; 903*67e74705SXin Li 904*67e74705SXin Li my $Num = scalar(@Index); 905*67e74705SXin Li if ($Num == 1) { 906*67e74705SXin Li Diag("$Num bug found.\n"); 907*67e74705SXin Li } else { 908*67e74705SXin Li Diag("$Num bugs found.\n"); 909*67e74705SXin Li } 910*67e74705SXin Li if ($Num > 0 && -r "$Dir/index.html") { 911*67e74705SXin Li Diag("Run 'scan-view $Dir' to examine bug reports.\n"); 912*67e74705SXin Li } 913*67e74705SXin Li 914*67e74705SXin Li DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); 915*67e74705SXin Li 916*67e74705SXin Li return $Num; 917*67e74705SXin Li} 918*67e74705SXin Li 919*67e74705SXin Li##----------------------------------------------------------------------------## 920*67e74705SXin Li# RunBuildCommand - Run the build command. 921*67e74705SXin Li##----------------------------------------------------------------------------## 922*67e74705SXin Li 923*67e74705SXin Lisub AddIfNotPresent { 924*67e74705SXin Li my $Args = shift; 925*67e74705SXin Li my $Arg = shift; 926*67e74705SXin Li my $found = 0; 927*67e74705SXin Li 928*67e74705SXin Li foreach my $k (@$Args) { 929*67e74705SXin Li if ($k eq $Arg) { 930*67e74705SXin Li $found = 1; 931*67e74705SXin Li last; 932*67e74705SXin Li } 933*67e74705SXin Li } 934*67e74705SXin Li 935*67e74705SXin Li if ($found == 0) { 936*67e74705SXin Li push @$Args, $Arg; 937*67e74705SXin Li } 938*67e74705SXin Li} 939*67e74705SXin Li 940*67e74705SXin Lisub SetEnv { 941*67e74705SXin Li my $EnvVars = shift @_; 942*67e74705SXin Li foreach my $var ('CC', 'CXX', 'CLANG', 'CLANG_CXX', 943*67e74705SXin Li 'CCC_ANALYZER_ANALYSIS', 'CCC_ANALYZER_PLUGINS', 944*67e74705SXin Li 'CCC_ANALYZER_CONFIG') { 945*67e74705SXin Li die "$var is undefined\n" if (!defined $var); 946*67e74705SXin Li $ENV{$var} = $EnvVars->{$var}; 947*67e74705SXin Li } 948*67e74705SXin Li foreach my $var ('CCC_ANALYZER_STORE_MODEL', 949*67e74705SXin Li 'CCC_ANALYZER_CONSTRAINTS_MODEL', 950*67e74705SXin Li 'CCC_ANALYZER_INTERNAL_STATS', 951*67e74705SXin Li 'CCC_ANALYZER_OUTPUT_FORMAT', 952*67e74705SXin Li 'CCC_CC', 953*67e74705SXin Li 'CCC_CXX', 954*67e74705SXin Li 'CCC_REPORT_FAILURES', 955*67e74705SXin Li 'CLANG_ANALYZER_TARGET', 956*67e74705SXin Li 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') { 957*67e74705SXin Li my $x = $EnvVars->{$var}; 958*67e74705SXin Li if (defined $x) { $ENV{$var} = $x } 959*67e74705SXin Li } 960*67e74705SXin Li my $Verbose = $EnvVars->{'VERBOSE'}; 961*67e74705SXin Li if ($Verbose >= 2) { 962*67e74705SXin Li $ENV{'CCC_ANALYZER_VERBOSE'} = 1; 963*67e74705SXin Li } 964*67e74705SXin Li if ($Verbose >= 3) { 965*67e74705SXin Li $ENV{'CCC_ANALYZER_LOG'} = 1; 966*67e74705SXin Li } 967*67e74705SXin Li} 968*67e74705SXin Li 969*67e74705SXin Lisub RunXcodebuild { 970*67e74705SXin Li my $Args = shift; 971*67e74705SXin Li my $IgnoreErrors = shift; 972*67e74705SXin Li my $CCAnalyzer = shift; 973*67e74705SXin Li my $CXXAnalyzer = shift; 974*67e74705SXin Li my $EnvVars = shift; 975*67e74705SXin Li 976*67e74705SXin Li if ($IgnoreErrors) { 977*67e74705SXin Li AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); 978*67e74705SXin Li } 979*67e74705SXin Li 980*67e74705SXin Li # Detect the version of Xcode. If Xcode 4.6 or higher, use new 981*67e74705SXin Li # in situ support for analyzer interposition without needed to override 982*67e74705SXin Li # the compiler. 983*67e74705SXin Li open(DETECT_XCODE, "-|", $Args->[0], "-version") or 984*67e74705SXin Li die "error: cannot detect version of xcodebuild\n"; 985*67e74705SXin Li 986*67e74705SXin Li my $oldBehavior = 1; 987*67e74705SXin Li 988*67e74705SXin Li while(<DETECT_XCODE>) { 989*67e74705SXin Li if (/^Xcode (.+)$/) { 990*67e74705SXin Li my $ver = $1; 991*67e74705SXin Li if ($ver =~ /^([0-9]+[.][0-9]+)[^0-9]?/) { 992*67e74705SXin Li if ($1 >= 4.6) { 993*67e74705SXin Li $oldBehavior = 0; 994*67e74705SXin Li last; 995*67e74705SXin Li } 996*67e74705SXin Li } 997*67e74705SXin Li } 998*67e74705SXin Li } 999*67e74705SXin Li close(DETECT_XCODE); 1000*67e74705SXin Li 1001*67e74705SXin Li # If --override-compiler is explicitely requested, resort to the old 1002*67e74705SXin Li # behavior regardless of Xcode version. 1003*67e74705SXin Li if ($Options{OverrideCompiler}) { 1004*67e74705SXin Li $oldBehavior = 1; 1005*67e74705SXin Li } 1006*67e74705SXin Li 1007*67e74705SXin Li if ($oldBehavior == 0) { 1008*67e74705SXin Li my $OutputDir = $EnvVars->{"OUTPUT_DIR"}; 1009*67e74705SXin Li my $CLANG = $EnvVars->{"CLANG"}; 1010*67e74705SXin Li my $OtherFlags = $EnvVars->{"CCC_ANALYZER_ANALYSIS"}; 1011*67e74705SXin Li push @$Args, 1012*67e74705SXin Li "RUN_CLANG_STATIC_ANALYZER=YES", 1013*67e74705SXin Li "CLANG_ANALYZER_OUTPUT=plist-html", 1014*67e74705SXin Li "CLANG_ANALYZER_EXEC=$CLANG", 1015*67e74705SXin Li "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir", 1016*67e74705SXin Li "CLANG_ANALYZER_OTHER_FLAGS=$OtherFlags"; 1017*67e74705SXin Li 1018*67e74705SXin Li return (system(@$Args) >> 8); 1019*67e74705SXin Li } 1020*67e74705SXin Li 1021*67e74705SXin Li # Default to old behavior where we insert a bogus compiler. 1022*67e74705SXin Li SetEnv($EnvVars); 1023*67e74705SXin Li 1024*67e74705SXin Li # Check if using iPhone SDK 3.0 (simulator). If so the compiler being 1025*67e74705SXin Li # used should be gcc-4.2. 1026*67e74705SXin Li if (!defined $ENV{"CCC_CC"}) { 1027*67e74705SXin Li for (my $i = 0 ; $i < scalar(@$Args); ++$i) { 1028*67e74705SXin Li if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { 1029*67e74705SXin Li if (@$Args[$i+1] =~ /^iphonesimulator3/) { 1030*67e74705SXin Li $ENV{"CCC_CC"} = "gcc-4.2"; 1031*67e74705SXin Li $ENV{"CCC_CXX"} = "g++-4.2"; 1032*67e74705SXin Li } 1033*67e74705SXin Li } 1034*67e74705SXin Li } 1035*67e74705SXin Li } 1036*67e74705SXin Li 1037*67e74705SXin Li # Disable PCH files until clang supports them. 1038*67e74705SXin Li AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); 1039*67e74705SXin Li 1040*67e74705SXin Li # When 'CC' is set, xcodebuild uses it to do all linking, even if we are 1041*67e74705SXin Li # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' 1042*67e74705SXin Li # (via c++-analyzer) when linking such files. 1043*67e74705SXin Li $ENV{"LDPLUSPLUS"} = $CXXAnalyzer; 1044*67e74705SXin Li 1045*67e74705SXin Li return (system(@$Args) >> 8); 1046*67e74705SXin Li} 1047*67e74705SXin Li 1048*67e74705SXin Lisub RunBuildCommand { 1049*67e74705SXin Li my $Args = shift; 1050*67e74705SXin Li my $IgnoreErrors = shift; 1051*67e74705SXin Li my $Cmd = $Args->[0]; 1052*67e74705SXin Li my $CCAnalyzer = shift; 1053*67e74705SXin Li my $CXXAnalyzer = shift; 1054*67e74705SXin Li my $EnvVars = shift; 1055*67e74705SXin Li 1056*67e74705SXin Li if ($Cmd =~ /\bxcodebuild$/) { 1057*67e74705SXin Li return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $EnvVars); 1058*67e74705SXin Li } 1059*67e74705SXin Li 1060*67e74705SXin Li # Setup the environment. 1061*67e74705SXin Li SetEnv($EnvVars); 1062*67e74705SXin Li 1063*67e74705SXin Li if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or 1064*67e74705SXin Li $Cmd =~ /(.*\/?cc[^\/]*$)/ or 1065*67e74705SXin Li $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or 1066*67e74705SXin Li $Cmd =~ /(.*\/?clang$)/ or 1067*67e74705SXin Li $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { 1068*67e74705SXin Li 1069*67e74705SXin Li if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { 1070*67e74705SXin Li $ENV{"CCC_CC"} = $1; 1071*67e74705SXin Li } 1072*67e74705SXin Li 1073*67e74705SXin Li shift @$Args; 1074*67e74705SXin Li unshift @$Args, $CCAnalyzer; 1075*67e74705SXin Li } 1076*67e74705SXin Li elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or 1077*67e74705SXin Li $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or 1078*67e74705SXin Li $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or 1079*67e74705SXin Li $Cmd =~ /(.*\/?clang\+\+$)/ or 1080*67e74705SXin Li $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) { 1081*67e74705SXin Li if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) { 1082*67e74705SXin Li $ENV{"CCC_CXX"} = $1; 1083*67e74705SXin Li } 1084*67e74705SXin Li shift @$Args; 1085*67e74705SXin Li unshift @$Args, $CXXAnalyzer; 1086*67e74705SXin Li } 1087*67e74705SXin Li elsif ($Cmd eq "make" or $Cmd eq "gmake" or $Cmd eq "mingw32-make") { 1088*67e74705SXin Li AddIfNotPresent($Args, "CC=$CCAnalyzer"); 1089*67e74705SXin Li AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); 1090*67e74705SXin Li if ($IgnoreErrors) { 1091*67e74705SXin Li AddIfNotPresent($Args,"-k"); 1092*67e74705SXin Li AddIfNotPresent($Args,"-i"); 1093*67e74705SXin Li } 1094*67e74705SXin Li } 1095*67e74705SXin Li 1096*67e74705SXin Li return (system(@$Args) >> 8); 1097*67e74705SXin Li} 1098*67e74705SXin Li 1099*67e74705SXin Li##----------------------------------------------------------------------------## 1100*67e74705SXin Li# DisplayHelp - Utility function to display all help options. 1101*67e74705SXin Li##----------------------------------------------------------------------------## 1102*67e74705SXin Li 1103*67e74705SXin Lisub DisplayHelp { 1104*67e74705SXin Li 1105*67e74705SXin Li my $ArgClangNotFoundErrMsg = shift; 1106*67e74705SXin Liprint <<ENDTEXT; 1107*67e74705SXin LiUSAGE: $Prog [options] <build command> [build options] 1108*67e74705SXin Li 1109*67e74705SXin LiENDTEXT 1110*67e74705SXin Li 1111*67e74705SXin Li if (defined $BuildName) { 1112*67e74705SXin Li print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; 1113*67e74705SXin Li } 1114*67e74705SXin Li 1115*67e74705SXin Liprint <<ENDTEXT; 1116*67e74705SXin LiOPTIONS: 1117*67e74705SXin Li 1118*67e74705SXin Li -analyze-headers 1119*67e74705SXin Li 1120*67e74705SXin Li Also analyze functions in #included files. By default, such functions 1121*67e74705SXin Li are skipped unless they are called by functions within the main source file. 1122*67e74705SXin Li 1123*67e74705SXin Li --force-analyze-debug-code 1124*67e74705SXin Li 1125*67e74705SXin Li Tells analyzer to enable assertions in code even if they were disabled 1126*67e74705SXin Li during compilation to enable more precise results. 1127*67e74705SXin Li 1128*67e74705SXin Li -o <output location> 1129*67e74705SXin Li 1130*67e74705SXin Li Specifies the output directory for analyzer reports. Subdirectories will be 1131*67e74705SXin Li created as needed to represent separate "runs" of the analyzer. If this 1132*67e74705SXin Li option is not specified, a directory is created in /tmp (TMPDIR on Mac OS X) 1133*67e74705SXin Li to store the reports. 1134*67e74705SXin Li 1135*67e74705SXin Li -h 1136*67e74705SXin Li --help 1137*67e74705SXin Li 1138*67e74705SXin Li Display this message. 1139*67e74705SXin Li 1140*67e74705SXin Li -k 1141*67e74705SXin Li --keep-going 1142*67e74705SXin Li 1143*67e74705SXin Li Add a "keep on going" option to the specified build command. This option 1144*67e74705SXin Li currently supports make and xcodebuild. This is a convenience option; one 1145*67e74705SXin Li can specify this behavior directly using build options. 1146*67e74705SXin Li 1147*67e74705SXin Li --html-title [title] 1148*67e74705SXin Li --html-title=[title] 1149*67e74705SXin Li 1150*67e74705SXin Li Specify the title used on generated HTML pages. If not specified, a default 1151*67e74705SXin Li title will be used. 1152*67e74705SXin Li 1153*67e74705SXin Li -plist 1154*67e74705SXin Li 1155*67e74705SXin Li By default the output of scan-build is a set of HTML files. This option 1156*67e74705SXin Li outputs the results as a set of .plist files. 1157*67e74705SXin Li 1158*67e74705SXin Li -plist-html 1159*67e74705SXin Li 1160*67e74705SXin Li By default the output of scan-build is a set of HTML files. This option 1161*67e74705SXin Li outputs the results as a set of HTML and .plist files. 1162*67e74705SXin Li 1163*67e74705SXin Li --status-bugs 1164*67e74705SXin Li 1165*67e74705SXin Li By default, the exit status of scan-build is the same as the executed build 1166*67e74705SXin Li command. Specifying this option causes the exit status of scan-build to be 1 1167*67e74705SXin Li if it found potential bugs and 0 otherwise. 1168*67e74705SXin Li 1169*67e74705SXin Li --use-cc [compiler path] 1170*67e74705SXin Li --use-cc=[compiler path] 1171*67e74705SXin Li 1172*67e74705SXin Li scan-build analyzes a project by interposing a "fake compiler", which 1173*67e74705SXin Li executes a real compiler for compilation and the static analyzer for analysis. 1174*67e74705SXin Li Because of the current implementation of interposition, scan-build does not 1175*67e74705SXin Li know what compiler your project normally uses. Instead, it simply overrides 1176*67e74705SXin Li the CC environment variable, and guesses your default compiler. 1177*67e74705SXin Li 1178*67e74705SXin Li In the future, this interposition mechanism to be improved, but if you need 1179*67e74705SXin Li scan-build to use a specific compiler for *compilation* then you can use 1180*67e74705SXin Li this option to specify a path to that compiler. 1181*67e74705SXin Li 1182*67e74705SXin Li If the given compiler is a cross compiler, you may also need to provide 1183*67e74705SXin Li --analyzer-target option to properly analyze the source code because static 1184*67e74705SXin Li analyzer runs as if the code is compiled for the host machine by default. 1185*67e74705SXin Li 1186*67e74705SXin Li --use-c++ [compiler path] 1187*67e74705SXin Li --use-c++=[compiler path] 1188*67e74705SXin Li 1189*67e74705SXin Li This is the same as "--use-cc" but for C++ code. 1190*67e74705SXin Li 1191*67e74705SXin Li --analyzer-target [target triple name for analysis] 1192*67e74705SXin Li --analyzer-target=[target triple name for analysis] 1193*67e74705SXin Li 1194*67e74705SXin Li This provides target triple information to clang static analyzer. 1195*67e74705SXin Li It only changes the target for analysis but doesn't change the target of a 1196*67e74705SXin Li real compiler given by --use-cc and --use-c++ options. 1197*67e74705SXin Li 1198*67e74705SXin Li -v 1199*67e74705SXin Li 1200*67e74705SXin Li Enable verbose output from scan-build. A second and third '-v' increases 1201*67e74705SXin Li verbosity. 1202*67e74705SXin Li 1203*67e74705SXin Li -V 1204*67e74705SXin Li --view 1205*67e74705SXin Li 1206*67e74705SXin Li View analysis results in a web browser when the build completes. 1207*67e74705SXin Li 1208*67e74705SXin LiADVANCED OPTIONS: 1209*67e74705SXin Li 1210*67e74705SXin Li -no-failure-reports 1211*67e74705SXin Li 1212*67e74705SXin Li Do not create a 'failures' subdirectory that includes analyzer crash reports 1213*67e74705SXin Li and preprocessed source files. 1214*67e74705SXin Li 1215*67e74705SXin Li -stats 1216*67e74705SXin Li 1217*67e74705SXin Li Generates visitation statistics for the project being analyzed. 1218*67e74705SXin Li 1219*67e74705SXin Li -maxloop <loop count> 1220*67e74705SXin Li 1221*67e74705SXin Li Specifiy the number of times a block can be visited before giving up. 1222*67e74705SXin Li Default is 4. Increase for more comprehensive coverage at a cost of speed. 1223*67e74705SXin Li 1224*67e74705SXin Li -internal-stats 1225*67e74705SXin Li 1226*67e74705SXin Li Generate internal analyzer statistics. 1227*67e74705SXin Li 1228*67e74705SXin Li --use-analyzer [Xcode|path to clang] 1229*67e74705SXin Li --use-analyzer=[Xcode|path to clang] 1230*67e74705SXin Li 1231*67e74705SXin Li scan-build uses the 'clang' executable relative to itself for static 1232*67e74705SXin Li analysis. One can override this behavior with this option by using the 1233*67e74705SXin Li 'clang' packaged with Xcode (on OS X) or from the PATH. 1234*67e74705SXin Li 1235*67e74705SXin Li --keep-empty 1236*67e74705SXin Li 1237*67e74705SXin Li Don't remove the build results directory even if no issues were reported. 1238*67e74705SXin Li 1239*67e74705SXin Li --override-compiler 1240*67e74705SXin Li Always resort to the ccc-analyzer even when better interposition methods 1241*67e74705SXin Li are available. 1242*67e74705SXin Li 1243*67e74705SXin Li -analyzer-config <options> 1244*67e74705SXin Li 1245*67e74705SXin Li Provide options to pass through to the analyzer's -analyzer-config flag. 1246*67e74705SXin Li Several options are separated with comma: 'key1=val1,key2=val2' 1247*67e74705SXin Li 1248*67e74705SXin Li Available options: 1249*67e74705SXin Li * stable-report-filename=true or false (default) 1250*67e74705SXin Li Switch the page naming to: 1251*67e74705SXin Li report-<filename>-<function/method name>-<id>.html 1252*67e74705SXin Li instead of report-XXXXXX.html 1253*67e74705SXin Li 1254*67e74705SXin LiCONTROLLING CHECKERS: 1255*67e74705SXin Li 1256*67e74705SXin Li A default group of checkers are always run unless explicitly disabled. 1257*67e74705SXin Li Checkers may be enabled/disabled using the following options: 1258*67e74705SXin Li 1259*67e74705SXin Li -enable-checker [checker name] 1260*67e74705SXin Li -disable-checker [checker name] 1261*67e74705SXin Li 1262*67e74705SXin LiLOADING CHECKERS: 1263*67e74705SXin Li 1264*67e74705SXin Li Loading external checkers using the clang plugin interface: 1265*67e74705SXin Li 1266*67e74705SXin Li -load-plugin [plugin library] 1267*67e74705SXin LiENDTEXT 1268*67e74705SXin Li 1269*67e74705SXin Li if (defined $Clang && -x $Clang) { 1270*67e74705SXin Li # Query clang for list of checkers that are enabled. 1271*67e74705SXin Li 1272*67e74705SXin Li # create a list to load the plugins via the 'Xclang' command line 1273*67e74705SXin Li # argument 1274*67e74705SXin Li my @PluginLoadCommandline_xclang; 1275*67e74705SXin Li foreach my $param ( @{$Options{PluginsToLoad}} ) { 1276*67e74705SXin Li push ( @PluginLoadCommandline_xclang, "-Xclang" ); 1277*67e74705SXin Li push ( @PluginLoadCommandline_xclang, "-load" ); 1278*67e74705SXin Li push ( @PluginLoadCommandline_xclang, "-Xclang" ); 1279*67e74705SXin Li push ( @PluginLoadCommandline_xclang, $param ); 1280*67e74705SXin Li } 1281*67e74705SXin Li 1282*67e74705SXin Li my %EnabledCheckers; 1283*67e74705SXin Li foreach my $lang ("c", "objective-c", "objective-c++", "c++") { 1284*67e74705SXin Li my $ExecLine = join(' ', qq/"$Clang"/, @PluginLoadCommandline_xclang, "--analyze", "-x", $lang, "-", "-###", "2>&1", "|"); 1285*67e74705SXin Li open(PS, $ExecLine); 1286*67e74705SXin Li while (<PS>) { 1287*67e74705SXin Li foreach my $val (split /\s+/) { 1288*67e74705SXin Li $val =~ s/\"//g; 1289*67e74705SXin Li if ($val =~ /-analyzer-checker\=([^\s]+)/) { 1290*67e74705SXin Li $EnabledCheckers{$1} = 1; 1291*67e74705SXin Li } 1292*67e74705SXin Li } 1293*67e74705SXin Li } 1294*67e74705SXin Li } 1295*67e74705SXin Li 1296*67e74705SXin Li # Query clang for complete list of checkers. 1297*67e74705SXin Li my @PluginLoadCommandline; 1298*67e74705SXin Li foreach my $param ( @{$Options{PluginsToLoad}} ) { 1299*67e74705SXin Li push ( @PluginLoadCommandline, "-load" ); 1300*67e74705SXin Li push ( @PluginLoadCommandline, $param ); 1301*67e74705SXin Li } 1302*67e74705SXin Li 1303*67e74705SXin Li my $ExecLine = join(' ', qq/"$Clang"/, "-cc1", @PluginLoadCommandline, "-analyzer-checker-help", "2>&1", "|"); 1304*67e74705SXin Li open(PS, $ExecLine); 1305*67e74705SXin Li my $foundCheckers = 0; 1306*67e74705SXin Li while (<PS>) { 1307*67e74705SXin Li if (/CHECKERS:/) { 1308*67e74705SXin Li $foundCheckers = 1; 1309*67e74705SXin Li last; 1310*67e74705SXin Li } 1311*67e74705SXin Li } 1312*67e74705SXin Li if (!$foundCheckers) { 1313*67e74705SXin Li print " *** Could not query Clang for the list of available checkers."; 1314*67e74705SXin Li } 1315*67e74705SXin Li else { 1316*67e74705SXin Li print("\nAVAILABLE CHECKERS:\n\n"); 1317*67e74705SXin Li my $skip = 0; 1318*67e74705SXin Li while(<PS>) { 1319*67e74705SXin Li if (/experimental/) { 1320*67e74705SXin Li $skip = 1; 1321*67e74705SXin Li next; 1322*67e74705SXin Li } 1323*67e74705SXin Li if ($skip) { 1324*67e74705SXin Li next if (!/^\s\s[^\s]/); 1325*67e74705SXin Li $skip = 0; 1326*67e74705SXin Li } 1327*67e74705SXin Li s/^\s\s//; 1328*67e74705SXin Li if (/^([^\s]+)/) { 1329*67e74705SXin Li # Is the checker enabled? 1330*67e74705SXin Li my $checker = $1; 1331*67e74705SXin Li my $enabled = 0; 1332*67e74705SXin Li my $aggregate = ""; 1333*67e74705SXin Li foreach my $domain (split /\./, $checker) { 1334*67e74705SXin Li $aggregate .= $domain; 1335*67e74705SXin Li if ($EnabledCheckers{$aggregate}) { 1336*67e74705SXin Li $enabled =1; 1337*67e74705SXin Li last; 1338*67e74705SXin Li } 1339*67e74705SXin Li # append a dot, if an additional domain is added in the next iteration 1340*67e74705SXin Li $aggregate .= "."; 1341*67e74705SXin Li } 1342*67e74705SXin Li 1343*67e74705SXin Li if ($enabled) { 1344*67e74705SXin Li print " + "; 1345*67e74705SXin Li } 1346*67e74705SXin Li else { 1347*67e74705SXin Li print " "; 1348*67e74705SXin Li } 1349*67e74705SXin Li } 1350*67e74705SXin Li else { 1351*67e74705SXin Li print " "; 1352*67e74705SXin Li } 1353*67e74705SXin Li print $_; 1354*67e74705SXin Li } 1355*67e74705SXin Li print "\nNOTE: \"+\" indicates that an analysis is enabled by default.\n"; 1356*67e74705SXin Li } 1357*67e74705SXin Li close PS; 1358*67e74705SXin Li } 1359*67e74705SXin Li else { 1360*67e74705SXin Li print " *** Could not query Clang for the list of available checkers.\n"; 1361*67e74705SXin Li if (defined $ArgClangNotFoundErrMsg) { 1362*67e74705SXin Li print " *** Reason: $ArgClangNotFoundErrMsg\n"; 1363*67e74705SXin Li } 1364*67e74705SXin Li } 1365*67e74705SXin Li 1366*67e74705SXin Liprint <<ENDTEXT 1367*67e74705SXin Li 1368*67e74705SXin LiBUILD OPTIONS 1369*67e74705SXin Li 1370*67e74705SXin Li You can specify any build option acceptable to the build command. 1371*67e74705SXin Li 1372*67e74705SXin LiEXAMPLE 1373*67e74705SXin Li 1374*67e74705SXin Li scan-build -o /tmp/myhtmldir make -j4 1375*67e74705SXin Li 1376*67e74705SXin LiThe above example causes analysis reports to be deposited into a subdirectory 1377*67e74705SXin Liof "/tmp/myhtmldir" and to run "make" with the "-j4" option. A different 1378*67e74705SXin Lisubdirectory is created each time scan-build analyzes a project. The analyzer 1379*67e74705SXin Lishould support most parallel builds, but not distributed builds. 1380*67e74705SXin Li 1381*67e74705SXin LiENDTEXT 1382*67e74705SXin Li} 1383*67e74705SXin Li 1384*67e74705SXin Li##----------------------------------------------------------------------------## 1385*67e74705SXin Li# HtmlEscape - HTML entity encode characters that are special in HTML 1386*67e74705SXin Li##----------------------------------------------------------------------------## 1387*67e74705SXin Li 1388*67e74705SXin Lisub HtmlEscape { 1389*67e74705SXin Li # copy argument to new variable so we don't clobber the original 1390*67e74705SXin Li my $arg = shift || ''; 1391*67e74705SXin Li my $tmp = $arg; 1392*67e74705SXin Li $tmp =~ s/&/&/g; 1393*67e74705SXin Li $tmp =~ s/</</g; 1394*67e74705SXin Li $tmp =~ s/>/>/g; 1395*67e74705SXin Li return $tmp; 1396*67e74705SXin Li} 1397*67e74705SXin Li 1398*67e74705SXin Li##----------------------------------------------------------------------------## 1399*67e74705SXin Li# ShellEscape - backslash escape characters that are special to the shell 1400*67e74705SXin Li##----------------------------------------------------------------------------## 1401*67e74705SXin Li 1402*67e74705SXin Lisub ShellEscape { 1403*67e74705SXin Li # copy argument to new variable so we don't clobber the original 1404*67e74705SXin Li my $arg = shift || ''; 1405*67e74705SXin Li if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } 1406*67e74705SXin Li return $arg; 1407*67e74705SXin Li} 1408*67e74705SXin Li 1409*67e74705SXin Li##----------------------------------------------------------------------------## 1410*67e74705SXin Li# FindClang - searches for 'clang' executable. 1411*67e74705SXin Li##----------------------------------------------------------------------------## 1412*67e74705SXin Li 1413*67e74705SXin Lisub FindClang { 1414*67e74705SXin Li if (!defined $Options{AnalyzerDiscoveryMethod}) { 1415*67e74705SXin Li $Clang = Cwd::realpath("$RealBin/bin/clang") if (-f "$RealBin/bin/clang"); 1416*67e74705SXin Li if (!defined $Clang || ! -x $Clang) { 1417*67e74705SXin Li $Clang = Cwd::realpath("$RealBin/clang") if (-f "$RealBin/clang"); 1418*67e74705SXin Li } 1419*67e74705SXin Li if (!defined $Clang || ! -x $Clang) { 1420*67e74705SXin Li return "error: Cannot find an executable 'clang' relative to" . 1421*67e74705SXin Li " scan-build. Consider using --use-analyzer to pick a version of" . 1422*67e74705SXin Li " 'clang' to use for static analysis.\n"; 1423*67e74705SXin Li } 1424*67e74705SXin Li } 1425*67e74705SXin Li else { 1426*67e74705SXin Li if ($Options{AnalyzerDiscoveryMethod} =~ /^[Xx]code$/) { 1427*67e74705SXin Li my $xcrun = `which xcrun`; 1428*67e74705SXin Li chomp $xcrun; 1429*67e74705SXin Li if ($xcrun eq "") { 1430*67e74705SXin Li return "Cannot find 'xcrun' to find 'clang' for analysis.\n"; 1431*67e74705SXin Li } 1432*67e74705SXin Li $Clang = `$xcrun -toolchain XcodeDefault -find clang`; 1433*67e74705SXin Li chomp $Clang; 1434*67e74705SXin Li if ($Clang eq "") { 1435*67e74705SXin Li return "No 'clang' executable found by 'xcrun'\n"; 1436*67e74705SXin Li } 1437*67e74705SXin Li } 1438*67e74705SXin Li else { 1439*67e74705SXin Li $Clang = $Options{AnalyzerDiscoveryMethod}; 1440*67e74705SXin Li if (!defined $Clang or not -x $Clang) { 1441*67e74705SXin Li return "Cannot find an executable clang at '$Options{AnalyzerDiscoveryMethod}'\n"; 1442*67e74705SXin Li } 1443*67e74705SXin Li } 1444*67e74705SXin Li } 1445*67e74705SXin Li return undef; 1446*67e74705SXin Li} 1447*67e74705SXin Li 1448*67e74705SXin Li##----------------------------------------------------------------------------## 1449*67e74705SXin Li# Process command-line arguments. 1450*67e74705SXin Li##----------------------------------------------------------------------------## 1451*67e74705SXin Li 1452*67e74705SXin Limy $RequestDisplayHelp = 0; 1453*67e74705SXin Limy $ForceDisplayHelp = 0; 1454*67e74705SXin Li 1455*67e74705SXin Lisub ProcessArgs { 1456*67e74705SXin Li my $Args = shift; 1457*67e74705SXin Li my $NumArgs = 0; 1458*67e74705SXin Li 1459*67e74705SXin Li while (@$Args) { 1460*67e74705SXin Li 1461*67e74705SXin Li $NumArgs++; 1462*67e74705SXin Li 1463*67e74705SXin Li # Scan for options we recognize. 1464*67e74705SXin Li 1465*67e74705SXin Li my $arg = $Args->[0]; 1466*67e74705SXin Li 1467*67e74705SXin Li if ($arg eq "-h" or $arg eq "--help") { 1468*67e74705SXin Li $RequestDisplayHelp = 1; 1469*67e74705SXin Li shift @$Args; 1470*67e74705SXin Li next; 1471*67e74705SXin Li } 1472*67e74705SXin Li 1473*67e74705SXin Li if ($arg eq '-analyze-headers') { 1474*67e74705SXin Li shift @$Args; 1475*67e74705SXin Li $Options{AnalyzeHeaders} = 1; 1476*67e74705SXin Li next; 1477*67e74705SXin Li } 1478*67e74705SXin Li 1479*67e74705SXin Li if ($arg eq "-o") { 1480*67e74705SXin Li shift @$Args; 1481*67e74705SXin Li 1482*67e74705SXin Li if (!@$Args) { 1483*67e74705SXin Li DieDiag("'-o' option requires a target directory name.\n"); 1484*67e74705SXin Li } 1485*67e74705SXin Li 1486*67e74705SXin Li # Construct an absolute path. Uses the current working directory 1487*67e74705SXin Li # as a base if the original path was not absolute. 1488*67e74705SXin Li my $OutDir = shift @$Args; 1489*67e74705SXin Li mkpath($OutDir) unless (-e $OutDir); # abs_path wants existing dir 1490*67e74705SXin Li $Options{OutputDir} = abs_path($OutDir); 1491*67e74705SXin Li 1492*67e74705SXin Li next; 1493*67e74705SXin Li } 1494*67e74705SXin Li 1495*67e74705SXin Li if ($arg =~ /^--html-title(=(.+))?$/) { 1496*67e74705SXin Li shift @$Args; 1497*67e74705SXin Li 1498*67e74705SXin Li if (!defined $2 || $2 eq '') { 1499*67e74705SXin Li if (!@$Args) { 1500*67e74705SXin Li DieDiag("'--html-title' option requires a string.\n"); 1501*67e74705SXin Li } 1502*67e74705SXin Li 1503*67e74705SXin Li $Options{HtmlTitle} = shift @$Args; 1504*67e74705SXin Li } else { 1505*67e74705SXin Li $Options{HtmlTitle} = $2; 1506*67e74705SXin Li } 1507*67e74705SXin Li 1508*67e74705SXin Li next; 1509*67e74705SXin Li } 1510*67e74705SXin Li 1511*67e74705SXin Li if ($arg eq "-k" or $arg eq "--keep-going") { 1512*67e74705SXin Li shift @$Args; 1513*67e74705SXin Li $Options{IgnoreErrors} = 1; 1514*67e74705SXin Li next; 1515*67e74705SXin Li } 1516*67e74705SXin Li 1517*67e74705SXin Li if ($arg =~ /^--use-cc(=(.+))?$/) { 1518*67e74705SXin Li shift @$Args; 1519*67e74705SXin Li my $cc; 1520*67e74705SXin Li 1521*67e74705SXin Li if (!defined $2 || $2 eq "") { 1522*67e74705SXin Li if (!@$Args) { 1523*67e74705SXin Li DieDiag("'--use-cc' option requires a compiler executable name.\n"); 1524*67e74705SXin Li } 1525*67e74705SXin Li $cc = shift @$Args; 1526*67e74705SXin Li } 1527*67e74705SXin Li else { 1528*67e74705SXin Li $cc = $2; 1529*67e74705SXin Li } 1530*67e74705SXin Li 1531*67e74705SXin Li $Options{UseCC} = $cc; 1532*67e74705SXin Li next; 1533*67e74705SXin Li } 1534*67e74705SXin Li 1535*67e74705SXin Li if ($arg =~ /^--use-c\+\+(=(.+))?$/) { 1536*67e74705SXin Li shift @$Args; 1537*67e74705SXin Li my $cxx; 1538*67e74705SXin Li 1539*67e74705SXin Li if (!defined $2 || $2 eq "") { 1540*67e74705SXin Li if (!@$Args) { 1541*67e74705SXin Li DieDiag("'--use-c++' option requires a compiler executable name.\n"); 1542*67e74705SXin Li } 1543*67e74705SXin Li $cxx = shift @$Args; 1544*67e74705SXin Li } 1545*67e74705SXin Li else { 1546*67e74705SXin Li $cxx = $2; 1547*67e74705SXin Li } 1548*67e74705SXin Li 1549*67e74705SXin Li $Options{UseCXX} = $cxx; 1550*67e74705SXin Li next; 1551*67e74705SXin Li } 1552*67e74705SXin Li 1553*67e74705SXin Li if ($arg =~ /^--analyzer-target(=(.+))?$/) { 1554*67e74705SXin Li shift @ARGV; 1555*67e74705SXin Li my $AnalyzerTarget; 1556*67e74705SXin Li 1557*67e74705SXin Li if (!defined $2 || $2 eq "") { 1558*67e74705SXin Li if (!@ARGV) { 1559*67e74705SXin Li DieDiag("'--analyzer-target' option requires a target triple name.\n"); 1560*67e74705SXin Li } 1561*67e74705SXin Li $AnalyzerTarget = shift @ARGV; 1562*67e74705SXin Li } 1563*67e74705SXin Li else { 1564*67e74705SXin Li $AnalyzerTarget = $2; 1565*67e74705SXin Li } 1566*67e74705SXin Li 1567*67e74705SXin Li $Options{AnalyzerTarget} = $AnalyzerTarget; 1568*67e74705SXin Li next; 1569*67e74705SXin Li } 1570*67e74705SXin Li 1571*67e74705SXin Li if ($arg eq "-v") { 1572*67e74705SXin Li shift @$Args; 1573*67e74705SXin Li $Options{Verbose}++; 1574*67e74705SXin Li next; 1575*67e74705SXin Li } 1576*67e74705SXin Li 1577*67e74705SXin Li if ($arg eq "-V" or $arg eq "--view") { 1578*67e74705SXin Li shift @$Args; 1579*67e74705SXin Li $Options{ViewResults} = 1; 1580*67e74705SXin Li next; 1581*67e74705SXin Li } 1582*67e74705SXin Li 1583*67e74705SXin Li if ($arg eq "--status-bugs") { 1584*67e74705SXin Li shift @$Args; 1585*67e74705SXin Li $Options{ExitStatusFoundBugs} = 1; 1586*67e74705SXin Li next; 1587*67e74705SXin Li } 1588*67e74705SXin Li 1589*67e74705SXin Li if ($arg eq "-store") { 1590*67e74705SXin Li shift @$Args; 1591*67e74705SXin Li $Options{StoreModel} = shift @$Args; 1592*67e74705SXin Li next; 1593*67e74705SXin Li } 1594*67e74705SXin Li 1595*67e74705SXin Li if ($arg eq "-constraints") { 1596*67e74705SXin Li shift @$Args; 1597*67e74705SXin Li $Options{ConstraintsModel} = shift @$Args; 1598*67e74705SXin Li next; 1599*67e74705SXin Li } 1600*67e74705SXin Li 1601*67e74705SXin Li if ($arg eq "-internal-stats") { 1602*67e74705SXin Li shift @$Args; 1603*67e74705SXin Li $Options{InternalStats} = 1; 1604*67e74705SXin Li next; 1605*67e74705SXin Li } 1606*67e74705SXin Li 1607*67e74705SXin Li if ($arg eq "-plist") { 1608*67e74705SXin Li shift @$Args; 1609*67e74705SXin Li $Options{OutputFormat} = "plist"; 1610*67e74705SXin Li next; 1611*67e74705SXin Li } 1612*67e74705SXin Li 1613*67e74705SXin Li if ($arg eq "-plist-html") { 1614*67e74705SXin Li shift @$Args; 1615*67e74705SXin Li $Options{OutputFormat} = "plist-html"; 1616*67e74705SXin Li next; 1617*67e74705SXin Li } 1618*67e74705SXin Li 1619*67e74705SXin Li if ($arg eq "-analyzer-config") { 1620*67e74705SXin Li shift @$Args; 1621*67e74705SXin Li push @{$Options{ConfigOptions}}, shift @$Args; 1622*67e74705SXin Li next; 1623*67e74705SXin Li } 1624*67e74705SXin Li 1625*67e74705SXin Li if ($arg eq "-no-failure-reports") { 1626*67e74705SXin Li shift @$Args; 1627*67e74705SXin Li $Options{ReportFailures} = 0; 1628*67e74705SXin Li next; 1629*67e74705SXin Li } 1630*67e74705SXin Li 1631*67e74705SXin Li if ($arg eq "-stats") { 1632*67e74705SXin Li shift @$Args; 1633*67e74705SXin Li $Options{AnalyzerStats} = 1; 1634*67e74705SXin Li next; 1635*67e74705SXin Li } 1636*67e74705SXin Li 1637*67e74705SXin Li if ($arg eq "-maxloop") { 1638*67e74705SXin Li shift @$Args; 1639*67e74705SXin Li $Options{MaxLoop} = shift @$Args; 1640*67e74705SXin Li next; 1641*67e74705SXin Li } 1642*67e74705SXin Li 1643*67e74705SXin Li if ($arg eq "-enable-checker") { 1644*67e74705SXin Li shift @$Args; 1645*67e74705SXin Li my $Checker = shift @$Args; 1646*67e74705SXin Li # Store $NumArgs to preserve the order the checkers were enabled. 1647*67e74705SXin Li $Options{EnableCheckers}{$Checker} = $NumArgs; 1648*67e74705SXin Li delete $Options{DisableCheckers}{$Checker}; 1649*67e74705SXin Li next; 1650*67e74705SXin Li } 1651*67e74705SXin Li 1652*67e74705SXin Li if ($arg eq "-disable-checker") { 1653*67e74705SXin Li shift @$Args; 1654*67e74705SXin Li my $Checker = shift @$Args; 1655*67e74705SXin Li # Store $NumArgs to preserve the order the checkers were disabled. 1656*67e74705SXin Li $Options{DisableCheckers}{$Checker} = $NumArgs; 1657*67e74705SXin Li delete $Options{EnableCheckers}{$Checker}; 1658*67e74705SXin Li next; 1659*67e74705SXin Li } 1660*67e74705SXin Li 1661*67e74705SXin Li if ($arg eq "-load-plugin") { 1662*67e74705SXin Li shift @$Args; 1663*67e74705SXin Li push @{$Options{PluginsToLoad}}, shift @$Args; 1664*67e74705SXin Li next; 1665*67e74705SXin Li } 1666*67e74705SXin Li 1667*67e74705SXin Li if ($arg eq "--use-analyzer") { 1668*67e74705SXin Li shift @$Args; 1669*67e74705SXin Li $Options{AnalyzerDiscoveryMethod} = shift @$Args; 1670*67e74705SXin Li next; 1671*67e74705SXin Li } 1672*67e74705SXin Li 1673*67e74705SXin Li if ($arg =~ /^--use-analyzer=(.+)$/) { 1674*67e74705SXin Li shift @$Args; 1675*67e74705SXin Li $Options{AnalyzerDiscoveryMethod} = $1; 1676*67e74705SXin Li next; 1677*67e74705SXin Li } 1678*67e74705SXin Li 1679*67e74705SXin Li if ($arg eq "--keep-empty") { 1680*67e74705SXin Li shift @$Args; 1681*67e74705SXin Li $Options{KeepEmpty} = 1; 1682*67e74705SXin Li next; 1683*67e74705SXin Li } 1684*67e74705SXin Li 1685*67e74705SXin Li if ($arg eq "--override-compiler") { 1686*67e74705SXin Li shift @$Args; 1687*67e74705SXin Li $Options{OverrideCompiler} = 1; 1688*67e74705SXin Li next; 1689*67e74705SXin Li } 1690*67e74705SXin Li 1691*67e74705SXin Li if ($arg eq "--force-analyze-debug-code") { 1692*67e74705SXin Li shift @$Args; 1693*67e74705SXin Li $Options{ForceAnalyzeDebugCode} = 1; 1694*67e74705SXin Li next; 1695*67e74705SXin Li } 1696*67e74705SXin Li 1697*67e74705SXin Li DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); 1698*67e74705SXin Li 1699*67e74705SXin Li $NumArgs--; 1700*67e74705SXin Li last; 1701*67e74705SXin Li } 1702*67e74705SXin Li return $NumArgs; 1703*67e74705SXin Li} 1704*67e74705SXin Li 1705*67e74705SXin Liif (!@ARGV) { 1706*67e74705SXin Li $ForceDisplayHelp = 1 1707*67e74705SXin Li} 1708*67e74705SXin Li 1709*67e74705SXin LiProcessArgs(\@ARGV); 1710*67e74705SXin Li# All arguments are now shifted from @ARGV. The rest is a build command, if any. 1711*67e74705SXin Li 1712*67e74705SXin Liif (!@ARGV and !$RequestDisplayHelp) { 1713*67e74705SXin Li ErrorDiag("No build command specified.\n\n"); 1714*67e74705SXin Li $ForceDisplayHelp = 1; 1715*67e74705SXin Li} 1716*67e74705SXin Li 1717*67e74705SXin Limy $ClangNotFoundErrMsg = FindClang(); 1718*67e74705SXin Li 1719*67e74705SXin Liif ($ForceDisplayHelp || $RequestDisplayHelp) { 1720*67e74705SXin Li DisplayHelp($ClangNotFoundErrMsg); 1721*67e74705SXin Li exit $ForceDisplayHelp; 1722*67e74705SXin Li} 1723*67e74705SXin Li 1724*67e74705SXin LiDieDiag($ClangNotFoundErrMsg) if (defined $ClangNotFoundErrMsg); 1725*67e74705SXin Li 1726*67e74705SXin Li$ClangCXX = $Clang; 1727*67e74705SXin Liif ($Clang !~ /\+\+(\.exe)?$/) { 1728*67e74705SXin Li # If $Clang holds the name of the clang++ executable then we leave 1729*67e74705SXin Li # $ClangCXX and $Clang equal, otherwise construct the name of the clang++ 1730*67e74705SXin Li # executable from the clang executable name. 1731*67e74705SXin Li 1732*67e74705SXin Li # Determine operating system under which this copy of Perl was built. 1733*67e74705SXin Li my $IsWinBuild = ($^O =~/msys|cygwin|MSWin32/); 1734*67e74705SXin Li if($IsWinBuild) { 1735*67e74705SXin Li $ClangCXX =~ s/.exe$/++.exe/; 1736*67e74705SXin Li } 1737*67e74705SXin Li else { 1738*67e74705SXin Li $ClangCXX =~ s/\-\d+\.\d+$//; 1739*67e74705SXin Li $ClangCXX .= "++"; 1740*67e74705SXin Li } 1741*67e74705SXin Li} 1742*67e74705SXin Li 1743*67e74705SXin Li# Make sure to use "" to handle paths with spaces. 1744*67e74705SXin Li$ClangVersion = HtmlEscape(`"$Clang" --version`); 1745*67e74705SXin Li 1746*67e74705SXin Li# Determine where results go. 1747*67e74705SXin Li$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); 1748*67e74705SXin Li 1749*67e74705SXin Li# Determine the output directory for the HTML reports. 1750*67e74705SXin Limy $BaseDir = $Options{OutputDir}; 1751*67e74705SXin Li$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir}); 1752*67e74705SXin Li 1753*67e74705SXin Li# Determine the location of ccc-analyzer. 1754*67e74705SXin Limy $AbsRealBin = Cwd::realpath($RealBin); 1755*67e74705SXin Limy $Cmd = "$AbsRealBin/../libexec/ccc-analyzer"; 1756*67e74705SXin Limy $CmdCXX = "$AbsRealBin/../libexec/c++-analyzer"; 1757*67e74705SXin Li 1758*67e74705SXin Li# Portability: use less strict but portable check -e (file exists) instead of 1759*67e74705SXin Li# non-portable -x (file is executable). On some windows ports -x just checks 1760*67e74705SXin Li# file extension to determine if a file is executable (see Perl language 1761*67e74705SXin Li# reference, perlport) 1762*67e74705SXin Liif (!defined $Cmd || ! -e $Cmd) { 1763*67e74705SXin Li $Cmd = "$AbsRealBin/ccc-analyzer"; 1764*67e74705SXin Li DieDiag("'ccc-analyzer' does not exist at '$Cmd'\n") if(! -e $Cmd); 1765*67e74705SXin Li} 1766*67e74705SXin Liif (!defined $CmdCXX || ! -e $CmdCXX) { 1767*67e74705SXin Li $CmdCXX = "$AbsRealBin/c++-analyzer"; 1768*67e74705SXin Li DieDiag("'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -e $CmdCXX); 1769*67e74705SXin Li} 1770*67e74705SXin Li 1771*67e74705SXin LiDiag("Using '$Clang' for static analysis\n"); 1772*67e74705SXin Li 1773*67e74705SXin LiSetHtmlEnv(\@ARGV, $Options{OutputDir}); 1774*67e74705SXin Li 1775*67e74705SXin Limy @AnalysesToRun; 1776*67e74705SXin Liforeach (sort { $Options{EnableCheckers}{$a} <=> $Options{EnableCheckers}{$b} } 1777*67e74705SXin Li keys %{$Options{EnableCheckers}}) { 1778*67e74705SXin Li # Push checkers in order they were enabled. 1779*67e74705SXin Li push @AnalysesToRun, "-analyzer-checker", $_; 1780*67e74705SXin Li} 1781*67e74705SXin Liforeach (sort { $Options{DisableCheckers}{$a} <=> $Options{DisableCheckers}{$b} } 1782*67e74705SXin Li keys %{$Options{DisableCheckers}}) { 1783*67e74705SXin Li # Push checkers in order they were disabled. 1784*67e74705SXin Li push @AnalysesToRun, "-analyzer-disable-checker", $_; 1785*67e74705SXin Li} 1786*67e74705SXin Liif ($Options{AnalyzeHeaders}) { push @AnalysesToRun, "-analyzer-opt-analyze-headers"; } 1787*67e74705SXin Liif ($Options{AnalyzerStats}) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; } 1788*67e74705SXin Liif ($Options{MaxLoop} > 0) { push @AnalysesToRun, "-analyzer-max-loop $Options{MaxLoop}"; } 1789*67e74705SXin Li 1790*67e74705SXin Li# Delay setting up other environment variables in case we can do true 1791*67e74705SXin Li# interposition. 1792*67e74705SXin Limy $CCC_ANALYZER_ANALYSIS = join ' ', @AnalysesToRun; 1793*67e74705SXin Limy $CCC_ANALYZER_PLUGINS = join ' ', map { "-load ".$_ } @{$Options{PluginsToLoad}}; 1794*67e74705SXin Limy $CCC_ANALYZER_CONFIG = join ' ', map { "-analyzer-config ".$_ } @{$Options{ConfigOptions}}; 1795*67e74705SXin Limy %EnvVars = ( 1796*67e74705SXin Li 'CC' => $Cmd, 1797*67e74705SXin Li 'CXX' => $CmdCXX, 1798*67e74705SXin Li 'CLANG' => $Clang, 1799*67e74705SXin Li 'CLANG_CXX' => $ClangCXX, 1800*67e74705SXin Li 'VERBOSE' => $Options{Verbose}, 1801*67e74705SXin Li 'CCC_ANALYZER_ANALYSIS' => $CCC_ANALYZER_ANALYSIS, 1802*67e74705SXin Li 'CCC_ANALYZER_PLUGINS' => $CCC_ANALYZER_PLUGINS, 1803*67e74705SXin Li 'CCC_ANALYZER_CONFIG' => $CCC_ANALYZER_CONFIG, 1804*67e74705SXin Li 'OUTPUT_DIR' => $Options{OutputDir}, 1805*67e74705SXin Li 'CCC_CC' => $Options{UseCC}, 1806*67e74705SXin Li 'CCC_CXX' => $Options{UseCXX}, 1807*67e74705SXin Li 'CCC_REPORT_FAILURES' => $Options{ReportFailures}, 1808*67e74705SXin Li 'CCC_ANALYZER_STORE_MODEL' => $Options{StoreModel}, 1809*67e74705SXin Li 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, 1810*67e74705SXin Li 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, 1811*67e74705SXin Li 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, 1812*67e74705SXin Li 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}, 1813*67e74705SXin Li 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode} 1814*67e74705SXin Li); 1815*67e74705SXin Li 1816*67e74705SXin Li# Run the build. 1817*67e74705SXin Limy $ExitStatus = RunBuildCommand(\@ARGV, $Options{IgnoreErrors}, $Cmd, $CmdCXX, 1818*67e74705SXin Li \%EnvVars); 1819*67e74705SXin Li 1820*67e74705SXin Liif (defined $Options{OutputFormat}) { 1821*67e74705SXin Li if ($Options{OutputFormat} =~ /plist/) { 1822*67e74705SXin Li Diag "Analysis run complete.\n"; 1823*67e74705SXin Li Diag "Analysis results (plist files) deposited in '$Options{OutputDir}'\n"; 1824*67e74705SXin Li } 1825*67e74705SXin Li if ($Options{OutputFormat} =~ /html/) { 1826*67e74705SXin Li # Postprocess the HTML directory. 1827*67e74705SXin Li my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir, 1828*67e74705SXin Li $Options{AnalyzerStats}, $Options{KeepEmpty}); 1829*67e74705SXin Li 1830*67e74705SXin Li if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") { 1831*67e74705SXin Li Diag "Analysis run complete.\n"; 1832*67e74705SXin Li Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n"; 1833*67e74705SXin Li my $ScanView = Cwd::realpath("$RealBin/scan-view"); 1834*67e74705SXin Li if (! -x $ScanView) { $ScanView = "scan-view"; } 1835*67e74705SXin Li if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); } 1836*67e74705SXin Li exec $ScanView, "$Options{OutputDir}"; 1837*67e74705SXin Li } 1838*67e74705SXin Li 1839*67e74705SXin Li if ($Options{ExitStatusFoundBugs}) { 1840*67e74705SXin Li exit 1 if ($NumBugs > 0); 1841*67e74705SXin Li exit 0; 1842*67e74705SXin Li } 1843*67e74705SXin Li } 1844*67e74705SXin Li} 1845*67e74705SXin Li 1846*67e74705SXin Liexit $ExitStatus; 1847