# Do not enable the missing -dealloc check by default.
# '-analyzer-check-objc-missing-dealloc' => 1,
'-analyzer-check-objc-unused-ivars' => 1,
- '-analyzer-check-security-syntactic' => 1
+ '-analyzer-check-security-syntactic' => 1,
+ '-analyzer-check-idempotent-operations' => 1
);
##----------------------------------------------------------------------------##
system("mv", "$fname.tmp", $fname);
}
+##----------------------------------------------------------------------------##
+# AddStatLine - Decode and insert a statistics line into the database.
+##----------------------------------------------------------------------------##
+
+sub AddStatLine {
+ my $Line = shift;
+ my $Stats = shift;
+
+ print $Line . "\n";
+
+ my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable
+ \ CFGBlocks:\ (\d+)\ \|\ Aborted\ Block:\ (yes|no)\ \|\ Empty\ WorkList:
+ \ (yes|no)/x;
+
+ if ($Line !~ $Regex) {
+ return;
+ }
+
+ # Create a hash of the interesting fields
+ my $Row = {
+ Filename => $1,
+ Function => $2,
+ Total => $3,
+ Unreachable => $4,
+ Aborted => $5,
+ Empty => $6
+ };
+
+ # Add them to the stats array
+ push @$Stats, $Row;
+}
+
##----------------------------------------------------------------------------##
# ScanFile - Scan a report file for various identifying attributes.
##----------------------------------------------------------------------------##
my $Index = shift;
my $Dir = shift;
my $FName = shift;
+ my $Stats = shift;
# Compute a digest for the report file. Determine if we have already
# scanned a file that looks just like it.
# 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 $BugType = "";
+ my $BugFile = "";
+ my $BugCategory = "";
+ my $BugDescription = "";
+ my $BugPathLength = 1;
+ my $BugLine = 0;
while (<IN>) {
last if (/<!-- BUGMETAEND -->/);
elsif (/<!-- BUGCATEGORY (.*) -->$/) {
$BugCategory = $1;
}
+ elsif (/<!-- BUGDESC (.*) -->$/) {
+ $BugDescription = $1;
+ }
}
close(IN);
if (!defined $BugCategory) {
$BugCategory = "Other";
}
-
+
+ # Don't add internal statistics to the bug reports
+ if ($BugCategory =~ /statistics/i) {
+ AddStatLine($BugDescription, $Stats);
+ return;
+ }
+
push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine,
$BugPathLength ];
}
if (! -r $CSS);
}
+##----------------------------------------------------------------------------##
+# CalcStats - Calculates visitation statistics and returns the string.
+##----------------------------------------------------------------------------##
+
+sub CalcStats {
+ my $Stats = shift;
+
+ my $TotalBlocks = 0;
+ my $UnreachedBlocks = 0;
+ my $TotalFunctions = scalar(@$Stats);
+ my $BlockAborted = 0;
+ my $WorkListAborted = 0;
+ my $Aborted = 0;
+
+ # Calculate the unique files
+ my $FilesHash = {};
+
+ foreach my $Row (@$Stats) {
+ $FilesHash->{$Row->{Filename}} = 1;
+ $TotalBlocks += $Row->{Total};
+ $UnreachedBlocks += $Row->{Unreachable};
+ $BlockAborted++ if $Row->{Aborted} eq 'yes';
+ $WorkListAborted++ if $Row->{Empty} eq 'no';
+ $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no';
+ }
+
+ my $TotalFiles = scalar(keys(%$FilesHash));
+
+ # Calculations
+ my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100);
+ my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions
+ * 100);
+ my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted /
+ $TotalFunctions * 100);
+ my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks
+ * 100);
+
+ my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions"
+ . " in $TotalFiles files\n"
+ . "$Aborted functions aborted early ($PercentAborted%)\n"
+ . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n"
+ . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n"
+ . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n";
+
+ return $StatsString;
+}
+
##----------------------------------------------------------------------------##
# Postprocess - Postprocess the results of an analysis scan.
##----------------------------------------------------------------------------##
sub Postprocess {
- my $Dir = shift;
- my $BaseDir = shift;
+ my $Dir = shift;
+ my $BaseDir = shift;
+ my $AnalyzerStats = shift;
die "No directory specified." if (!defined $Dir);
}
# Scan each report file and build an index.
- my @Index;
- foreach my $file (@files) { ScanFile(\@Index, $Dir, $file); }
+ my @Index;
+ my @Stats;
+ foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); }
# Scan the failures directory and use the information in the .info files
# to update the common prefix directory.
system("chmod", "755", $Dir);
if (defined $BaseDir) { system("chmod", "755", $BaseDir); }
+ # Print statistics
+ print CalcStats(\@Stats) if $AnalyzerStats;
+
my $Num = scalar(@Index);
Diag("$Num bugs found.\n");
if ($Num > 0 && -r "$Dir/index.html") {
-no-failure-reports - Do not create a 'failures' subdirectory that includes
analyzer crash reports and preprocessed source files.
+ -stats - Generates visitation statistics for the project being analyzed.
+
+ -maxloop N - specifiy the number of times a block can be visited before giving
+ up. Default is 3. Increase for more comprehensive coverage at a
+ cost of speed.
+
AVAILABLE ANALYSES (multiple analyses may be specified):
ENDTEXT
my $StoreModel;
my $ConstraintsModel;
my $OutputFormat = "html";
+my $AnalyzerStats = 0;
+my $MaxLoop = 0;
if (!@ARGV) {
DisplayHelp();
$ENV{"CCC_REPORT_FAILURES"} = 0;
next;
}
+ if ($arg eq "-stats") {
+ shift @ARGV;
+ $AnalyzerStats = 1;
+ next;
+ }
+ if ($arg eq "-maxloop") {
+ shift @ARGV;
+ $MaxLoop = shift @ARGV;
+ next;
+ }
DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
push @AnalysesToRun,"-analyzer-opt-analyze-headers";
}
+if ($AnalyzerStats) {
+ push @AnalysesToRun, '-analyzer-stats';
+}
+
+if ($MaxLoop > 0) {
+ push @AnalysesToRun, '-analyzer-max-loop ' . $MaxLoop;
+}
+
$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun;
if (defined $StoreModel) {
}
elsif ($OutputFormat =~ /html/) {
# Postprocess the HTML directory.
- my $NumBugs = Postprocess($HtmlDir, $BaseDir);
+ my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats);
if ($ViewResults and -r "$HtmlDir/index.html") {
Diag "Analysis run complete.\n";