From 3772afb662d67528957eedab53a8371c4bfc12b2 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 30 May 2009 01:41:32 +0000 Subject: Import static analysis scripts from llvm/clang svn path=/trunk/tools/buildsystem/; revision=7647 --- llvm/LICENSE.TXT | 69 +++ llvm/ccc-analyzer | 617 ++++++++++++++++++++++++++ llvm/scan-build | 1278 +++++++++++++++++++++++++++++++++++++++++++++++++++++ llvm/sorttable.js | 493 +++++++++++++++++++++ 4 files changed, 2457 insertions(+) create mode 100644 llvm/LICENSE.TXT create mode 100755 llvm/ccc-analyzer create mode 100755 llvm/scan-build create mode 100644 llvm/sorttable.js (limited to 'llvm') diff --git a/llvm/LICENSE.TXT b/llvm/LICENSE.TXT new file mode 100644 index 0000000..0dca8ce --- /dev/null +++ b/llvm/LICENSE.TXT @@ -0,0 +1,69 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2009 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== +Copyrights and Licenses for Third Party Software Distributed with LLVM: +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- +System Library llvm/lib/System +Autoconf llvm/autoconf + llvm/projects/ModuleMaker/autoconf + llvm/projects/sample/autoconf +CellSPU backend llvm/lib/Target/CellSPU/README.txt +Google Test llvm/utils/unittest/googletest diff --git a/llvm/ccc-analyzer b/llvm/ccc-analyzer new file mode 100755 index 0000000..e4bf415 --- /dev/null +++ b/llvm/ccc-analyzer @@ -0,0 +1,617 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to interpose between the build system and gcc. It invokes +# both gcc and the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use Cwd qw/ getcwd abs_path /; +use File::Temp qw/ tempfile /; +use File::Path qw / mkpath /; +use File::Basename; +use Text::ParseWords; + +my $CC = $ENV{'CCC_CC'}; +if (!defined $CC) { $CC = "gcc"; } +my $CleanupFile; +my $ResultFile; + +# Remove any stale files at exit. +END { + if (defined $CleanupFile && -z $CleanupFile) { + `rm -f $CleanupFile`; + } +} + +##----------------------------------------------------------------------------## +# Process Clang Crashes. +##----------------------------------------------------------------------------## + +sub GetPPExt { + my $Lang = shift; + if ($Lang =~ /objective-c/) { return ".mi"; } + return ".i"; +} + +# Set this to 1 if we want to include 'parser rejects' files. +my $IncludeParserRejects = 0; +my $ParserRejects = "Parser Rejects"; + +my $AttributeIgnored = "Attribute Ignored"; + +sub ProcessClangFailure { + my ($ClangCC, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_; + my $Dir = "$HtmlDir/failures"; + mkpath $Dir; + + my $prefix = "clang_crash"; + if ($ErrorType eq $ParserRejects) { + $prefix = "clang_parser_rejects"; + } + elsif ($ErrorType eq $AttributeIgnored) { + $prefix = "clang_attribute_ignored"; + } + + # Generate the preprocessed file with cc (i.e., gcc). + my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX", + SUFFIX => GetPPExt($Lang), + DIR => $Dir); + + system $CC, @$Args, "-E", "-o", $PPFile; + close ($PPH); + + # Generate the preprocessed file with clang. + my $PPFile_Clang = $PPFile; + $PPFile_Clang =~ s/[.](.+)$/.clang.$1/; + system $ClangCC, @$Args, "-E", "-o", "$PPFile_Clang"; + + # Create the info file. + open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n"; + print OUT abs_path($file), "\n"; + print OUT "$ErrorType\n"; + print OUT "@$Args\n"; + close OUT; + `uname -a >> $PPFile.info.txt 2>&1`; + `$CC -v >> $PPFile.info.txt 2>&1`; + system 'mv',$ofile,"$PPFile.stderr.txt"; + return (basename $PPFile); +} + +##----------------------------------------------------------------------------## +# Running the analyzer. +##----------------------------------------------------------------------------## + +# Determine what clang executable to use. +my $Clang = $ENV{'CLANG'}; +if (!defined $Clang) { $Clang = 'clang'; } + +sub GetCCArgs { + my $Args = shift; + + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Clang, "-###", "-fsyntax-only", @$Args; + } + close(TO_PARENT); + my $line; + while () { + next if (!/clang-cc/); + $line = $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + + die "could not find clang-cc line\n" if (!defined $line); + # Strip the newline and initial whitspace + chomp $line; + $line =~ s/^\s+//; + + my @items = quotewords('\s+', 1, $line); + for (my $i = 0 ; $ i < scalar(@items); ++$i) { + $items[$i] =~ s/^\"//; + $items[$i] =~ s/\"$//; + } + my $cmd = shift @items; + die "cannot find 'clang-cc' in 'clang' command\n" if (!($cmd =~ /clang-cc/)); + return \@items; +} + +sub Analyze { + my ($ClangCC, $Args, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir, + $file, $Analyses) = @_; + + $Args = GetCCArgs($Args); + + # Skip anything related to C++. + return if ($Lang =~ /c[+][+]/); + + my $RunAnalyzer = 0; + my $Cmd; + my @CmdArgs; + my @CmdArgsSansAnalyses; + + if ($Lang =~ /header/) { + exit 0 if (!defined ($Output)); + $Cmd = 'cp'; + push @CmdArgs,$file; + # Remove the PCH extension. + $Output =~ s/[.]gch$//; + push @CmdArgs,$Output; + @CmdArgsSansAnalyses = @CmdArgs; + } + else { + $Cmd = $ClangCC; + push @CmdArgs,'-DIBOutlet=__attribute__((iboutlet))'; + push @CmdArgs,@$Args; + @CmdArgsSansAnalyses = @CmdArgs; + push @CmdArgs,'-analyze'; + push @CmdArgs,"-analyzer-display-progress"; + push @CmdArgs,"-analyzer-eagerly-assume"; + push @CmdArgs,(split /\s/,$Analyses); + $RunAnalyzer = 1; + } + + # Add the analysis arguments passed down from scan-build. + foreach my $Arg (@$AnalyzeArgs) { + push @CmdArgs, $Arg; + } + + my @PrintArgs; + my $dir; + + if ($Verbose) { + $dir = getcwd(); + print STDERR "\n[LOCATION]: $dir\n"; + push @PrintArgs,"'$Cmd'"; + foreach my $arg (@CmdArgs) { push @PrintArgs,"\'$arg\'"; } + } + + if ($Verbose == 1) { + # We MUST print to stderr. Some clients use the stdout output of + # gcc for various purposes. + print STDERR join(' ',@PrintArgs); + print STDERR "\n"; + } + elsif ($Verbose == 2) { + print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; + } + + if ($RunAnalyzer) { + if (defined $ResultFile) { + push @CmdArgs,'-o'; + push @CmdArgs, $ResultFile; + } + elsif (defined $HtmlDir) { + push @CmdArgs,'-o'; + push @CmdArgs, $HtmlDir; + } + } + + if (defined $ENV{'CCC_UBI'}) { + push @CmdArgs,"--analyzer-viz-egraph-ubigraph"; + } + + # Capture the STDERR of clang and send it to a temporary file. + # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR. + # We save the output file in the 'crashes' directory if clang encounters + # any problems with the file. + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Cmd, @CmdArgs; + } + + close TO_PARENT; + my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir); + + while () { + print $ofh $_; + print STDERR $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + my $Result = $?; + + # Did the command die because of a signal? + if ($Result & 127 and $Cmd eq $ClangCC and defined $HtmlDir) { + ProcessClangFailure($ClangCC, $Lang, $file, \@CmdArgsSansAnalyses, $HtmlDir, + "Crash", $ofile); + } + elsif ($Result) { + if ($IncludeParserRejects && !($file =~/conftest/)) { + ProcessClangFailure($ClangCC, $Lang, $file, \@CmdArgsSansAnalyses, $HtmlDir, + $ParserRejects, $ofile); + } + } + else { + # Check if there were any unhandled attributes. + if (open(CHILD, $ofile)) { + my %attributes_not_handled; + + # Don't flag warnings about the following attributes that we + # know are currently not supported by Clang. + $attributes_not_handled{"cdecl"} = 1; + + my $ppfile; + while () { + next if (! /warning: '([^\']+)' attribute ignored/); + + # Have we already spotted this unhandled attribute? + next if (defined $attributes_not_handled{$1}); + $attributes_not_handled{$1} = 1; + + # Get the name of the attribute file. + my $dir = "$HtmlDir/failures"; + my $afile = "$dir/attribute_ignored_$1.txt"; + + # Only create another preprocessed file if the attribute file + # doesn't exist yet. + next if (-e $afile); + + # Add this file to the list of files that contained this attribute. + # Generate a preprocessed file if we haven't already. + if (!(defined $ppfile)) { + $ppfile = ProcessClangFailure($ClangCC, $Lang, $file, + \@CmdArgsSansAnalyses, + $HtmlDir, $AttributeIgnored, $ofile); + } + + mkpath $dir; + open(AFILE, ">$afile"); + print AFILE "$ppfile\n"; + close(AFILE); + } + close CHILD; + } + } + + `rm -f $ofile`; +} + +##----------------------------------------------------------------------------## +# Lookup tables. +##----------------------------------------------------------------------------## + +my %CompileOptionMap = ( + '-nostdinc' => 0, + '-fblocks' => 0, + '-fobjc-gc-only' => 0, + '-fobjc-gc' => 0, + '-ffreestanding' => 0, + '-include' => 1, + '-idirafter' => 1, + '-iprefix' => 1, + '-iquote' => 1, + '-isystem' => 1, + '-iwithprefix' => 1, + '-iwithprefixbefore' => 1 +); + +my %LinkerOptionMap = ( + '-framework' => 1 +); + +my %CompilerLinkerOptionMap = ( + '-isysroot' => 1, + '-arch' => 1, + '-v' => 0, + '-fpascal-strings' => 0, + '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' + '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '=' +); + +my %IgnoredOptionMap = ( + '-MT' => 1, # Ignore these preprocessor options. + '-MF' => 1, + + '-fsyntax-only' => 0, + '-save-temps' => 0, + '-install_name' => 1, + '-exported_symbols_list' => 1, + '-current_version' => 1, + '-compatibility_version' => 1, + '-init' => 1, + '-e' => 1, + '-seg1addr' => 1, + '-bundle_loader' => 1, + '-multiply_defined' => 1, + '-sectorder' => 3, + '--param' => 1, + '-u' => 1 +); + +my %LangMap = ( + 'c' => 'c', + 'cpp' => 'c++', + 'cc' => 'c++', + 'i' => 'c-cpp-output', + 'm' => 'objective-c', + 'mi' => 'objective-c-cpp-output' +); + +my %UniqueOptions = ( + '-isysroot' => 0 +); + +my %LangsAccepted = ( + "objective-c" => 1, + "c" => 1 +); + +##----------------------------------------------------------------------------## +# Main Logic. +##----------------------------------------------------------------------------## + +my $Action = 'link'; +my @CompileOpts; +my @LinkOpts; +my @Files; +my $Lang; +my $Output; +my %Uniqued; + +# Forward arguments to gcc. +my $Status = system($CC,@ARGV); +if ($Status) { exit($Status >> 8); } + +# Get the analysis options. +my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; +if (!defined($Analyses)) { $Analyses = '-checker-cfref'; } + +# Get the store model. +my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; +if (!defined $StoreModel) { $StoreModel = "basic"; } + +# Get the constraints engine. +my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; +if (!defined $ConstraintsModel) { $ConstraintsModel = "range"; } + +# Get the output format. +my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'}; +if (!defined $OutputFormat) { $OutputFormat = "html"; } + +# Determine the level of verbosity. +my $Verbose = 0; +if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } +if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } + +# Determine what clang-cc executable to use. +my $ClangCC = $ENV{'CLANG_CC'}; +if (!defined $ClangCC) { $ClangCC = 'clang-cc'; } + +# Get the HTML output directory. +my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; + +my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); +my %ArchsSeen; +my $HadArch = 0; + +# Process the arguments. +foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { + my $Arg = $ARGV[$i]; + my ($ArgKey) = split /=/,$Arg,2; + + # Modes ccc-analyzer supports + if ($Arg eq '-E') { $Action = 'preprocess'; } + elsif ($Arg eq '-c') { $Action = 'compile'; } + elsif ($Arg =~ /^-print-prog-name/) { exit 0; } + + # Specially handle duplicate cases of -arch + if ($Arg eq "-arch") { + my $arch = $ARGV[$i+1]; + # We don't want to process 'ppc' because of Clang's lack of support + # for Altivec (also some #defines won't likely be defined correctly, etc.) + if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; } + $HadArch = 1; + ++$i; + next; + } + + # Options with possible arguments that should pass through to compiler. + if (defined $CompileOptionMap{$ArgKey}) { + my $Cnt = $CompileOptionMap{$ArgKey}; + push @CompileOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to linker. + if (defined $LinkerOptionMap{$ArgKey}) { + my $Cnt = $LinkerOptionMap{$ArgKey}; + push @LinkOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to both compiler + # and the linker. + if (defined $CompilerLinkerOptionMap{$ArgKey}) { + my $Cnt = $CompilerLinkerOptionMap{$ArgKey}; + + # Check if this is an option that should have a unique value, and if so + # determine if the value was checked before. + if ($UniqueOptions{$Arg}) { + if (defined $Uniqued{$Arg}) { + $i += $Cnt; + next; + } + $Uniqued{$Arg} = 1; + } + + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + + while ($Cnt > 0) { + ++$i; --$Cnt; + push @CompileOpts, $ARGV[$i]; + push @LinkOpts, $ARGV[$i]; + } + next; + } + + # Ignored options. + if (defined $IgnoredOptionMap{$ArgKey}) { + my $Cnt = $IgnoredOptionMap{$ArgKey}; + while ($Cnt > 0) { + ++$i; --$Cnt; + } + next; + } + + # Compile mode flags. + if ($Arg =~ /^-[D,I,U](.*)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + next; + } + + # Language. + if ($Arg eq '-x') { + $Lang = $ARGV[$i+1]; + ++$i; next; + } + + # Output file. + if ($Arg eq '-o') { + ++$i; + $Output = $ARGV[$i]; + next; + } + + # Get the link mode. + if ($Arg =~ /^-[l,L,O]/) { + if ($Arg eq '-O') { push @LinkOpts,'-O1'; } + elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } + else { push @LinkOpts,$Arg; } + next; + } + + if ($Arg =~ /^-std=/) { + push @CompileOpts,$Arg; + next; + } + +# if ($Arg =~ /^-f/) { +# # FIXME: Not sure if the remaining -fxxxx options have no arguments. +# push @CompileOpts,$Arg; +# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts. +# } + + # Get the compiler/link mode. + if ($Arg =~ /^-F(.+)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + push @LinkOpts,$Tmp; + next; + } + + # Input files. + if ($Arg eq '-filelist') { + # FIXME: Make sure we aren't walking off the end. + open(IN, $ARGV[$i+1]); + while () { s/\015?\012//; push @Files,$_; } + close(IN); + ++$i; next; + } + + if (!($Arg =~ /^-/)) { + push @Files,$Arg; next; + } +} + +if ($Action eq 'compile' or $Action eq 'link') { + my @Archs = keys %ArchsSeen; + # Skip the file if we don't support the architectures specified. + exit 0 if ($HadArch && scalar(@Archs) == 0); + + foreach my $file (@Files) { + # Determine the language for the file. + my $FileLang = $Lang; + + if (!defined($FileLang)) { + # Infer the language from the extension. + if ($file =~ /[.]([^.]+)$/) { + $FileLang = $LangMap{$1}; + } + } + + next if (!defined $FileLang); + next if (!defined $LangsAccepted{$FileLang}); + + my @CmdArgs; + my @AnalyzeArgs; + + if ($FileLang ne 'unknown') { + push @CmdArgs,'-x'; + push @CmdArgs,$FileLang; + } + + if (defined $StoreModel) { + push @AnalyzeArgs, "-analyzer-store=$StoreModel"; + } + + if (defined $ConstraintsModel) { + push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; + } + + if (defined $OutputFormat) { + push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; + if ($OutputFormat eq "plist") { + # Change "Output" to be a file. + my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist", + DIR => $HtmlDir); + $ResultFile = $f; + $CleanupFile = $f; + } + } + + push @CmdArgs,@CompileOpts; + push @CmdArgs,$file; + + if (scalar @Archs) { + foreach my $arch (@Archs) { + my @NewArgs; + push @NewArgs, '-arch'; + push @NewArgs, $arch; + push @NewArgs, @CmdArgs; + Analyze($ClangCC, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file, $Analyses); + } + } + else { + Analyze($ClangCC, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file, $Analyses); + } + } +} + +exit($Status >> 8); + diff --git a/llvm/scan-build b/llvm/scan-build new file mode 100755 index 0000000..5835628 --- /dev/null +++ b/llvm/scan-build @@ -0,0 +1,1278 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to wrap a build so that all calls to gcc are intercepted +# and piped to the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin qw($RealBin); +use Digest::MD5; +use File::Basename; +use Term::ANSIColor; +use Term::ANSIColor qw(:constants); +use Cwd qw/ getcwd abs_path /; +use Sys::Hostname; + +my $Verbose = 0; # Verbose output from this script. +my $Prog = "scan-build"; +my $BuildName; +my $BuildDate; +my $CXX; # Leave undefined initially. + +my $TERM = $ENV{'TERM'}; +my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT + and defined $ENV{'SCAN_BUILD_COLOR'}); + +my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); +my $HostName = HtmlEscape(hostname() || 'unknown'); +my $CurrentDir = HtmlEscape(getcwd()); +my $CurrentDirSuffix = basename($CurrentDir); + +my $CmdArgs; + +my $HtmlTitle; + +my $Date = localtime(); + +##----------------------------------------------------------------------------## +# Diagnostics +##----------------------------------------------------------------------------## + +sub Diag { + if ($UseColor) { + print BOLD, MAGENTA "$Prog: @_"; + print RESET; + } + else { + print "$Prog: @_"; + } +} + +sub DiagCrashes { + my $Dir = shift; + Diag ("The analyzer encountered problems on some source files.\n"); + Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); + Diag ("Please consider submitting a bug report using these files:\n"); + Diag (" http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs\n") +} + +sub DieDiag { + if ($UseColor) { + print BOLD, RED "$Prog: "; + print RESET, RED @_; + print RESET; + } + else { + print "$Prog: ", @_; + } + exit(0); +} + +##----------------------------------------------------------------------------## +# Some initial preprocessing of Clang options. +##----------------------------------------------------------------------------## + +# First, look for 'clang-cc' in libexec. +my $ClangCCSB = Cwd::realpath("$RealBin/libexec/clang-cc"); +# Second, look for 'clang-cc' in the same directory as scan-build. +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = Cwd::realpath("$RealBin/clang-cc"); +} +# Third, look for 'clang-cc' in ../libexec +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = Cwd::realpath("$RealBin/../libexec/clang-cc"); +} +# Finally, default to looking for 'clang-cc' in the path. +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = "clang-cc"; +} +my $ClangCC = $ClangCCSB; + +# Now find 'clang' +my $ClangSB = Cwd::realpath("$RealBin/bin/clang"); +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = Cwd::realpath("$RealBin/clang"); +} +# Third, look for 'clang' in ../bin +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = Cwd::realpath("$RealBin/../bin/clang"); +} +# Finally, default to looking for 'clang-cc' in the path. +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = "clang"; +} +my $Clang = $ClangSB; + + +my %AvailableAnalyses; + +# Query clang for analysis options. +open(PIPE, "-|", $ClangCC, "--help") or + DieDiag("Cannot execute '$ClangCC'\n"); + +my $FoundAnalysis = 0; + +while() { + if ($FoundAnalysis == 0) { + if (/Checks and Analyses/) { + $FoundAnalysis = 1; + } + next; + } + + if (/^\s\s\s\s([^\s]+)\s(.+)$/) { + next if ($1 =~ /-dump/ or $1 =~ /-view/ + or $1 =~ /-checker-simple/ or $1 =~ /-warn-uninit/); + + $AvailableAnalyses{$1} = $2; + next; + } + last; +} + +close (PIPE); + +my %AnalysesDefaultEnabled = ( + '-warn-dead-stores' => 1, + '-checker-cfref' => 1, + '-warn-objc-methodsigs' => 1, + # Do not enable the missing -dealloc check by default. + # '-warn-objc-missing-dealloc' => 1, + '-warn-objc-unused-ivars' => 1, +); + +##----------------------------------------------------------------------------## +# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. +##----------------------------------------------------------------------------## + +sub GetHTMLRunDir { + + die "Not enough arguments." if (@_ == 0); + my $Dir = shift @_; + + my $TmpMode = 0; + if (!defined $Dir) { + if (`uname` =~ /Darwin/) { + $Dir = $ENV{'TMPDIR'}; + if (!defined $Dir) { $Dir = "/tmp"; } + } + else { + $Dir = "/tmp"; + } + + $TmpMode = 1; + } + + # Chop off any trailing '/' characters. + while ($Dir =~ /\/$/) { chop $Dir; } + + # Get current date and time. + + my @CurrentTime = localtime(); + + my $year = $CurrentTime[5] + 1900; + my $day = $CurrentTime[3]; + my $month = $CurrentTime[4] + 1; + + my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); + + # Determine the run number. + + my $RunNumber; + + if (-d $Dir) { + + if (! -r $Dir) { + DieDiag("directory '$Dir' exists but is not readable.\n"); + } + + # Iterate over all files in the specified directory. + + my $max = 0; + + opendir(DIR, $Dir); + my @FILES = grep { -d "$Dir/$_" } readdir(DIR); + closedir(DIR); + + foreach my $f (@FILES) { + + # Strip the prefix '$Prog-' if we are dumping files to /tmp. + if ($TmpMode) { + next if (!($f =~ /^$Prog-(.+)/)); + $f = $1; + } + + + my @x = split/-/, $f; + next if (scalar(@x) != 4); + next if ($x[0] != $year); + next if ($x[1] != $month); + next if ($x[2] != $day); + + if ($x[3] > $max) { + $max = $x[3]; + } + } + + $RunNumber = $max + 1; + } + else { + + if (-x $Dir) { + DieDiag("'$Dir' exists but is not a directory.\n"); + } + + if ($TmpMode) { + DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); + } + + # $Dir does not exist. It will be automatically created by the + # clang driver. Set the run number to 1. + + $RunNumber = 1; + } + + die "RunNumber must be defined!" if (!defined $RunNumber); + + # Append the run number. + my $NewDir; + if ($TmpMode) { + $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; + } + else { + $NewDir = "$Dir/$DateString-$RunNumber"; + } + system 'mkdir','-p',$NewDir; + return $NewDir; +} + +sub SetHtmlEnv { + + die "Wrong number of arguments." if (scalar(@_) != 2); + + my $Args = shift; + my $Dir = shift; + + die "No build command." if (scalar(@$Args) == 0); + + my $Cmd = $$Args[0]; + + if ($Cmd =~ /configure/) { + return; + } + + if ($Verbose) { + Diag("Emitting reports for this run to '$Dir'.\n"); + } + + $ENV{'CCC_ANALYZER_HTML'} = $Dir; +} + +##----------------------------------------------------------------------------## +# ComputeDigest - Compute a digest of the specified file. +##----------------------------------------------------------------------------## + +sub ComputeDigest { + my $FName = shift; + DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); + + # Use Digest::MD5. We don't have to be cryptographically secure. We're + # just looking for duplicate files that come from a non-malicious source. + # We use Digest::MD5 because it is a standard Perl module that should + # come bundled on most systems. + open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); + binmode FILE; + my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; + close(FILE); + + # Return the digest. + return $Result; +} + +##----------------------------------------------------------------------------## +# UpdatePrefix - Compute the common prefix of files. +##----------------------------------------------------------------------------## + +my $Prefix; + +sub UpdatePrefix { + my $x = shift; + my $y = basename($x); + $x =~ s/\Q$y\E$//; + + if (!defined $Prefix) { + $Prefix = $x; + return; + } + + chop $Prefix while (!($x =~ /^\Q$Prefix/)); +} + +sub GetPrefix { + return $Prefix; +} + +##----------------------------------------------------------------------------## +# UpdateInFilePath - Update the path in the report file. +##----------------------------------------------------------------------------## + +sub UpdateInFilePath { + my $fname = shift; + my $regex = shift; + my $newtext = shift; + + open (RIN, $fname) or die "cannot open $fname"; + open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; + + while () { + s/$regex/$newtext/; + print ROUT $_; + } + + close (ROUT); + close (RIN); + system("mv", "$fname.tmp", $fname); +} + +##----------------------------------------------------------------------------## +# ScanFile - Scan a report file for various identifying attributes. +##----------------------------------------------------------------------------## + +# Sometimes a source file is scanned more than once, and thus produces +# multiple error reports. We use a cache to solve this problem. + +my %AlreadyScanned; + +sub ScanFile { + + my $Index = shift; + my $Dir = shift; + my $FName = shift; + + # Compute a digest for the report file. Determine if we have already + # scanned a file that looks just like it. + + my $digest = ComputeDigest("$Dir/$FName"); + + if (defined $AlreadyScanned{$digest}) { + # Redundant file. Remove it. + system ("rm", "-f", "$Dir/$FName"); + return; + } + + $AlreadyScanned{$digest} = 1; + + # At this point the report file is not world readable. Make it happen. + system ("chmod", "644", "$Dir/$FName"); + + # Scan the report file for tags. + open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); + + my $BugType = ""; + my $BugFile = ""; + my $BugCategory; + my $BugPathLength = 1; + my $BugLine = 0; + my $found = 0; + + while () { + + last if ($found == 5); + + if (/$/) { + $BugType = $1; + ++$found; + } + elsif (/$/) { + $BugFile = abs_path($1); + UpdatePrefix($BugFile); + ++$found; + } + elsif (/$/) { + $BugPathLength = $1; + ++$found; + } + elsif (/$/) { + $BugLine = $1; + ++$found; + } + elsif (/$/) { + $BugCategory = $1; + ++$found; + } + } + + close(IN); + + if (!defined $BugCategory) { + $BugCategory = "Other"; + } + + push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine, + $BugPathLength ]; +} + +##----------------------------------------------------------------------------## +# CopyFiles - Copy resource files to target directory. +##----------------------------------------------------------------------------## + +sub CopyFiles { + + my $Dir = shift; + + my $JS = Cwd::realpath("$RealBin/sorttable.js"); + + DieDiag("Cannot find 'sorttable.js'.\n") + if (! -r $JS); + + system ("cp", $JS, "$Dir"); + + DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") + if (! -r "$Dir/sorttable.js"); + + my $CSS = Cwd::realpath("$RealBin/scanview.css"); + + DieDiag("Cannot find 'scanview.css'.\n") + if (! -r $CSS); + + system ("cp", $CSS, "$Dir"); + + DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") + if (! -r $CSS); +} + +##----------------------------------------------------------------------------## +# Postprocess - Postprocess the results of an analysis scan. +##----------------------------------------------------------------------------## + +sub Postprocess { + + my $Dir = shift; + my $BaseDir = shift; + + die "No directory specified." if (!defined $Dir); + + if (! -d $Dir) { + Diag("No bugs found.\n"); + return 0; + } + + opendir(DIR, $Dir); + my @files = grep { /^report-.*\.html$/ } readdir(DIR); + closedir(DIR); + + if (scalar(@files) == 0 and ! -e "$Dir/failures") { + Diag("Removing directory '$Dir' because it contains no reports.\n"); + system ("rm", "-fR", $Dir); + return 0; + } + + # Scan each report file and build an index. + my @Index; + foreach my $file (@files) { ScanFile(\@Index, $Dir, $file); } + + # Scan the failures directory and use the information in the .info files + # to update the common prefix directory. + my @failures; + my @attributes_ignored; + if (-d "$Dir/failures") { + opendir(DIR, "$Dir/failures"); + @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); + closedir(DIR); + opendir(DIR, "$Dir/failures"); + @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); + closedir(DIR); + foreach my $file (@failures) { + open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); + my $Path = ; + if (defined $Path) { UpdatePrefix($Path); } + close IN; + } + } + + # Generate an index.html file. + my $FName = "$Dir/index.html"; + open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); + + # Print out the header. + +print OUT < + +${HtmlTitle} + + + + + + +

${HtmlTitle}

+ + + + + + +ENDTEXT + +print OUT "\n" + if (defined($BuildName) && defined($BuildDate)); + +print OUT < +ENDTEXT + + if (scalar(@files)) { + # Print out the summary table. + my %Totals; + + for my $row ( @Index ) { + my $bug_type = ($row->[2]); + my $bug_category = ($row->[1]); + my $key = "$bug_category:$bug_type"; + + if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } + else { $Totals{$key}->[0]++; } + } + + print OUT "

Bug Summary

"; + + if (defined $BuildName) { + print OUT "\n

Results in this analysis run are based on analyzer build $BuildName.

\n" + } + + my $TotalBugs = scalar(@Index); +print OUT < +
+ +ENDTEXT + + my $last_category; + + for my $key ( + sort { + my $x = $Totals{$a}; + my $y = $Totals{$b}; + my $res = $x->[1] cmp $y->[1]; + $res = $x->[2] cmp $y->[2] if ($res == 0); + $res + } keys %Totals ) + { + my $val = $Totals{$key}; + my $category = $val->[1]; + if (!defined $last_category or $last_category ne $category) { + $last_category = $category; + print OUT "\n"; + } + my $x = lc $key; + $x =~ s/[ ,'":\/()]+/_/g; + print OUT "\n"; + } + + # Print out the table of errors. + +print OUT < +

Reports

+ +
User:${UserName}\@${HostName}
Working Directory:${CurrentDir}
Command Line:${CmdArgs}
Date:${Date}
Version:${BuildName} (${BuildDate})
Bug TypeQuantityDisplay?
All Bugs$TotalBugs
$category
"; + print OUT $val->[2]; + print OUT ""; + print OUT $val->[0]; + print OUT "
+ + + + + + + + + + +ENDTEXT + + my $prefix = GetPrefix(); + my $regex; + my $InFileRegex; + my $InFilePrefix = "File:"; + print OUT ""; + print OUT ""; + + # Update the file prefix. + my $fname = $row->[3]; + + if (defined $regex) { + $fname =~ s/$regex//; + UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) + } + + print OUT ""; + + # Print out the quantities. + for my $j ( 4 .. 5 ) { + print OUT ""; + } + + # Print the rest of the columns. + for (my $j = 6; $j <= $#{$row}; ++$j) { + print OUT "" + } + + # Emit the "View" link. + print OUT ""; + + # Emit REPORTBUG markers. + print OUT "\n\n"; + + # End the row. + print OUT "\n"; + } + + print OUT "\n
Bug GroupBug Type ▾FileLinePath Length
"; + + if (defined $prefix) { + $regex = qr/^\Q$prefix\E/is; + $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; + } + + for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { + my $x = "$row->[1]:$row->[2]"; + $x = lc $x; + $x =~ s/[ ,'":\/()]+/_/g; + + my $ReportFile = $row->[0]; + + print OUT "
"; + print OUT $row->[1]; + print OUT ""; + print OUT $row->[2]; + print OUT ""; + my @fname = split /\//,$fname; + if ($#fname > 0) { + while ($#fname >= 0) { + my $x = shift @fname; + print OUT $x; + if ($#fname >= 0) { + print OUT " /"; + } + } + } + else { + print OUT $fname; + } + print OUT "$row->[$j]$row->[$j]View Report
\n\n"; + } + + if (scalar (@failures) || scalar(@attributes_ignored)) { + print OUT "

Analyzer Failures

\n"; + + if (scalar @attributes_ignored) { + print OUT "The analyzer's parser ignored the following attributes:

\n"; + print OUT "\n"; + print OUT "\n"; + foreach my $file (sort @attributes_ignored) { + die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); + my $attribute = $1; + # Open the attribute file to get the first file that failed. + next if (!open (ATTR, "$Dir/failures/$file")); + my $ppfile = ; + chomp $ppfile; + close ATTR; + next if (! -e "$Dir/failures/$ppfile"); + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$ppfile.info.txt") or + die "Cannot open $Dir/failures/$ppfile.info.txt\n"; + my $srcfile = ; + chomp $srcfile; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " \n"; + } + print OUT "
AttributeSource FilePreprocessed FileSTDERR Output
$attribute$srcfile$ppfile$ppfile.stderr.txt
\n"; + } + + if (scalar @failures) { + print OUT "

The analyzer had problems processing the following files:

\n"; + print OUT "\n"; + print OUT "\n"; + foreach my $file (sort @failures) { + $file =~ /(.+).info.txt$/; + # Get the preprocessed file. + my $ppfile = $1; + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$file") or + die "Cannot open $Dir/failures/$file\n"; + my $srcfile = ; + chomp $srcfile; + my $problem = ; + chomp $problem; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " \n"; + } + print OUT "
ProblemSource FilePreprocessed FileSTDERR Output
$problem$srcfile$ppfile$ppfile.stderr.txt
\n"; + } + print OUT "

Please consider submitting preprocessed files as bug reports.

\n"; + } + + print OUT "\n"; + close(OUT); + CopyFiles($Dir); + + # Make sure $Dir and $BaseDir are world readable/executable. + system("chmod", "755", $Dir); + if (defined $BaseDir) { system("chmod", "755", $BaseDir); } + + my $Num = scalar(@Index); + Diag("$Num bugs found.\n"); + if ($Num > 0 && -r "$Dir/index.html") { + Diag("Run 'scan-view $Dir' to examine bug reports.\n"); + } + + DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); + + return $Num; +} + +##----------------------------------------------------------------------------## +# RunBuildCommand - Run the build command. +##----------------------------------------------------------------------------## + +sub AddIfNotPresent { + my $Args = shift; + my $Arg = shift; + my $found = 0; + + foreach my $k (@$Args) { + if ($k eq $Arg) { + $found = 1; + last; + } + } + + if ($found == 0) { + push @$Args, $Arg; + } +} + +sub RunBuildCommand { + + my $Args = shift; + my $IgnoreErrors = shift; + my $Cmd = $Args->[0]; + my $CCAnalyzer = shift; + + # Get only the part of the command after the last '/'. + if ($Cmd =~ /\/([^\/]+)$/) { + $Cmd = $1; + } + + if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?cc[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { + + if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { + $ENV{"CCC_CC"} = $1; + } + + shift @$Args; + unshift @$Args, $CCAnalyzer; + } + elsif ($IgnoreErrors) { + if ($Cmd eq "make" or $Cmd eq "gmake") { + AddIfNotPresent($Args,"-k"); + AddIfNotPresent($Args,"-i"); + } + elsif ($Cmd eq "xcodebuild") { + AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); + } + } + + if ($Cmd eq "xcodebuild") { + # Check if using iPhone SDK 3.0 (simulator). If so the compiler being + # used should be gcc-4.2. + if (!defined $ENV{"CCC_CC"}) { + for (my $i = 0 ; $i < scalar(@$Args); ++$i) { + if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { + if (@$Args[$i+1] =~ /^iphonesimulator3/) { + $ENV{"CCC_CC"} = "gcc-4.2"; + } + } + } + } + + # Disable distributed builds for xcodebuild. + AddIfNotPresent($Args,"-nodistribute"); + + # Disable PCH files until clang supports them. + AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); + + # When 'CC' is set, xcodebuild uses it to do all linking, even if we are + # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' + # when linking such files. + die if (!defined $CXX); + my $LDPLUSPLUS = `which $CXX`; + $LDPLUSPLUS =~ s/\015?\012//; # strip newlines + $ENV{'LDPLUSPLUS'} = $LDPLUSPLUS; + } + + return (system(@$Args) >> 8); +} + +##----------------------------------------------------------------------------## +# DisplayHelp - Utility function to display all help options. +##----------------------------------------------------------------------------## + +sub DisplayHelp { + +print < [build options] + +ENDTEXT + + if (defined $BuildName) { + print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; + } + +print </>/g; + return $tmp; +} + +##----------------------------------------------------------------------------## +# ShellEscape - backslash escape characters that are special to the shell +##----------------------------------------------------------------------------## + +sub ShellEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } + return $arg; +} + +##----------------------------------------------------------------------------## +# Process command-line arguments. +##----------------------------------------------------------------------------## + +my $AnalyzeHeaders = 0; +my $HtmlDir; # Parent directory to store HTML files. +my $IgnoreErrors = 0; # Ignore build errors. +my $ViewResults = 0; # View results when the build terminates. +my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found +my @AnalysesToRun; +my $StoreModel; +my $ConstraintsModel; +my $OutputFormat; + +if (!@ARGV) { + DisplayHelp(); + exit 1; +} + +while (@ARGV) { + + # Scan for options we recognize. + + my $arg = $ARGV[0]; + + if ($arg eq "-h" or $arg eq "--help") { + DisplayHelp(); + exit 0; + } + + if ($arg eq '-analyze-headers') { + shift @ARGV; + $AnalyzeHeaders = 1; + next; + } + + if (defined $AvailableAnalyses{$arg}) { + shift @ARGV; + push @AnalysesToRun, $arg; + next; + } + + if ($arg eq "-o") { + shift @ARGV; + + if (!@ARGV) { + DieDiag("'-o' option requires a target directory name.\n"); + } + + # Construct an absolute path. Uses the current working directory + # as a base if the original path was not absolute. + $HtmlDir = abs_path(shift @ARGV); + + next; + } + + if ($arg =~ /^--html-title(=(.+))?$/) { + shift @ARGV; + + if (!defined $2 || $2 eq '') { + if (!@ARGV) { + DieDiag("'--html-title' option requires a string.\n"); + } + + $HtmlTitle = shift @ARGV; + } else { + $HtmlTitle = $2; + } + + next; + } + + if ($arg eq "-k" or $arg eq "--keep-going") { + shift @ARGV; + $IgnoreErrors = 1; + next; + } + + if ($arg =~ /^--use-cc(=(.+))?$/) { + shift @ARGV; + my $cc; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-cc' option requires a compiler executable name.\n"); + } + $cc = shift @ARGV; + } + else { + $cc = $2; + } + + $ENV{"CCC_CC"} = $cc; + next; + } + + if ($arg =~ /^--use-c\+\+(=(.+))?$/) { + shift @ARGV; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-c++' option requires a compiler executable name.\n"); + } + $CXX = shift @ARGV; + } + else { + $CXX = $2; + } + next; + } + + if ($arg eq "-v") { + shift @ARGV; + $Verbose++; + next; + } + + if ($arg eq "-V" or $arg eq "--view") { + shift @ARGV; + $ViewResults = 1; + next; + } + + if ($arg eq "--status-bugs") { + shift @ARGV; + $ExitStatusFoundBugs = 1; + next; + } + + if ($arg eq "-store") { + shift @ARGV; + $StoreModel = shift @ARGV; + next; + } + + if ($arg eq "-constraints") { + shift @ARGV; + $ConstraintsModel = shift @ARGV; + next; + } + + if ($arg eq "-plist") { + shift @ARGV; + $OutputFormat = "plist"; + next; + } + + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); + + last; +} + +if (!@ARGV) { + Diag("No build command specified.\n\n"); + DisplayHelp(); + exit 1; +} + +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); +$HtmlTitle = "${CurrentDirSuffix} - scan-build results" + unless (defined($HtmlTitle)); + +# Determine the output directory for the HTML reports. +my $BaseDir = $HtmlDir; +$HtmlDir = GetHTMLRunDir($HtmlDir); + +# Set the appropriate environment variables. +SetHtmlEnv(\@ARGV, $HtmlDir); + +my $Cmd = Cwd::realpath("$RealBin/libexec/ccc-analyzer"); +if (!defined $Cmd || ! -x $Cmd) { + $Cmd = Cwd::realpath("$RealBin/ccc-analyzer"); + DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd); +} + +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + Diag("'clang-cc' executable not found in '$RealBin/libexec'.\n"); + Diag("Using 'clang-cc' from path.\n"); +} +if (!defined $ClangSB || ! -x $ClangSB) { + Diag("'clang' executable not found in '$RealBin/bin'.\n"); + Diag("Using 'clang' from path.\n"); +} + +if (defined $CXX) { + $ENV{'CXX'} = $CXX; +} +else { + $CXX = 'g++'; # This variable is used by other parts of scan-build + # that need to know a default C++ compiler to fall back to. +} + +$ENV{'CC'} = $Cmd; +$ENV{'CLANG_CC'} = $ClangCC; +$ENV{'CLANG'} = $Clang; + +if ($Verbose >= 2) { + $ENV{'CCC_ANALYZER_VERBOSE'} = 1; +} + +if ($Verbose >= 3) { + $ENV{'CCC_ANALYZER_LOG'} = 1; +} + +if (scalar(@AnalysesToRun) == 0) { + foreach my $key (keys %AnalysesDefaultEnabled) { + push @AnalysesToRun,$key; + } +} + +if ($AnalyzeHeaders) { + push @AnalysesToRun,"-analyzer-opt-analyze-headers"; +} + +$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun; + +if (defined $StoreModel) { + $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel; +} + +if (defined $ConstraintsModel) { + $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel; +} + +if (defined $OutputFormat) { + $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat; +} + + +# Run the build. +my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd); + +if (defined $OutputFormat and $OutputFormat eq "plist") { + Diag "Analysis run complete.\n"; + Diag "Analysis results (plist files) deposited in '$HtmlDir'\n"; +} +else { + # Postprocess the HTML directory. + my $NumBugs = Postprocess($HtmlDir, $BaseDir); + + if ($ViewResults and -r "$HtmlDir/index.html") { + Diag "Analysis run complete.\n"; + Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n"; + my $ScanView = Cwd::realpath("$RealBin/scan-view"); + if (! -x $ScanView) { $ScanView = "scan-view"; } + exec $ScanView, "$HtmlDir"; + } + + if ($ExitStatusFoundBugs) { + exit 1 if ($NumBugs > 0); + exit 0; + } +} + +exit $ExitStatus; + diff --git a/llvm/sorttable.js b/llvm/sorttable.js new file mode 100644 index 0000000..4352d3b --- /dev/null +++ b/llvm/sorttable.js @@ -0,0 +1,493 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i5' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0] 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("